custom__ prefix. This page is the reference for every custom tool: the arguments it accepts and the fields it returns. Each tool returns a single JSON object as the text content of its result.
The custom toolset will grow over time as more jobs get their own purpose-built tool.
The image post flow
Publishing an image post takes three steps — two tool calls with an image upload in between:Start the upload
Call
custom__start-image-upload. It returns a mediaUuid, an uploadId and a short-lived
uploadUrl for one image.Upload the image
HTTP
PUT the raw image bytes (not base64) to uploadUrl, with no Authorization header. Keep the
ETag response header — it confirms the upload in the next step.custom__start-image-upload
Starts an image post (step 1 of 3) by reserving an upload slot for one image and returning everything the upload step needs. Takes no arguments.
Requires the write:media scope.
Result
UUID of the new media item. Pass it to
custom__create-image-post, and use it to reference the media
in your library afterwards.Opaque identifier of this upload. Pass it to
custom__create-image-post exactly as received — don’t
parse or modify it.Short-lived URL to
PUT the raw image bytes to. If it expires before the upload happens, call
custom__start-image-upload again for a fresh one — an unused slot is harmless.A reminder of the remaining steps, embedded in the result so an assistant can complete the flow without
consulting this page.
Example result
custom__create-image-post
Publishes a post carrying the uploaded image (step 3 of 3). It accepts the same post fields as create a new post — text, audience, pricing, scheduling, expiry, collections — plus the identifiers of the uploaded image. The post is only created once the image has finished processing and is ready to display, so a post never reaches the feed with broken media.
Requires the write:media, read:media and write:post scopes.
Arguments
The uploaded image to attach to the post.
Who can view the post:
subscribers or followers-and-subscribers.Text content of the post, up to 5,000 characters.
Price in cents for a pay-to-view post, minimum
300 ($3.00). Omit for a free post.Future date/time to publish the post, in ISO 8601 format with a timezone (for example
2026-07-01T18:00:00Z). When set, the post is scheduled instead of published immediately.Date/time when the post expires, in ISO 8601 format with a timezone.
Free teaser image shown to non-subscribers before they unlock a paid post. Upload it through its own
custom__start-image-upload call; the fields are the same as image.UUIDs of the collections to add the post to.
Result
The created post — the same shape as the create a new post response.Unique identifier of the created post.
Date/time when the post was created (ISO 8601 format).
Text content of the post.
Price in cents for paid posts.
UUID of the free preview media, when a
previewImage was supplied.Audience the post is visible to.
Future date/time when the post will be published, for scheduled posts.
Date/time when the post was published.
null for scheduled posts that have not gone live yet.Date/time when the post expires.
Example result
If a call fails
- A failed call never publishes a broken post: if the image could not be processed, no post is created and the error names the media that failed.
- An image that was already uploaded is not lost — the error lists the media UUIDs that remain in your media library, ready to attach to a post.
- The call is safe to retry with the same arguments; a retry picks up where the failed call left off rather than uploading or posting twice.
- If the error reports a rejected upload, check that the bytes were
PUTto theuploadUrland thatetagis theETagheader from thatPUT. An expireduploadUrlmeans starting over withcustom__start-image-upload.