The Content area publishes 42 REST endpoints. Paths are relative to https://api.lepsto.com; see Authentication for the required header.
GET/content/channels
List the channels this product can publish to, with each one's limits and per-post cost. A LinkedIn profile, X or YouTube account connected on the Integrations page appears here by itself as an auto channel named after the account (origin integration). The seeded channels, one per network, use reminder delivery: the system prepares the post and notifies a person to publish it. Every channel is returned, on or off: enabled says whether it is in use (integration channels always are; a new product starts with every seeded channel off), and only a channel with enabled true can be scheduled. Label a network with networkName, never with a channel's displayName.
MCP tool: content_channels_list
Parameters
| Name | In | Type | Required | Description |
|---|
network | query | stringone of: linkedin, x, youtube, instagram, reddit, facebook, tiktok, threads, pinterest, bluesky, telegram
| — | Return only channels on this network. |
limit | query | integer
| — | Maximum number of channels to return (1-100, default 50). |
offset | query | integer
| — | How many channels to skip, for paging. |
Response
Returns object.
| Field | Type | Always | Description |
|---|
total | integer
| • | Total number of matching channels, ignoring paging. |
channels | object[]
| • | The page of channels, ordered by network. |
GET/content/channels/:id
Read one channel: its delivery mode and its capability descriptor — the character limit, media rules and cost to check before composing a variant for it.
MCP tool: content_channels_get
Parameters
| Name | In | Type | Required | Description |
|---|
id | path | string (uuid)
| • | The channel id returned by content_channels_list. |
Response
Returns object.
| Field | Type | Always | Description |
|---|
id | string
| • | Channel id — pass this to content_publications_schedule. |
origin | stringone of: integration, manual
| — | integration: the channel appeared by itself from an account connected on the Integrations page. manual: a seeded channel, or one bound by hand. |
enabled | boolean
| • | Whether this channel is in use. An integration channel is always true. A manual channel is true once someone turns it on with content_channels_update (a new product starts with every manual channel off). Nothing can be scheduled on a channel that is off: content_publications_schedule refuses it with channel_disabled. |
network | string
| • | Social network: linkedin, x, youtube, instagram, reddit, facebook, tiktok, threads, pinterest, bluesky or telegram. |
pausedAt | string | null
| • | ISO-8601 time the connector stopped working, or null when healthy. A paused channel keeps its scheduled publications — they are frozen, not cancelled, and dispatch once it resumes. |
createdAt | string
| • | ISO-8601 creation time. |
updatedAt | string
| • | ISO-8601 time of the last change. |
connectorId | string | null
| • | Platform connector this channel publishes through, or null for a reminder channel that needs none. An id — this toolkit never holds social credentials; a token is fetched per call and discarded. |
displayName | string
| • | Human label for the channel. |
networkName | string
| • | The network as a person names it — LinkedIn, X, YouTube. Use it to label a network; displayName is the account or channel name. |
pauseReason | string | null
| • | Why it paused: revoked (access was taken away), expired (the credential ran out), detached (the integration was detached from this product), removed (the integration was deleted), or the provider reason code from a failed publish. Null when healthy. |
subjectType | stringone of: member, organization
| • | Who publishes: member (a personal account) or organization (a company page). Phase 1 channels are all member accounts. |
capabilities | object
| • | What the channel accepts and what it costs: maxTextLength, maxMedia, supportsVideo, supportsLink, costPerPostUsd, costPerLinkPostUsd, dailyPostCeiling, ceilingIsRuntimeData, acceptsCover (whether the cover of an output is set on the network automatically — the YouTube thumbnail) and coverMaxBytes (the largest cover it takes, or null). Read maxTextLength before writing a per-channel text variant. |
deliveryMode | stringone of: auto, reminder
| • | How the post reaches the network: auto (this API publishes it) or reminder (the system prepares it and notifies a person, who publishes by hand). Every seeded channel is reminder. |
PATCH/content/channels/:id
Rename a channel, change its delivery mode, turn a manual channel on or off (enabled), or correct its capability descriptor. Turning off is refused with 409 channel_not_toggleable for an integration channel, which is always on, and with 409 channel_has_scheduled while the channel still has publications waiting to go out — cancel them first. Channels are not created with a tool: every product starts with one per network, and each account connected on the Integrations page adds its own.
MCP tool: content_channels_update
Parameters
| Name | In | Type | Required | Description |
|---|
id | path | string (uuid)
| • | The channel id returned by content_channels_list. |
displayName | body | string
| — | New human label for the channel. Omit to keep the current one. |
deliveryMode | body | stringone of: auto, reminder
| — | Change how the post reaches the network. Only reminder can be scheduled in this release — no publishing adapter exists yet. |
enabled | body | boolean
| — | Turn a manual channel on (true) or off (false). Integration channels are always on: false is refused with channel_not_toggleable. A channel with publications still scheduled cannot be turned off: the call is refused with channel_has_scheduled until they are cancelled or have gone out. Omit to leave it as it is. |
capabilities | body | object
| — | Override individual capability fields — a known ceiling beats the hand-filled constant. Merges over the existing descriptor; fields you omit keep their current values. |
Response
Returns object.
| Field | Type | Always | Description |
|---|
id | string
| • | Channel id — pass this to content_publications_schedule. |
origin | stringone of: integration, manual
| — | integration: the channel appeared by itself from an account connected on the Integrations page. manual: a seeded channel, or one bound by hand. |
enabled | boolean
| • | Whether this channel is in use. An integration channel is always true. A manual channel is true once someone turns it on with content_channels_update (a new product starts with every manual channel off). Nothing can be scheduled on a channel that is off: content_publications_schedule refuses it with channel_disabled. |
network | string
| • | Social network: linkedin, x, youtube, instagram, reddit, facebook, tiktok, threads, pinterest, bluesky or telegram. |
pausedAt | string | null
| • | ISO-8601 time the connector stopped working, or null when healthy. A paused channel keeps its scheduled publications — they are frozen, not cancelled, and dispatch once it resumes. |
createdAt | string
| • | ISO-8601 creation time. |
updatedAt | string
| • | ISO-8601 time of the last change. |
connectorId | string | null
| • | Platform connector this channel publishes through, or null for a reminder channel that needs none. An id — this toolkit never holds social credentials; a token is fetched per call and discarded. |
displayName | string
| • | Human label for the channel. |
networkName | string
| • | The network as a person names it — LinkedIn, X, YouTube. Use it to label a network; displayName is the account or channel name. |
pauseReason | string | null
| • | Why it paused: revoked (access was taken away), expired (the credential ran out), detached (the integration was detached from this product), removed (the integration was deleted), or the provider reason code from a failed publish. Null when healthy. |
subjectType | stringone of: member, organization
| • | Who publishes: member (a personal account) or organization (a company page). Phase 1 channels are all member accounts. |
capabilities | object
| • | What the channel accepts and what it costs: maxTextLength, maxMedia, supportsVideo, supportsLink, costPerPostUsd, costPerLinkPostUsd, dailyPostCeiling, ceilingIsRuntimeData, acceptsCover (whether the cover of an output is set on the network automatically — the YouTube thumbnail) and coverMaxBytes (the largest cover it takes, or null). Read maxTextLength before writing a per-channel text variant. |
deliveryMode | stringone of: auto, reminder
| • | How the post reaches the network: auto (this API publishes it) or reminder (the system prepares it and notifies a person, who publishes by hand). Every seeded channel is reminder. |
POST/content/channels/:id/bind
Bind a channel to a platform connector by hand. For manual cases only: an account connected on the Integrations page already has its own channel. The connector must be attached to this product, match the channel's network and have no other channel in this product (connector_not_found, connector_network_mismatch, connector_already_bound otherwise). Clears an existing pause and turns the channel on. Pass deliveryMode "auto" to also switch the channel to automatic publishing; binding alone never does.
MCP tool: content_channels_bind
Parameters
| Name | In | Type | Required | Description |
|---|
id | path | string (uuid)
| • | The channel id returned by content_channels_list. |
connectorId | body | string (uuid)
| • | The platform connector to publish through — the id of a connector attached to this product. It must be attached and of this channel's network, and not already carried by another channel of this product, or the call is refused with connector_not_found, connector_network_mismatch or connector_already_bound. Networks with no publishing connector (instagram, facebook, tiktok, threads, pinterest, bluesky, telegram, LinkedIn company pages) are refused with no_api_provider. |
deliveryMode | body | stringone of: auto, reminder
| — | Pass auto to make this API publish through the connector. Omit to keep the current mode: binding alone never switches a channel to automatic publishing. |
Response
Returns object.
| Field | Type | Always | Description |
|---|
id | string
| • | Channel id — pass this to content_publications_schedule. |
origin | stringone of: integration, manual
| — | integration: the channel appeared by itself from an account connected on the Integrations page. manual: a seeded channel, or one bound by hand. |
enabled | boolean
| • | Whether this channel is in use. An integration channel is always true. A manual channel is true once someone turns it on with content_channels_update (a new product starts with every manual channel off). Nothing can be scheduled on a channel that is off: content_publications_schedule refuses it with channel_disabled. |
network | string
| • | Social network: linkedin, x, youtube, instagram, reddit, facebook, tiktok, threads, pinterest, bluesky or telegram. |
pausedAt | string | null
| • | ISO-8601 time the connector stopped working, or null when healthy. A paused channel keeps its scheduled publications — they are frozen, not cancelled, and dispatch once it resumes. |
createdAt | string
| • | ISO-8601 creation time. |
updatedAt | string
| • | ISO-8601 time of the last change. |
connectorId | string | null
| • | Platform connector this channel publishes through, or null for a reminder channel that needs none. An id — this toolkit never holds social credentials; a token is fetched per call and discarded. |
displayName | string
| • | Human label for the channel. |
networkName | string
| • | The network as a person names it — LinkedIn, X, YouTube. Use it to label a network; displayName is the account or channel name. |
pauseReason | string | null
| • | Why it paused: revoked (access was taken away), expired (the credential ran out), detached (the integration was detached from this product), removed (the integration was deleted), or the provider reason code from a failed publish. Null when healthy. |
subjectType | stringone of: member, organization
| • | Who publishes: member (a personal account) or organization (a company page). Phase 1 channels are all member accounts. |
capabilities | object
| • | What the channel accepts and what it costs: maxTextLength, maxMedia, supportsVideo, supportsLink, costPerPostUsd, costPerLinkPostUsd, dailyPostCeiling, ceilingIsRuntimeData, acceptsCover (whether the cover of an output is set on the network automatically — the YouTube thumbnail) and coverMaxBytes (the largest cover it takes, or null). Read maxTextLength before writing a per-channel text variant. |
deliveryMode | stringone of: auto, reminder
| • | How the post reaches the network: auto (this API publishes it) or reminder (the system prepares it and notifies a person, who publishes by hand). Every seeded channel is reminder. |
POST/content/channels/:id/resume
Resume a paused channel once its connection works again. Its frozen publications dispatch on the next scheduler tick — late, not cancelled. A channel paused as detached or removed resumes only after its integration is attached to this product again on the Integrations page; otherwise the call is refused with connector_detached. Reconnecting never resumes a channel by itself.
MCP tool: content_channels_resume
Parameters
| Name | In | Type | Required | Description |
|---|
id | path | string (uuid)
| • | The paused channel to resume, once its connector has been reconnected. |
Response
Returns object.
| Field | Type | Always | Description |
|---|
id | string
| • | Channel id — pass this to content_publications_schedule. |
origin | stringone of: integration, manual
| — | integration: the channel appeared by itself from an account connected on the Integrations page. manual: a seeded channel, or one bound by hand. |
enabled | boolean
| • | Whether this channel is in use. An integration channel is always true. A manual channel is true once someone turns it on with content_channels_update (a new product starts with every manual channel off). Nothing can be scheduled on a channel that is off: content_publications_schedule refuses it with channel_disabled. |
network | string
| • | Social network: linkedin, x, youtube, instagram, reddit, facebook, tiktok, threads, pinterest, bluesky or telegram. |
pausedAt | string | null
| • | ISO-8601 time the connector stopped working, or null when healthy. A paused channel keeps its scheduled publications — they are frozen, not cancelled, and dispatch once it resumes. |
createdAt | string
| • | ISO-8601 creation time. |
updatedAt | string
| • | ISO-8601 time of the last change. |
connectorId | string | null
| • | Platform connector this channel publishes through, or null for a reminder channel that needs none. An id — this toolkit never holds social credentials; a token is fetched per call and discarded. |
displayName | string
| • | Human label for the channel. |
networkName | string
| • | The network as a person names it — LinkedIn, X, YouTube. Use it to label a network; displayName is the account or channel name. |
pauseReason | string | null
| • | Why it paused: revoked (access was taken away), expired (the credential ran out), detached (the integration was detached from this product), removed (the integration was deleted), or the provider reason code from a failed publish. Null when healthy. |
subjectType | stringone of: member, organization
| • | Who publishes: member (a personal account) or organization (a company page). Phase 1 channels are all member accounts. |
capabilities | object
| • | What the channel accepts and what it costs: maxTextLength, maxMedia, supportsVideo, supportsLink, costPerPostUsd, costPerLinkPostUsd, dailyPostCeiling, ceilingIsRuntimeData, acceptsCover (whether the cover of an output is set on the network automatically — the YouTube thumbnail) and coverMaxBytes (the largest cover it takes, or null). Read maxTextLength before writing a per-channel text variant. |
deliveryMode | stringone of: auto, reminder
| • | How the post reaches the network: auto (this API publishes it) or reminder (the system prepares it and notifies a person, who publishes by hand). Every seeded channel is reminder. |
POST/content/channels/:id/unbind
Detach a channel from its platform connector, and clear any pause. Delivery mode is left as it is: an auto channel left without a connection fails its publications visibly rather than being silently downgraded to reminders. The account is not brought back as a new channel by itself: bind it again to undo.
MCP tool: content_channels_unbind
Parameters
| Name | In | Type | Required | Description |
|---|
id | path | string (uuid)
| • | The channel id returned by content_channels_list. |
Response
Returns object.
| Field | Type | Always | Description |
|---|
id | string
| • | Channel id — pass this to content_publications_schedule. |
origin | stringone of: integration, manual
| — | integration: the channel appeared by itself from an account connected on the Integrations page. manual: a seeded channel, or one bound by hand. |
enabled | boolean
| • | Whether this channel is in use. An integration channel is always true. A manual channel is true once someone turns it on with content_channels_update (a new product starts with every manual channel off). Nothing can be scheduled on a channel that is off: content_publications_schedule refuses it with channel_disabled. |
network | string
| • | Social network: linkedin, x, youtube, instagram, reddit, facebook, tiktok, threads, pinterest, bluesky or telegram. |
pausedAt | string | null
| • | ISO-8601 time the connector stopped working, or null when healthy. A paused channel keeps its scheduled publications — they are frozen, not cancelled, and dispatch once it resumes. |
createdAt | string
| • | ISO-8601 creation time. |
updatedAt | string
| • | ISO-8601 time of the last change. |
connectorId | string | null
| • | Platform connector this channel publishes through, or null for a reminder channel that needs none. An id — this toolkit never holds social credentials; a token is fetched per call and discarded. |
displayName | string
| • | Human label for the channel. |
networkName | string
| • | The network as a person names it — LinkedIn, X, YouTube. Use it to label a network; displayName is the account or channel name. |
pauseReason | string | null
| • | Why it paused: revoked (access was taken away), expired (the credential ran out), detached (the integration was detached from this product), removed (the integration was deleted), or the provider reason code from a failed publish. Null when healthy. |
subjectType | stringone of: member, organization
| • | Who publishes: member (a personal account) or organization (a company page). Phase 1 channels are all member accounts. |
capabilities | object
| • | What the channel accepts and what it costs: maxTextLength, maxMedia, supportsVideo, supportsLink, costPerPostUsd, costPerLinkPostUsd, dailyPostCeiling, ceilingIsRuntimeData, acceptsCover (whether the cover of an output is set on the network automatically — the YouTube thumbnail) and coverMaxBytes (the largest cover it takes, or null). Read maxTextLength before writing a per-channel text variant. |
deliveryMode | stringone of: auto, reminder
| • | How the post reaches the network: auto (this API publishes it) or reminder (the system prepares it and notifies a person, who publishes by hand). Every seeded channel is reminder. |
GET/content/folders
List this product’s folder tree as a flat array with parentId and sibling position. counts are direct posts; totalCounts include every descendant, grouped by post status.
MCP tool: content_folders_list
Parameters
No parameters.
Response
Returns object.
| Field | Type | Always | Description |
|---|
folders | object[]
| • | Flat folder tree; parentId and position describe the hierarchy and order. |
POST/content/folders
Create a folder at the root or inside another folder, placing it last among siblings. The parent must belong to this product; use content_folders_list to find its id.
MCP tool: content_folders_create
Parameters
| Name | In | Type | Required | Description |
|---|
name | body | string
| • | The folder name, from 1 to 200 characters. |
parentId | body | string (uuid) | null
| — | Parent folder id, or null for a root folder. New folders go last among siblings. |
Response
Returns object.
| Field | Type | Always | Description |
|---|
id | string (uuid)
| • | Folder id. |
name | string
| • | Folder name. |
parentId | string (uuid) | null
| • | Parent folder id, or null at the root. |
position | integer
| • | Order among siblings with the same parent. |
createdAt | string (date-time)
| • | ISO-8601 creation time. |
updatedAt | string (date-time)
| • | ISO-8601 time of the last change. |
DELETE/content/folders/:folderId
Delete an empty folder. A folder with posts or subfolders is refused as folder_not_empty; move or delete its contents first.
MCP tool: content_folders_delete
Parameters
| Name | In | Type | Required | Description |
|---|
folderId | path | string (uuid)
| • | A folder id from content_folders_list or content_folders_create. |
Response
Returns object.
| Field | Type | Always | Description |
|---|
deleted | booleanone of: true
| • | True when this empty folder was deleted. |
folderId | string (uuid)
| • | A folder id from content_folders_list or content_folders_create. |
PATCH/content/folders/:folderId
Rename, re-parent or reorder a folder. beforeFolderId must be a sibling under the target parent. A move into itself or a descendant is refused as folder_cycle; use content_folders_list to inspect the tree first.
MCP tool: content_folders_update
Parameters
| Name | In | Type | Required | Description |
|---|
folderId | path | string (uuid)
| • | A folder id from content_folders_list or content_folders_create. |
name | body | string
| — | New folder name. Omit to keep it. |
parentId | body | string (uuid) | null
| — | New parent folder id, or null to move to the root. Omit to keep the current parent. |
beforeFolderId | body | string (uuid) | null
| — | Insert before this sibling in the target parent. Omit or set null to put the folder last. |
Response
Returns object.
| Field | Type | Always | Description |
|---|
id | string (uuid)
| • | Folder id. |
name | string
| • | Folder name. |
parentId | string (uuid) | null
| • | Parent folder id, or null at the root. |
position | integer
| • | Order among siblings with the same parent. |
createdAt | string (date-time)
| • | ISO-8601 creation time. |
updatedAt | string (date-time)
| • | ISO-8601 time of the last change. |
GET/content/media
List media assets, newest first. Supply status to filter by storage status; omit it to include all statuses.
MCP tool: content_media_list
Parameters
| Name | In | Type | Required | Description |
|---|
status | query | stringone of: pending, ready, failed
| — | Return only assets in this storage status. |
limit | query | integer
| — | Maximum number of assets to return (1-100, default 50). |
offset | query | integer
| — | How many assets to skip, for paging. |
Response
Returns object.
| Field | Type | Always | Description |
|---|
total | integer
| • | Total number of matching assets, ignoring paging. |
assets | object[]
| • | The page of assets, newest first. |
POST/content/media
Ingest media from a public http(s) URL: the toolkit fetches it and stores it in the product bucket. Images and video up to 512 MB. A fetch that fails returns the asset with status "failed" and a reason rather than an error.
MCP tool: content_media_upload
Parameters
| Name | In | Type | Required | Description |
|---|
sourceUrl | body | string (uri)
| • | Public http(s) URL the toolkit fetches server-side. Images and video only, up to 512 MB. |
filename | body | string
| — | Name to store the file under. Defaults to the last path segment of the URL. |
Response
Returns object.
| Field | Type | Always | Description |
|---|
id | string
| • | Media asset id. Use it to attach the asset to a post. |
key | string
| • | Object key in the product bucket. Private — hand it to no one, it is not a URL. |
mime | string | null
| • | Stored media type, or null while still pending. |
error | string | null
| • | Why the asset is failed — a readable reason, or null when nothing went wrong. |
source | stringone of: url, upload
| • | How the bytes arrived: url (fetched by the toolkit) or upload (PUT by the app). |
status | stringone of: pending, ready, failed
| • | Storage status: pending (the bytes are not in the bucket yet) -> ready (stored, attachable to a post) or failed (ingestion gave up; see error). |
viewUrl | string (uri) | null
| • | One-hour signed GET URL for a ready asset, or null while pending or failed. Treat it as a bearer link. |
filename | string | null
| • | Original filename, kept for display only. |
createdAt | string
| • | ISO-8601 creation time. |
sizeBytes | integer | null
| • | Stored size in bytes, or null while pending. |
sourceUrl | string | null
| • | The URL the bytes were fetched from, if any. |
updatedAt | string
| • | ISO-8601 time of the last change. |
viewUrlExpiresAt | string (date-time) | null
| • | ISO-8601 expiry of the signed view URL, or null when no view URL is available. |
GET/content/media/:id
Read one media asset: its storage status, type and size.
MCP tool: content_media_get
Parameters
| Name | In | Type | Required | Description |
|---|
id | path | string (uuid)
| • | The media asset id returned when it was created. |
Response
Returns object.
| Field | Type | Always | Description |
|---|
id | string
| • | Media asset id. Use it to attach the asset to a post. |
key | string
| • | Object key in the product bucket. Private — hand it to no one, it is not a URL. |
mime | string | null
| • | Stored media type, or null while still pending. |
error | string | null
| • | Why the asset is failed — a readable reason, or null when nothing went wrong. |
source | stringone of: url, upload
| • | How the bytes arrived: url (fetched by the toolkit) or upload (PUT by the app). |
status | stringone of: pending, ready, failed
| • | Storage status: pending (the bytes are not in the bucket yet) -> ready (stored, attachable to a post) or failed (ingestion gave up; see error). |
viewUrl | string (uri) | null
| • | One-hour signed GET URL for a ready asset, or null while pending or failed. Treat it as a bearer link. |
filename | string | null
| • | Original filename, kept for display only. |
createdAt | string
| • | ISO-8601 creation time. |
sizeBytes | integer | null
| • | Stored size in bytes, or null while pending. |
sourceUrl | string | null
| • | The URL the bytes were fetched from, if any. |
updatedAt | string
| • | ISO-8601 time of the last change. |
viewUrlExpiresAt | string (date-time) | null
| • | ISO-8601 expiry of the signed view URL, or null when no view URL is available. |
POST/content/media/:id/finalize
Confirm a signed-URL upload. The bucket is asked what was actually stored, and the asset becomes ready. Fails while no object has been uploaded yet.
MCP tool: content_media_finalize
Parameters
| Name | In | Type | Required | Description |
|---|
id | path | string (uuid)
| • | The media asset id returned when it was created. |
Response
Returns object.
| Field | Type | Always | Description |
|---|
id | string
| • | Media asset id. Use it to attach the asset to a post. |
key | string
| • | Object key in the product bucket. Private — hand it to no one, it is not a URL. |
mime | string | null
| • | Stored media type, or null while still pending. |
error | string | null
| • | Why the asset is failed — a readable reason, or null when nothing went wrong. |
source | stringone of: url, upload
| • | How the bytes arrived: url (fetched by the toolkit) or upload (PUT by the app). |
status | stringone of: pending, ready, failed
| • | Storage status: pending (the bytes are not in the bucket yet) -> ready (stored, attachable to a post) or failed (ingestion gave up; see error). |
viewUrl | string (uri) | null
| • | One-hour signed GET URL for a ready asset, or null while pending or failed. Treat it as a bearer link. |
filename | string | null
| • | Original filename, kept for display only. |
createdAt | string
| • | ISO-8601 creation time. |
sizeBytes | integer | null
| • | Stored size in bytes, or null while pending. |
sourceUrl | string | null
| • | The URL the bytes were fetched from, if any. |
updatedAt | string
| • | ISO-8601 time of the last change. |
viewUrlExpiresAt | string (date-time) | null
| • | ISO-8601 expiry of the signed view URL, or null when no view URL is available. |
POST/content/media/upload-url
Get a time-limited signed URL to PUT a file straight into the product bucket. Creates a pending asset; call content_media_finalize once the upload finishes.
MCP tool: content_media_create_upload_url
Parameters
| Name | In | Type | Required | Description |
|---|
contentType | body | string
| • | The media type the app will PUT, e.g. video/mp4. Must be image/* or video/*. |
filename | body | string
| — | Name to store the file under, for display purposes. |
Response
Returns object.
| Field | Type | Always | Description |
|---|
asset | object
| • | The pending asset row. It turns ready on finalize. |
expiresAt | string
| • | ISO-8601 time after which the upload URL stops working. |
uploadUrl | string
| • | Time-limited signed PUT URL. Upload the bytes to it, then call the finalize tool. |
contentType | string
| • | The Content-Type header the PUT must send — the signature covers it. |
GET/content/metrics/channels/:id
Daily snapshots of a channel itself — follower count and channel-level reach over time. Sampled once a day while the channel is publishing; a channel with nothing published in the last 30 days stops being sampled, and one whose network reports no analytics is never sampled at all.
MCP tool: content_metrics_channel_series
Parameters
| Name | In | Type | Required | Description |
|---|
id | path | string (uuid)
| • | The channel id, from content_channels_list. |
from | query | string (date-time)
| — | ISO-8601 lower bound on capture time, inclusive. Omit for "from the beginning". |
to | query | string (date-time)
| — | ISO-8601 upper bound on capture time, inclusive. Omit for "up to now". |
limit | query | integer
| — | Maximum number of snapshots to return, oldest first. |
Response
Returns object.
| Field | Type | Always | Description |
|---|
points | object[]
| • | Daily snapshots, oldest first. |
network | stringone of: linkedin, x, youtube, instagram, reddit, facebook, tiktok, threads, pinterest, bluesky, telegram
| • | The network the channel publishes to. |
analytics | stringone of: full, basic, none
| • | What this network reports for this account. "none" means no snapshot will ever be taken — a personal Instagram profile has no API at all; "basic" means fewer numbers than a business account gets, so an absent metric is the network's silence, not a zero. |
channelId | string (uuid)
| • | The channel these snapshots belong to. |
GET/content/metrics/posts/:id
Metric snapshots of one published post on one channel, over time — how reach and engagement grew, not a single current number. Sampled hourly for the first day, then daily, then weekly, then never: a post stops growing and the polling stops with it. Empty until the first snapshot, and always empty where analytics is "none".
MCP tool: content_metrics_post_series
Parameters
| Name | In | Type | Required | Description |
|---|
id | path | string (uuid)
| • | The publication id, from content_publications_schedule or content_publications_list. |
from | query | string (date-time)
| — | ISO-8601 lower bound on capture time, inclusive. Omit for "from the beginning". |
to | query | string (date-time)
| — | ISO-8601 upper bound on capture time, inclusive. Omit for "up to now". |
limit | query | integer
| — | Maximum number of snapshots to return, oldest first. |
Response
Returns object.
| Field | Type | Always | Description |
|---|
latest | object | null
| • | The newest snapshot as flat, named numbers — what to read when you want "how is this post doing" rather than the whole curve. Null when nothing has been captured yet. |
points | object[]
| • | Snapshots oldest first. Readings over time, not a current value: reach keeps growing for weeks. Empty means nothing has been captured yet. |
network | stringone of: linkedin, x, youtube, instagram, reddit, facebook, tiktok, threads, pinterest, bluesky, telegram, unknown
| • | The network the channel publishes to. "unknown" means the channel behind this publication can no longer be read — the publication outlives the channel binding. |
analytics | stringone of: full, basic, none
| • | What this network reports for this account. "none" means no snapshot will ever be taken — a personal Instagram profile has no API at all; "basic" means fewer numbers than a business account gets, so an absent metric is the network's silence, not a zero. |
channelId | string (uuid)
| • | The channel it was published to. |
publicationId | string (uuid)
| • | The publication these snapshots belong to. |
GET/content/posts
List posts, including ideas, each with its outputs and their states. Filter by status (partially_published for posts with some outputs out and others not) or folderId (null for unfiled posts). With a folder filter, posts appear in plan order; otherwise newest first.
MCP tool: content_posts_list
Parameters
| Name | In | Type | Required | Description |
|---|
status | query | stringone of: idea, draft, pending_approval, approved, scheduled, partially_published, published, failed
| — | Return only posts in this status. |
folderId | query | string (uuid) | null
| — | Filter to one folder, or null for posts in no folder. Omit for posts in every folder. |
limit | query | integer
| — | Maximum number of posts to return (1-100, default 50). |
offset | query | integer
| — | How many posts to skip, for paging. |
Response
Returns object.
| Field | Type | Always | Description |
|---|
posts | object[]
| • | The page of posts, ordered by position when filtered to a folder and newest first otherwise. |
total | integer
| • | Total number of matching posts, ignoring paging. |
POST/content/posts
Create a content plan item. Write the text into each output (targets[].text): networks differ, and the base text is only optional source text for outputs that have none. A title with no text anywhere starts as an idea; text in any output or as base text starts as a draft. Optionally place it in a folder and add private notes, media and outputs (targets): a network and a variant each, several per network allowed, each with optional own media. Use content_posts_to_draft for an idea, then content_posts_submit when every output has publishable text.
MCP tool: content_posts_create
Parameters
| Name | In | Type | Required | Description |
|---|
title | body | string | null
| — | Optional plan title, up to 300 characters. Required when text is empty to create an idea. |
notes | body | string | null
| — | Private plan notes, up to 20,000 characters. They are not published. |
folderId | body | string (uuid) | null
| — | Folder in this product to place the post in, or null for no folder. A new post goes last in that folder. |
text | body | string
| — | Optional base markdown text: source text an output without its own text publishes. Networks differ, so prefer writing each output its own targets[].text and leave this empty. A title with no text anywhere creates an idea; text here or in any output creates a draft. |
media | body | string[]
| — | Media references attached to the post, in display order. Opaque strings — the toolkit does not upload or resolve them. |
targets | body | object[]
| — | The outputs of this post: for each, a network, a post type, an optional text override, the network-specific fields and optional own media. Several outputs may share a network. A post needs at least one output before it can be submitted, and it can only be scheduled to channels of networks it has an output for. Replaces the whole list; send each existing output with its id. |
Response
Returns object.
| Field | Type | Always | Description |
|---|
id | string (uuid)
| • | Post id. |
text | string
| • | The current text of the post, in markdown. |
media | string[]
| • | Media references attached to the current version. |
notes | string | null
| • | Private plan notes, or null when unset. |
title | string | null
| • | Plan title, or null when unset. |
author | string
| • | Identity id of whoever composed the post. |
status | stringone of: idea, draft, pending_approval, approved, scheduled, partially_published, published, failed
| • | Post lifecycle status: idea (may have no text) -> draft (being written) -> pending_approval (submitted) -> approved (cleared to publish). Past approval the status follows EVERY output of the approved version (see outputs): scheduled while an output is queued or in flight and none is out yet; partially_published once some outputs are out and others are still queued, failed or never scheduled; published once every output is out; failed when an output gave up and none is out or queued; back to approved when nothing is live any more (all cancelled). A post in any of these five statuses can still have an output scheduled, including a failed one again. |
outputs | object[]
| • | One entry per output of this post, in targets order, with the state its publications add up to and the publication that decides it. Before approval every output is `none`. The post's status past approval is derived from exactly these states. |
targets | object[]
| • | The outputs of this post — a network and a variant each; several may share a network. |
folderId | string (uuid) | null
| • | Folder containing this post, or null. |
position | integer | null
| • | Order within its folder, or null for an unfiled post. |
createdAt | string (date-time)
| • | ISO-8601 creation time. |
updatedAt | string (date-time)
| • | ISO-8601 time of the last change. |
currentVersion | integer
| • | Version number of the current text. |
approvedVersion | integer | null
| • | The version an approval binds to, or null. Cleared on every edit: approving one text never covers another. |
GET/content/posts/:id
Read one post: its current text, status, version numbers and outputs — one entry per output with its state (none, open, uncertain, failed, published, skipped) and the publication that decides it. The way to re-read a post mid-run — after a wait, after an approval, after a publication settled.
MCP tool: content_posts_get
Parameters
| Name | In | Type | Required | Description |
|---|
id | path | string (uuid)
| • | The post id returned by content_posts_create. |
Response
Returns object.
| Field | Type | Always | Description |
|---|
id | string (uuid)
| • | Post id. |
text | string
| • | The current text of the post, in markdown. |
media | string[]
| • | Media references attached to the current version. |
notes | string | null
| • | Private plan notes, or null when unset. |
title | string | null
| • | Plan title, or null when unset. |
author | string
| • | Identity id of whoever composed the post. |
status | stringone of: idea, draft, pending_approval, approved, scheduled, partially_published, published, failed
| • | Post lifecycle status: idea (may have no text) -> draft (being written) -> pending_approval (submitted) -> approved (cleared to publish). Past approval the status follows EVERY output of the approved version (see outputs): scheduled while an output is queued or in flight and none is out yet; partially_published once some outputs are out and others are still queued, failed or never scheduled; published once every output is out; failed when an output gave up and none is out or queued; back to approved when nothing is live any more (all cancelled). A post in any of these five statuses can still have an output scheduled, including a failed one again. |
outputs | object[]
| • | One entry per output of this post, in targets order, with the state its publications add up to and the publication that decides it. Before approval every output is `none`. The post's status past approval is derived from exactly these states. |
targets | object[]
| • | The outputs of this post — a network and a variant each; several may share a network. |
folderId | string (uuid) | null
| • | Folder containing this post, or null. |
position | integer | null
| • | Order within its folder, or null for an unfiled post. |
createdAt | string (date-time)
| • | ISO-8601 creation time. |
updatedAt | string (date-time)
| • | ISO-8601 time of the last change. |
currentVersion | integer
| • | Version number of the current text. |
approvedVersion | integer | null
| • | The version an approval binds to, or null. Cleared on every edit: approving one text never covers another. |
PATCH/content/posts/:id
Edit plan title or notes in any status without changing approval or version. Keep each output's own text in targets[].text — networks differ; the base text is optional source text. Editing text, media or targets creates a new immutable version; an approved post returns to pending_approval. A post that is scheduled, partially_published, published or failed cannot be edited — create a new post.
MCP tool: content_posts_update
Parameters
| Name | In | Type | Required | Description |
|---|
id | path | string (uuid)
| • | The post id returned by content_posts_create. |
title | body | string | null
| — | New plan title, or null to clear it. Changing it does not create a content version or revoke approval. |
notes | body | string | null
| — | New private plan notes, or null to clear them. Changing them does not create a content version or revoke approval. |
text | body | string
| — | Replacement text, in markdown; it may be empty for an idea or draft. Omit to keep the current text. |
media | body | string[]
| — | Replacement media list (replaces, never merges). Omit to keep the current media. |
targets | body | object[]
| — | Replacement output list (replaces, never merges). Omit to keep the current outputs. Send each kept output with its id; an output without an id is new. Resending the stored outputs — in any order, with or without inherited media — is not an edit. Changing one is an edit like changing the text: a new version, and an approved post returns to pending_approval. |
Response
Returns object.
| Field | Type | Always | Description |
|---|
id | string (uuid)
| • | Post id. |
text | string
| • | The current text of the post, in markdown. |
media | string[]
| • | Media references attached to the current version. |
notes | string | null
| • | Private plan notes, or null when unset. |
title | string | null
| • | Plan title, or null when unset. |
author | string
| • | Identity id of whoever composed the post. |
status | stringone of: idea, draft, pending_approval, approved, scheduled, partially_published, published, failed
| • | Post lifecycle status: idea (may have no text) -> draft (being written) -> pending_approval (submitted) -> approved (cleared to publish). Past approval the status follows EVERY output of the approved version (see outputs): scheduled while an output is queued or in flight and none is out yet; partially_published once some outputs are out and others are still queued, failed or never scheduled; published once every output is out; failed when an output gave up and none is out or queued; back to approved when nothing is live any more (all cancelled). A post in any of these five statuses can still have an output scheduled, including a failed one again. |
outputs | object[]
| • | One entry per output of this post, in targets order, with the state its publications add up to and the publication that decides it. Before approval every output is `none`. The post's status past approval is derived from exactly these states. |
targets | object[]
| • | The outputs of this post — a network and a variant each; several may share a network. |
folderId | string (uuid) | null
| • | Folder containing this post, or null. |
position | integer | null
| • | Order within its folder, or null for an unfiled post. |
createdAt | string (date-time)
| • | ISO-8601 creation time. |
updatedAt | string (date-time)
| • | ISO-8601 time of the last change. |
currentVersion | integer
| • | Version number of the current text. |
approvedVersion | integer | null
| • | The version an approval binds to, or null. Cleared on every edit: approving one text never covers another. |
POST/content/posts/:id/approve
Approve a post awaiting approval. The approval binds to the current version — a later edit revokes it.
MCP tool: content_posts_approve
Parameters
| Name | In | Type | Required | Description |
|---|
id | path | string (uuid)
| • | The post id returned by content_posts_create. |
Response
Returns object.
| Field | Type | Always | Description |
|---|
id | string (uuid)
| • | Post id. |
text | string
| • | The current text of the post, in markdown. |
media | string[]
| • | Media references attached to the current version. |
notes | string | null
| • | Private plan notes, or null when unset. |
title | string | null
| • | Plan title, or null when unset. |
author | string
| • | Identity id of whoever composed the post. |
status | stringone of: idea, draft, pending_approval, approved, scheduled, partially_published, published, failed
| • | Post lifecycle status: idea (may have no text) -> draft (being written) -> pending_approval (submitted) -> approved (cleared to publish). Past approval the status follows EVERY output of the approved version (see outputs): scheduled while an output is queued or in flight and none is out yet; partially_published once some outputs are out and others are still queued, failed or never scheduled; published once every output is out; failed when an output gave up and none is out or queued; back to approved when nothing is live any more (all cancelled). A post in any of these five statuses can still have an output scheduled, including a failed one again. |
outputs | object[]
| • | One entry per output of this post, in targets order, with the state its publications add up to and the publication that decides it. Before approval every output is `none`. The post's status past approval is derived from exactly these states. |
targets | object[]
| • | The outputs of this post — a network and a variant each; several may share a network. |
folderId | string (uuid) | null
| • | Folder containing this post, or null. |
position | integer | null
| • | Order within its folder, or null for an unfiled post. |
createdAt | string (date-time)
| • | ISO-8601 creation time. |
updatedAt | string (date-time)
| • | ISO-8601 time of the last change. |
currentVersion | integer
| • | Version number of the current text. |
approvedVersion | integer | null
| • | The version an approval binds to, or null. Cleared on every edit: approving one text never covers another. |
POST/content/posts/:id/media/attach
Attach stored media assets to a post, appended in the given order. Only assets that are ready can be attached. This counts as an edit: the post gets a new version and an approved post returns to pending_approval.
MCP tool: content_posts_attach_media
Parameters
| Name | In | Type | Required | Description |
|---|
id | path | string (uuid)
| • | The post id. |
mediaIds | body | string (uuid)[]
| • | Media asset ids, in the order they should appear on the post. |
Response
Returns object.
| Field | Type | Always | Description |
|---|
id | string (uuid)
| • | Post id. |
text | string
| • | The current text of the post, in markdown. |
media | string[]
| • | Media references attached to the current version. |
notes | string | null
| • | Private plan notes, or null when unset. |
title | string | null
| • | Plan title, or null when unset. |
author | string
| • | Identity id of whoever composed the post. |
status | stringone of: idea, draft, pending_approval, approved, scheduled, partially_published, published, failed
| • | Post lifecycle status: idea (may have no text) -> draft (being written) -> pending_approval (submitted) -> approved (cleared to publish). Past approval the status follows EVERY output of the approved version (see outputs): scheduled while an output is queued or in flight and none is out yet; partially_published once some outputs are out and others are still queued, failed or never scheduled; published once every output is out; failed when an output gave up and none is out or queued; back to approved when nothing is live any more (all cancelled). A post in any of these five statuses can still have an output scheduled, including a failed one again. |
outputs | object[]
| • | One entry per output of this post, in targets order, with the state its publications add up to and the publication that decides it. Before approval every output is `none`. The post's status past approval is derived from exactly these states. |
targets | object[]
| • | The outputs of this post — a network and a variant each; several may share a network. |
folderId | string (uuid) | null
| • | Folder containing this post, or null. |
position | integer | null
| • | Order within its folder, or null for an unfiled post. |
createdAt | string (date-time)
| • | ISO-8601 creation time. |
updatedAt | string (date-time)
| • | ISO-8601 time of the last change. |
currentVersion | integer
| • | Version number of the current text. |
approvedVersion | integer | null
| • | The version an approval binds to, or null. Cleared on every edit: approving one text never covers another. |
POST/content/posts/:id/media/detach
Remove media assets from a post, keeping the order of the rest. Like any edit, it produces a new version and returns an approved post to pending_approval. Removing the only video an output with a cover inherits is refused with 400 cover_without_video: clear that output's cover with content_posts_update first.
MCP tool: content_posts_detach_media
Parameters
| Name | In | Type | Required | Description |
|---|
id | path | string (uuid)
| • | The post id. |
mediaIds | body | string (uuid)[]
| • | Media asset ids to remove from the post. |
Response
Returns object.
| Field | Type | Always | Description |
|---|
id | string (uuid)
| • | Post id. |
text | string
| • | The current text of the post, in markdown. |
media | string[]
| • | Media references attached to the current version. |
notes | string | null
| • | Private plan notes, or null when unset. |
title | string | null
| • | Plan title, or null when unset. |
author | string
| • | Identity id of whoever composed the post. |
status | stringone of: idea, draft, pending_approval, approved, scheduled, partially_published, published, failed
| • | Post lifecycle status: idea (may have no text) -> draft (being written) -> pending_approval (submitted) -> approved (cleared to publish). Past approval the status follows EVERY output of the approved version (see outputs): scheduled while an output is queued or in flight and none is out yet; partially_published once some outputs are out and others are still queued, failed or never scheduled; published once every output is out; failed when an output gave up and none is out or queued; back to approved when nothing is live any more (all cancelled). A post in any of these five statuses can still have an output scheduled, including a failed one again. |
outputs | object[]
| • | One entry per output of this post, in targets order, with the state its publications add up to and the publication that decides it. Before approval every output is `none`. The post's status past approval is derived from exactly these states. |
targets | object[]
| • | The outputs of this post — a network and a variant each; several may share a network. |
folderId | string (uuid) | null
| • | Folder containing this post, or null. |
position | integer | null
| • | Order within its folder, or null for an unfiled post. |
createdAt | string (date-time)
| • | ISO-8601 creation time. |
updatedAt | string (date-time)
| • | ISO-8601 time of the last change. |
currentVersion | integer
| • | Version number of the current text. |
approvedVersion | integer | null
| • | The version an approval binds to, or null. Cleared on every edit: approving one text never covers another. |
POST/content/posts/:id/submit
Submit a draft for approval. Only a draft can be submitted, and only once it has at least one output and every output has non-empty text and its required variant fields filled.
MCP tool: content_posts_submit
Parameters
| Name | In | Type | Required | Description |
|---|
id | path | string (uuid)
| • | The post id returned by content_posts_create. |
Response
Returns object.
| Field | Type | Always | Description |
|---|
id | string (uuid)
| • | Post id. |
text | string
| • | The current text of the post, in markdown. |
media | string[]
| • | Media references attached to the current version. |
notes | string | null
| • | Private plan notes, or null when unset. |
title | string | null
| • | Plan title, or null when unset. |
author | string
| • | Identity id of whoever composed the post. |
status | stringone of: idea, draft, pending_approval, approved, scheduled, partially_published, published, failed
| • | Post lifecycle status: idea (may have no text) -> draft (being written) -> pending_approval (submitted) -> approved (cleared to publish). Past approval the status follows EVERY output of the approved version (see outputs): scheduled while an output is queued or in flight and none is out yet; partially_published once some outputs are out and others are still queued, failed or never scheduled; published once every output is out; failed when an output gave up and none is out or queued; back to approved when nothing is live any more (all cancelled). A post in any of these five statuses can still have an output scheduled, including a failed one again. |
outputs | object[]
| • | One entry per output of this post, in targets order, with the state its publications add up to and the publication that decides it. Before approval every output is `none`. The post's status past approval is derived from exactly these states. |
targets | object[]
| • | The outputs of this post — a network and a variant each; several may share a network. |
folderId | string (uuid) | null
| • | Folder containing this post, or null. |
position | integer | null
| • | Order within its folder, or null for an unfiled post. |
createdAt | string (date-time)
| • | ISO-8601 creation time. |
updatedAt | string (date-time)
| • | ISO-8601 time of the last change. |
currentVersion | integer
| • | Version number of the current text. |
approvedVersion | integer | null
| • | The version an approval binds to, or null. Cleared on every edit: approving one text never covers another. |
GET/content/posts/:id/versions
List every version of a post, oldest first — the audit trail of what was written and by whom.
MCP tool: content_posts_versions
Parameters
| Name | In | Type | Required | Description |
|---|
id | path | string (uuid)
| • | The post id returned by content_posts_create. |
Response
Returns object.
| Field | Type | Always | Description |
|---|
postId | string (uuid)
| • | The post these versions belong to. |
versions | object[]
| • | Every version, oldest first. |
POST/content/posts/:postId/delete
Delete an idea or draft only if it has no publication history. Cancelled and failed publications also block deletion. This deletes its content versions and emits content/post.deleted; other statuses return post_not_deletable.
MCP tool: content_posts_delete
Parameters
| Name | In | Type | Required | Description |
|---|
postId | path | string (uuid)
| • | The post id returned by content_posts_create. |
Response
Returns object.
| Field | Type | Always | Description |
|---|
postId | string (uuid)
| • | The post id returned by content_posts_create. |
deleted | booleanone of: true
| • | The post and its content versions were deleted. |
POST/content/posts/:postId/move
Move a post in any status into a folder, before another post there, or out to the unfiled plan. This changes only plan order, never approval or content version. The destination must belong to this product.
MCP tool: content_posts_move
Parameters
| Name | In | Type | Required | Description |
|---|
postId | path | string (uuid)
| • | The post id returned by content_posts_create. |
folderId | body | string (uuid) | null
| • | Destination folder in this product, or null to leave the post unfiled. |
beforePostId | body | string (uuid)
| — | Put the post immediately before this post in the destination folder. Omit to append it. |
Response
Returns object.
| Field | Type | Always | Description |
|---|
id | string (uuid)
| • | Post id. |
text | string
| • | The current text of the post, in markdown. |
media | string[]
| • | Media references attached to the current version. |
notes | string | null
| • | Private plan notes, or null when unset. |
title | string | null
| • | Plan title, or null when unset. |
author | string
| • | Identity id of whoever composed the post. |
status | stringone of: idea, draft, pending_approval, approved, scheduled, partially_published, published, failed
| • | Post lifecycle status: idea (may have no text) -> draft (being written) -> pending_approval (submitted) -> approved (cleared to publish). Past approval the status follows EVERY output of the approved version (see outputs): scheduled while an output is queued or in flight and none is out yet; partially_published once some outputs are out and others are still queued, failed or never scheduled; published once every output is out; failed when an output gave up and none is out or queued; back to approved when nothing is live any more (all cancelled). A post in any of these five statuses can still have an output scheduled, including a failed one again. |
outputs | object[]
| • | One entry per output of this post, in targets order, with the state its publications add up to and the publication that decides it. Before approval every output is `none`. The post's status past approval is derived from exactly these states. |
targets | object[]
| • | The outputs of this post — a network and a variant each; several may share a network. |
folderId | string (uuid) | null
| • | Folder containing this post, or null. |
position | integer | null
| • | Order within its folder, or null for an unfiled post. |
createdAt | string (date-time)
| • | ISO-8601 creation time. |
updatedAt | string (date-time)
| • | ISO-8601 time of the last change. |
currentVersion | integer
| • | Version number of the current text. |
approvedVersion | integer | null
| • | The version an approval binds to, or null. Cleared on every edit: approving one text never covers another. |
POST/content/posts/:postId/skip-output
Mark one output of an approved post as "won't publish": it counts as closed, so a post whose other outputs are all published becomes published. It is not a content edit — no new version, the approval stands — and content_posts_unskip_output undoes it. Only past approval (409 post_not_approved otherwise). Refused with 409 output_has_live_publication while the output has a queued or in-flight publication (cancel it first), 409 already_published once it is out, 409 may_already_be_live while a finished publication of it may be live, 409 variant_not_found for an unknown output. Schedule, publish and record of a skipped output answer 409 output_skipped.
MCP tool: content_posts_skip_output
Parameters
| Name | In | Type | Required | Description |
|---|
postId | path | string (uuid)
| • | The post id returned by content_posts_create. |
variantId | body | string
| • | The output to act on: a targets[].id (or outputs[].variantId) from content_posts_get. |
Response
Returns object.
| Field | Type | Always | Description |
|---|
id | string (uuid)
| • | Post id. |
text | string
| • | The current text of the post, in markdown. |
media | string[]
| • | Media references attached to the current version. |
notes | string | null
| • | Private plan notes, or null when unset. |
title | string | null
| • | Plan title, or null when unset. |
author | string
| • | Identity id of whoever composed the post. |
status | stringone of: idea, draft, pending_approval, approved, scheduled, partially_published, published, failed
| • | Post lifecycle status: idea (may have no text) -> draft (being written) -> pending_approval (submitted) -> approved (cleared to publish). Past approval the status follows EVERY output of the approved version (see outputs): scheduled while an output is queued or in flight and none is out yet; partially_published once some outputs are out and others are still queued, failed or never scheduled; published once every output is out; failed when an output gave up and none is out or queued; back to approved when nothing is live any more (all cancelled). A post in any of these five statuses can still have an output scheduled, including a failed one again. |
outputs | object[]
| • | One entry per output of this post, in targets order, with the state its publications add up to and the publication that decides it. Before approval every output is `none`. The post's status past approval is derived from exactly these states. |
targets | object[]
| • | The outputs of this post — a network and a variant each; several may share a network. |
folderId | string (uuid) | null
| • | Folder containing this post, or null. |
position | integer | null
| • | Order within its folder, or null for an unfiled post. |
createdAt | string (date-time)
| • | ISO-8601 creation time. |
updatedAt | string (date-time)
| • | ISO-8601 time of the last change. |
currentVersion | integer
| • | Version number of the current text. |
approvedVersion | integer | null
| • | The version an approval binds to, or null. Cleared on every edit: approving one text never covers another. |
POST/content/posts/:postId/to-draft
Move an idea to draft, or take a post awaiting approval back to draft, keeping its title, text and version (no new version; the submission is withdrawn). Only an idea or a pending_approval post can make this transition. Add text or output texts before content_posts_submit.
MCP tool: content_posts_to_draft
Parameters
| Name | In | Type | Required | Description |
|---|
postId | path | string (uuid)
| • | The post id returned by content_posts_create. |
Response
Returns object.
| Field | Type | Always | Description |
|---|
id | string (uuid)
| • | Post id. |
text | string
| • | The current text of the post, in markdown. |
media | string[]
| • | Media references attached to the current version. |
notes | string | null
| • | Private plan notes, or null when unset. |
title | string | null
| • | Plan title, or null when unset. |
author | string
| • | Identity id of whoever composed the post. |
status | stringone of: idea, draft, pending_approval, approved, scheduled, partially_published, published, failed
| • | Post lifecycle status: idea (may have no text) -> draft (being written) -> pending_approval (submitted) -> approved (cleared to publish). Past approval the status follows EVERY output of the approved version (see outputs): scheduled while an output is queued or in flight and none is out yet; partially_published once some outputs are out and others are still queued, failed or never scheduled; published once every output is out; failed when an output gave up and none is out or queued; back to approved when nothing is live any more (all cancelled). A post in any of these five statuses can still have an output scheduled, including a failed one again. |
outputs | object[]
| • | One entry per output of this post, in targets order, with the state its publications add up to and the publication that decides it. Before approval every output is `none`. The post's status past approval is derived from exactly these states. |
targets | object[]
| • | The outputs of this post — a network and a variant each; several may share a network. |
folderId | string (uuid) | null
| • | Folder containing this post, or null. |
position | integer | null
| • | Order within its folder, or null for an unfiled post. |
createdAt | string (date-time)
| • | ISO-8601 creation time. |
updatedAt | string (date-time)
| • | ISO-8601 time of the last change. |
currentVersion | integer
| • | Version number of the current text. |
approvedVersion | integer | null
| • | The version an approval binds to, or null. Cleared on every edit: approving one text never covers another. |
POST/content/posts/:postId/to-idea
Move a draft back to idea without changing its content version. Only a draft can make this transition; use content_posts_to_draft when it is ready for approval.
MCP tool: content_posts_to_idea
Parameters
| Name | In | Type | Required | Description |
|---|
postId | path | string (uuid)
| • | The post id returned by content_posts_create. |
Response
Returns object.
| Field | Type | Always | Description |
|---|
id | string (uuid)
| • | Post id. |
text | string
| • | The current text of the post, in markdown. |
media | string[]
| • | Media references attached to the current version. |
notes | string | null
| • | Private plan notes, or null when unset. |
title | string | null
| • | Plan title, or null when unset. |
author | string
| • | Identity id of whoever composed the post. |
status | stringone of: idea, draft, pending_approval, approved, scheduled, partially_published, published, failed
| • | Post lifecycle status: idea (may have no text) -> draft (being written) -> pending_approval (submitted) -> approved (cleared to publish). Past approval the status follows EVERY output of the approved version (see outputs): scheduled while an output is queued or in flight and none is out yet; partially_published once some outputs are out and others are still queued, failed or never scheduled; published once every output is out; failed when an output gave up and none is out or queued; back to approved when nothing is live any more (all cancelled). A post in any of these five statuses can still have an output scheduled, including a failed one again. |
outputs | object[]
| • | One entry per output of this post, in targets order, with the state its publications add up to and the publication that decides it. Before approval every output is `none`. The post's status past approval is derived from exactly these states. |
targets | object[]
| • | The outputs of this post — a network and a variant each; several may share a network. |
folderId | string (uuid) | null
| • | Folder containing this post, or null. |
position | integer | null
| • | Order within its folder, or null for an unfiled post. |
createdAt | string (date-time)
| • | ISO-8601 creation time. |
updatedAt | string (date-time)
| • | ISO-8601 time of the last change. |
currentVersion | integer
| • | Version number of the current text. |
approvedVersion | integer | null
| • | The version an approval binds to, or null. Cleared on every edit: approving one text never covers another. |
POST/content/posts/:postId/unskip-output
Undo content_posts_skip_output: the output can be scheduled, published or recorded again, and the post status is recomputed. Always allowed past approval; unskipping an output that is not skipped changes nothing.
MCP tool: content_posts_unskip_output
Parameters
| Name | In | Type | Required | Description |
|---|
postId | path | string (uuid)
| • | The post id returned by content_posts_create. |
variantId | body | string
| • | The output to act on: a targets[].id (or outputs[].variantId) from content_posts_get. |
Response
Returns object.
| Field | Type | Always | Description |
|---|
id | string (uuid)
| • | Post id. |
text | string
| • | The current text of the post, in markdown. |
media | string[]
| • | Media references attached to the current version. |
notes | string | null
| • | Private plan notes, or null when unset. |
title | string | null
| • | Plan title, or null when unset. |
author | string
| • | Identity id of whoever composed the post. |
status | stringone of: idea, draft, pending_approval, approved, scheduled, partially_published, published, failed
| • | Post lifecycle status: idea (may have no text) -> draft (being written) -> pending_approval (submitted) -> approved (cleared to publish). Past approval the status follows EVERY output of the approved version (see outputs): scheduled while an output is queued or in flight and none is out yet; partially_published once some outputs are out and others are still queued, failed or never scheduled; published once every output is out; failed when an output gave up and none is out or queued; back to approved when nothing is live any more (all cancelled). A post in any of these five statuses can still have an output scheduled, including a failed one again. |
outputs | object[]
| • | One entry per output of this post, in targets order, with the state its publications add up to and the publication that decides it. Before approval every output is `none`. The post's status past approval is derived from exactly these states. |
targets | object[]
| • | The outputs of this post — a network and a variant each; several may share a network. |
folderId | string (uuid) | null
| • | Folder containing this post, or null. |
position | integer | null
| • | Order within its folder, or null for an unfiled post. |
createdAt | string (date-time)
| • | ISO-8601 creation time. |
updatedAt | string (date-time)
| • | ISO-8601 time of the last change. |
currentVersion | integer
| • | Version number of the current text. |
approvedVersion | integer | null
| • | The version an approval binds to, or null. Cleared on every edit: approving one text never covers another. |
GET/content/publications
List publications — the queue and the timeline — newest slot first.
MCP tool: content_publications_list
Parameters
| Name | In | Type | Required | Description |
|---|
state | query | stringone of: pending, publishing, published, failed, cancelled
| — | Return only publications in this state. |
postId | query | string (uuid)
| — | Return only publications of this post. |
channelId | query | string (uuid)
| — | Return only publications to this channel. |
limit | query | integer
| — | Maximum number to return (1-100, default 50). |
offset | query | integer
| — | How many to skip, for paging. |
scheduledFrom | query | string (date-time)
| — | Return only publications scheduled at or after this ISO-8601 time. |
scheduledTo | query | string (date-time)
| — | Return only publications scheduled at or before this ISO-8601 time. |
Response
Returns object.
| Field | Type | Always | Description |
|---|
total | integer
| • | Total number of matching publications, ignoring paging. |
publications | object[]
| • | The page of publications, newest slot first. |
POST/content/publications
Queue one OUTPUT of an approved post for publication to one channel at a given time. A post's outputs are its targets (content_posts_get: targets[].id) — a network and a variant each, several per network allowed, e.g. a YouTube video and two Shorts. What goes out is that output of the post's APPROVED version: its text, post type, fields and media (its own list, or the post's when it inherits); there is no `text` parameter: per-output text lives in `targets[].text`, set with content_posts_update. variantId picks the output; omit it when the channel's network has exactly one output and it is inferred. When the network has several outputs and none is named, the call is refused with 409 variant_required and the list of outputs — pick one and call again. An output of another network is 409 network_not_targeted; an unknown id is 409 variant_not_found. On a reminder channel this does not call the network: at the scheduled time a person is notified to publish by hand, and confirms afterwards with content_publications_confirm. The channel must be on (enabled true in content_channels_list); a channel that is off is refused with 409 channel_disabled — turn it on with content_channels_update or pick another channel. An output goes to a channel once: while it has a publication there that is pending, publishing or published, a second one is refused with 409 already_scheduled naming that publication and output — move it with content_publications_reschedule, or cancel it with content_publications_cancel and schedule again; the post's OTHER outputs may still be scheduled to the same channel. A cancelled or failed publication does not count. Repeating a call with the same idempotency key (by default derived from post, channel, output and time) returns its publication, not a conflict — unless that publication was cancelled or failed: then nothing is queued and the call is refused with 409 idempotency_key_spent; pick a different time or pass a new idempotencyKey.
MCP tool: content_publications_schedule
Parameters
| Name | In | Type | Required | Description |
|---|
postId | body | string (uuid)
| • | The post to publish. It must already be approved — an unapproved version never publishes — and it must have an output for the channel's network. |
channelId | body | string (uuid)
| • | The channel to publish to. Its network must be one the post has an output for; what goes out is that output of the post's approved version. |
variantId | body | string
| — | Which OUTPUT of the post to publish: a targets[].id from content_posts_get. Omit when the channel's network has exactly one output — it is inferred. When the network has several (a YouTube video and two Shorts), omitting it is refused with 409 variant_required listing them; pick one and call again. An id of another network's output is refused with 409 network_not_targeted; an id the post does not have with 409 variant_not_found. |
scheduledAt | body | string (date-time)
| • | ISO-8601 time to publish. In the past means "as soon as the next tick runs". |
idempotencyKey | body | string
| — | Your own key for this publishing intent, unique within the product. Resending the same key returns the SAME publication instead of creating a second one — a duplicate in a client feed is the worst defect this product can ship. Omit it and one is derived from post, channel and time. |
Response
Returns object.
| Field | Type | Always | Description |
|---|
id | string (uuid)
| • | Publication id. |
text | string
| • | The exact text that will be published to this channel — markdown already flattened to plain network text at schedule time. Not the markdown source; that stays on the post. |
media | string[]
| • | The media asset ids this publication sends — the output's own list, or the post's when the output inherits — copied when it was scheduled. What the adapter uploads, character for character. |
state | stringone of: pending, publishing, published, failed, cancelled
| • | pending (queued) -> publishing (the reminder was sent; a person is publishing by hand) -> published (confirmed) | failed | cancelled. |
fields | object
| • | The network variant fields it goes out with — a YouTube title, a Reddit subreddit — copied from the post's approved output when it was scheduled. |
postId | string (uuid)
| • | The post being published. |
costUsd | number | null
| • | What publishing this costs in USD, computed when it was scheduled. On X a post carrying a link costs 13x a plain one. Null means the cost is not known — an auto channel refuses to dispatch it. |
warning | string | null
| • | Set on a published publication when something did not go as asked but the post is out — e.g. YouTube refused the cover as the thumbnail (an unverified channel, a file too large). The post is NOT retried; fix it on the network by hand. Null otherwise. |
attempts | integer
| • | How many dispatch attempts have been made. |
postType | string | null
| • | The post type this publication goes out as (video, short, reel, post, ...), copied from the post's approved variant when it was scheduled. Null on a publication scheduled before post targets existed. |
channelId | string (uuid)
| • | The channel it is published to. |
costBasis | string | nullone of: plain, link, free
| • | Which rate produced costUsd: `plain` the flat per-post rate, `link` the higher rate a post carrying a link is charged, `free` a channel whose rates are both zero. Null when the cost is not known. |
createdAt | string (date-time)
| • | ISO-8601 creation time. |
lastError | string | null
| • | The most recent failure, if any. |
updatedAt | string (date-time)
| • | ISO-8601 time of the last change. |
variantId | string
| • | The post output this publication is bound to (targets[].id). One live publication per post × channel × output. A publication scheduled before outputs existed carries the channel's network name. |
externalId | string | null
| • | The network's id for the published post, once known. |
externalUrl | string | null
| • | URL of the published post, once known. |
publishedAt | string (date-time) | null
| • | ISO-8601 time it was confirmed published. |
scheduledAt | string (date-time)
| • | ISO-8601 time the publication is due. |
coverMediaId | string | null
| • | The output's cover image asset id, copied when it was scheduled, or null for none. YouTube sets it as the video's thumbnail after the upload; a reminder links it for the person publishing by hand. |
publicationId | string (uuid)
| • | The publication id again, under the name the content/publication.settled event uses to report this publication finishing. Always identical to id. |
reminderCount | integer
| • | How many reminders have been sent for this publication. |
idempotencyKey | string
| • | The key that makes a retry the same publication. |
reminderSentAt | string (date-time) | null
| • | ISO-8601 time the most recent reminder went out, or null if none has. |
GET/content/publications/:id
Read one publication: its state, its slot, and where it ended up.
MCP tool: content_publications_get
Parameters
| Name | In | Type | Required | Description |
|---|
id | path | string (uuid)
| • | The publication id returned by content_publications_schedule. |
Response
Returns object.
| Field | Type | Always | Description |
|---|
id | string (uuid)
| • | Publication id. |
text | string
| • | The exact text that will be published to this channel — markdown already flattened to plain network text at schedule time. Not the markdown source; that stays on the post. |
media | string[]
| • | The media asset ids this publication sends — the output's own list, or the post's when the output inherits — copied when it was scheduled. What the adapter uploads, character for character. |
state | stringone of: pending, publishing, published, failed, cancelled
| • | pending (queued) -> publishing (the reminder was sent; a person is publishing by hand) -> published (confirmed) | failed | cancelled. |
fields | object
| • | The network variant fields it goes out with — a YouTube title, a Reddit subreddit — copied from the post's approved output when it was scheduled. |
postId | string (uuid)
| • | The post being published. |
costUsd | number | null
| • | What publishing this costs in USD, computed when it was scheduled. On X a post carrying a link costs 13x a plain one. Null means the cost is not known — an auto channel refuses to dispatch it. |
warning | string | null
| • | Set on a published publication when something did not go as asked but the post is out — e.g. YouTube refused the cover as the thumbnail (an unverified channel, a file too large). The post is NOT retried; fix it on the network by hand. Null otherwise. |
attempts | integer
| • | How many dispatch attempts have been made. |
postType | string | null
| • | The post type this publication goes out as (video, short, reel, post, ...), copied from the post's approved variant when it was scheduled. Null on a publication scheduled before post targets existed. |
channelId | string (uuid)
| • | The channel it is published to. |
costBasis | string | nullone of: plain, link, free
| • | Which rate produced costUsd: `plain` the flat per-post rate, `link` the higher rate a post carrying a link is charged, `free` a channel whose rates are both zero. Null when the cost is not known. |
createdAt | string (date-time)
| • | ISO-8601 creation time. |
lastError | string | null
| • | The most recent failure, if any. |
updatedAt | string (date-time)
| • | ISO-8601 time of the last change. |
variantId | string
| • | The post output this publication is bound to (targets[].id). One live publication per post × channel × output. A publication scheduled before outputs existed carries the channel's network name. |
externalId | string | null
| • | The network's id for the published post, once known. |
externalUrl | string | null
| • | URL of the published post, once known. |
publishedAt | string (date-time) | null
| • | ISO-8601 time it was confirmed published. |
scheduledAt | string (date-time)
| • | ISO-8601 time the publication is due. |
coverMediaId | string | null
| • | The output's cover image asset id, copied when it was scheduled, or null for none. YouTube sets it as the video's thumbnail after the upload; a reminder links it for the person publishing by hand. |
publicationId | string (uuid)
| • | The publication id again, under the name the content/publication.settled event uses to report this publication finishing. Always identical to id. |
reminderCount | integer
| • | How many reminders have been sent for this publication. |
idempotencyKey | string
| • | The key that makes a retry the same publication. |
reminderSentAt | string (date-time) | null
| • | ISO-8601 time the most recent reminder went out, or null if none has. |
POST/content/publications/:id/cancel
Cancel a publication that has not finished. A publication whose reminder already went out can still be cancelled — until someone confirms it. An auto publication that is being sent to the network right now is refused with 409 publication_in_flight: wait for its outcome. A finished publication that may still be live (its output is uncertain) blocks the output until a person settles it: confirm it if the post is on the network, or cancel it with checkedNotLive: true after checking that it is not.
MCP tool: content_publications_cancel
Parameters
| Name | In | Type | Required | Description |
|---|
id | path | string (uuid)
| • | The publication id returned by content_publications_schedule. |
checkedNotLive | body | boolean
| — | Only for a FINISHED publication that may still be live (an output in state uncertain: an automatic attempt ended without an answer). Pass true to state that you looked at the network and the post is NOT there; the publication then stops blocking its output. Without it such a cancel is refused with 409 may_already_be_live. Refused with 409 publication_is_live when the network itself reported the post — confirm it instead. Ignored for a publication that is still open. |
Response
Returns object.
| Field | Type | Always | Description |
|---|
id | string (uuid)
| • | Publication id. |
text | string
| • | The exact text that will be published to this channel — markdown already flattened to plain network text at schedule time. Not the markdown source; that stays on the post. |
media | string[]
| • | The media asset ids this publication sends — the output's own list, or the post's when the output inherits — copied when it was scheduled. What the adapter uploads, character for character. |
state | stringone of: pending, publishing, published, failed, cancelled
| • | pending (queued) -> publishing (the reminder was sent; a person is publishing by hand) -> published (confirmed) | failed | cancelled. |
fields | object
| • | The network variant fields it goes out with — a YouTube title, a Reddit subreddit — copied from the post's approved output when it was scheduled. |
postId | string (uuid)
| • | The post being published. |
costUsd | number | null
| • | What publishing this costs in USD, computed when it was scheduled. On X a post carrying a link costs 13x a plain one. Null means the cost is not known — an auto channel refuses to dispatch it. |
warning | string | null
| • | Set on a published publication when something did not go as asked but the post is out — e.g. YouTube refused the cover as the thumbnail (an unverified channel, a file too large). The post is NOT retried; fix it on the network by hand. Null otherwise. |
attempts | integer
| • | How many dispatch attempts have been made. |
postType | string | null
| • | The post type this publication goes out as (video, short, reel, post, ...), copied from the post's approved variant when it was scheduled. Null on a publication scheduled before post targets existed. |
channelId | string (uuid)
| • | The channel it is published to. |
costBasis | string | nullone of: plain, link, free
| • | Which rate produced costUsd: `plain` the flat per-post rate, `link` the higher rate a post carrying a link is charged, `free` a channel whose rates are both zero. Null when the cost is not known. |
createdAt | string (date-time)
| • | ISO-8601 creation time. |
lastError | string | null
| • | The most recent failure, if any. |
updatedAt | string (date-time)
| • | ISO-8601 time of the last change. |
variantId | string
| • | The post output this publication is bound to (targets[].id). One live publication per post × channel × output. A publication scheduled before outputs existed carries the channel's network name. |
externalId | string | null
| • | The network's id for the published post, once known. |
externalUrl | string | null
| • | URL of the published post, once known. |
publishedAt | string (date-time) | null
| • | ISO-8601 time it was confirmed published. |
scheduledAt | string (date-time)
| • | ISO-8601 time the publication is due. |
coverMediaId | string | null
| • | The output's cover image asset id, copied when it was scheduled, or null for none. YouTube sets it as the video's thumbnail after the upload; a reminder links it for the person publishing by hand. |
publicationId | string (uuid)
| • | The publication id again, under the name the content/publication.settled event uses to report this publication finishing. Always identical to id. |
reminderCount | integer
| • | How many reminders have been sent for this publication. |
idempotencyKey | string
| • | The key that makes a retry the same publication. |
reminderSentAt | string (date-time) | null
| • | ISO-8601 time the most recent reminder went out, or null if none has. |
POST/content/publications/:id/confirm
Confirm that a person published the post by hand. Supply externalUrl to record its URL; omit it to store no URL. This closes the publication. Confirmation remains available after publication when the person did not confirm at the time, and for a finished publication that may be live (its output is uncertain) once the person has seen the post on the network. An auto publication that is being sent to the network right now is refused with 409 publication_in_flight.
MCP tool: content_publications_confirm
Parameters
| Name | In | Type | Required | Description |
|---|
id | path | string (uuid)
| • | The publication id returned by content_publications_schedule. |
externalUrl | body | string (uri)
| — | URL of the post the person actually published. Worth supplying — it is what identifies the post later. |
externalId | body | string
| — | The network's own id for the post, if known. |
Response
Returns object.
| Field | Type | Always | Description |
|---|
id | string (uuid)
| • | Publication id. |
text | string
| • | The exact text that will be published to this channel — markdown already flattened to plain network text at schedule time. Not the markdown source; that stays on the post. |
media | string[]
| • | The media asset ids this publication sends — the output's own list, or the post's when the output inherits — copied when it was scheduled. What the adapter uploads, character for character. |
state | stringone of: pending, publishing, published, failed, cancelled
| • | pending (queued) -> publishing (the reminder was sent; a person is publishing by hand) -> published (confirmed) | failed | cancelled. |
fields | object
| • | The network variant fields it goes out with — a YouTube title, a Reddit subreddit — copied from the post's approved output when it was scheduled. |
postId | string (uuid)
| • | The post being published. |
costUsd | number | null
| • | What publishing this costs in USD, computed when it was scheduled. On X a post carrying a link costs 13x a plain one. Null means the cost is not known — an auto channel refuses to dispatch it. |
warning | string | null
| • | Set on a published publication when something did not go as asked but the post is out — e.g. YouTube refused the cover as the thumbnail (an unverified channel, a file too large). The post is NOT retried; fix it on the network by hand. Null otherwise. |
attempts | integer
| • | How many dispatch attempts have been made. |
postType | string | null
| • | The post type this publication goes out as (video, short, reel, post, ...), copied from the post's approved variant when it was scheduled. Null on a publication scheduled before post targets existed. |
channelId | string (uuid)
| • | The channel it is published to. |
costBasis | string | nullone of: plain, link, free
| • | Which rate produced costUsd: `plain` the flat per-post rate, `link` the higher rate a post carrying a link is charged, `free` a channel whose rates are both zero. Null when the cost is not known. |
createdAt | string (date-time)
| • | ISO-8601 creation time. |
lastError | string | null
| • | The most recent failure, if any. |
updatedAt | string (date-time)
| • | ISO-8601 time of the last change. |
variantId | string
| • | The post output this publication is bound to (targets[].id). One live publication per post × channel × output. A publication scheduled before outputs existed carries the channel's network name. |
externalId | string | null
| • | The network's id for the published post, once known. |
externalUrl | string | null
| • | URL of the published post, once known. |
publishedAt | string (date-time) | null
| • | ISO-8601 time it was confirmed published. |
scheduledAt | string (date-time)
| • | ISO-8601 time the publication is due. |
coverMediaId | string | null
| • | The output's cover image asset id, copied when it was scheduled, or null for none. YouTube sets it as the video's thumbnail after the upload; a reminder links it for the person publishing by hand. |
publicationId | string (uuid)
| • | The publication id again, under the name the content/publication.settled event uses to report this publication finishing. Always identical to id. |
reminderCount | integer
| • | How many reminders have been sent for this publication. |
idempotencyKey | string
| • | The key that makes a retry the same publication. |
reminderSentAt | string (date-time) | null
| • | ISO-8601 time the most recent reminder went out, or null if none has. |
POST/content/publications/:id/remind
Send the publish reminder AGAIN. Repeat reminders are never automatic: the system cannot double-publish, but a person can, so a second nudge only ever happens because someone asked for it. Only for a reminder channel whose first reminder went out: an auto channel is refused with 409 not_a_reminder_channel, and an auto publication being sent right now with 409 publication_in_flight.
MCP tool: content_publications_remind
Parameters
| Name | In | Type | Required | Description |
|---|
id | path | string (uuid)
| • | The publication id returned by content_publications_schedule. |
Response
Returns object.
| Field | Type | Always | Description |
|---|
id | string (uuid)
| • | Publication id. |
text | string
| • | The exact text that will be published to this channel — markdown already flattened to plain network text at schedule time. Not the markdown source; that stays on the post. |
media | string[]
| • | The media asset ids this publication sends — the output's own list, or the post's when the output inherits — copied when it was scheduled. What the adapter uploads, character for character. |
state | stringone of: pending, publishing, published, failed, cancelled
| • | pending (queued) -> publishing (the reminder was sent; a person is publishing by hand) -> published (confirmed) | failed | cancelled. |
fields | object
| • | The network variant fields it goes out with — a YouTube title, a Reddit subreddit — copied from the post's approved output when it was scheduled. |
postId | string (uuid)
| • | The post being published. |
costUsd | number | null
| • | What publishing this costs in USD, computed when it was scheduled. On X a post carrying a link costs 13x a plain one. Null means the cost is not known — an auto channel refuses to dispatch it. |
warning | string | null
| • | Set on a published publication when something did not go as asked but the post is out — e.g. YouTube refused the cover as the thumbnail (an unverified channel, a file too large). The post is NOT retried; fix it on the network by hand. Null otherwise. |
attempts | integer
| • | How many dispatch attempts have been made. |
postType | string | null
| • | The post type this publication goes out as (video, short, reel, post, ...), copied from the post's approved variant when it was scheduled. Null on a publication scheduled before post targets existed. |
channelId | string (uuid)
| • | The channel it is published to. |
costBasis | string | nullone of: plain, link, free
| • | Which rate produced costUsd: `plain` the flat per-post rate, `link` the higher rate a post carrying a link is charged, `free` a channel whose rates are both zero. Null when the cost is not known. |
createdAt | string (date-time)
| • | ISO-8601 creation time. |
lastError | string | null
| • | The most recent failure, if any. |
updatedAt | string (date-time)
| • | ISO-8601 time of the last change. |
variantId | string
| • | The post output this publication is bound to (targets[].id). One live publication per post × channel × output. A publication scheduled before outputs existed carries the channel's network name. |
externalId | string | null
| • | The network's id for the published post, once known. |
externalUrl | string | null
| • | URL of the published post, once known. |
publishedAt | string (date-time) | null
| • | ISO-8601 time it was confirmed published. |
scheduledAt | string (date-time)
| • | ISO-8601 time the publication is due. |
coverMediaId | string | null
| • | The output's cover image asset id, copied when it was scheduled, or null for none. YouTube sets it as the video's thumbnail after the upload; a reminder links it for the person publishing by hand. |
publicationId | string (uuid)
| • | The publication id again, under the name the content/publication.settled event uses to report this publication finishing. Always identical to id. |
reminderCount | integer
| • | How many reminders have been sent for this publication. |
idempotencyKey | string
| • | The key that makes a retry the same publication. |
reminderSentAt | string (date-time) | null
| • | ISO-8601 time the most recent reminder went out, or null if none has. |
POST/content/publications/:id/reschedule
Move a publication to a new time. It returns to the queue, and the reminder for the old slot is spent — a new one goes out at the new time. An auto publication that is being sent to the network right now is refused with 409 publication_in_flight; one whose channel has been turned off is refused with 409 channel_disabled.
MCP tool: content_publications_reschedule
Parameters
| Name | In | Type | Required | Description |
|---|
id | path | string (uuid)
| • | The publication id returned by content_publications_schedule. |
scheduledAt | body | string (date-time)
| • | New ISO-8601 time. The publication returns to the queue. |
Response
Returns object.
| Field | Type | Always | Description |
|---|
id | string (uuid)
| • | Publication id. |
text | string
| • | The exact text that will be published to this channel — markdown already flattened to plain network text at schedule time. Not the markdown source; that stays on the post. |
media | string[]
| • | The media asset ids this publication sends — the output's own list, or the post's when the output inherits — copied when it was scheduled. What the adapter uploads, character for character. |
state | stringone of: pending, publishing, published, failed, cancelled
| • | pending (queued) -> publishing (the reminder was sent; a person is publishing by hand) -> published (confirmed) | failed | cancelled. |
fields | object
| • | The network variant fields it goes out with — a YouTube title, a Reddit subreddit — copied from the post's approved output when it was scheduled. |
postId | string (uuid)
| • | The post being published. |
costUsd | number | null
| • | What publishing this costs in USD, computed when it was scheduled. On X a post carrying a link costs 13x a plain one. Null means the cost is not known — an auto channel refuses to dispatch it. |
warning | string | null
| • | Set on a published publication when something did not go as asked but the post is out — e.g. YouTube refused the cover as the thumbnail (an unverified channel, a file too large). The post is NOT retried; fix it on the network by hand. Null otherwise. |
attempts | integer
| • | How many dispatch attempts have been made. |
postType | string | null
| • | The post type this publication goes out as (video, short, reel, post, ...), copied from the post's approved variant when it was scheduled. Null on a publication scheduled before post targets existed. |
channelId | string (uuid)
| • | The channel it is published to. |
costBasis | string | nullone of: plain, link, free
| • | Which rate produced costUsd: `plain` the flat per-post rate, `link` the higher rate a post carrying a link is charged, `free` a channel whose rates are both zero. Null when the cost is not known. |
createdAt | string (date-time)
| • | ISO-8601 creation time. |
lastError | string | null
| • | The most recent failure, if any. |
updatedAt | string (date-time)
| • | ISO-8601 time of the last change. |
variantId | string
| • | The post output this publication is bound to (targets[].id). One live publication per post × channel × output. A publication scheduled before outputs existed carries the channel's network name. |
externalId | string | null
| • | The network's id for the published post, once known. |
externalUrl | string | null
| • | URL of the published post, once known. |
publishedAt | string (date-time) | null
| • | ISO-8601 time it was confirmed published. |
scheduledAt | string (date-time)
| • | ISO-8601 time the publication is due. |
coverMediaId | string | null
| • | The output's cover image asset id, copied when it was scheduled, or null for none. YouTube sets it as the video's thumbnail after the upload; a reminder links it for the person publishing by hand. |
publicationId | string (uuid)
| • | The publication id again, under the name the content/publication.settled event uses to report this publication finishing. Always identical to id. |
reminderCount | integer
| • | How many reminders have been sent for this publication. |
idempotencyKey | string
| • | The key that makes a retry the same publication. |
reminderSentAt | string (date-time) | null
| • | ISO-8601 time the most recent reminder went out, or null if none has. |
POST/content/publications/publish
Publish NOW: the same queue, scheduled for this instant, publishing one output of the post's approved version to the channel (there is no `text` parameter: per-output text lives in `targets[].text`, set with content_posts_update). variantId picks the output; it is inferred when the channel's network has exactly one, and refused with 409 variant_required (listing the outputs) when it has several and none is named. There is no separate synchronous path — a fast path would differ exactly where it matters, on failure. The same one-publication-per-output-per-channel rule as content_publications_schedule: if that output already has a pending, publishing or published publication on the channel, this is refused with 409 already_scheduled naming it. An idempotencyKey already spent on a cancelled or failed publication is refused with 409 idempotency_key_spent.
MCP tool: content_publications_publish
Parameters
| Name | In | Type | Required | Description |
|---|
postId | body | string (uuid)
| • | The post to publish. It must already be approved and have an output for the channel's network. |
channelId | body | string (uuid)
| • | The channel to publish to. What goes out is the chosen output of the post's approved version. |
variantId | body | string
| — | Which OUTPUT of the post to publish: a targets[].id from content_posts_get. Omit when the channel's network has exactly one output — it is inferred. When the network has several (a YouTube video and two Shorts), omitting it is refused with 409 variant_required listing them; pick one and call again. An id of another network's output is refused with 409 network_not_targeted; an id the post does not have with 409 variant_not_found. |
idempotencyKey | body | string
| — | Your own key for this publishing intent, unique within the product. Resending the same key returns the SAME publication instead of creating a second one — a duplicate in a client feed is the worst defect this product can ship. Omit it and one is derived from post, channel and time. |
Response
Returns object.
| Field | Type | Always | Description |
|---|
id | string (uuid)
| • | Publication id. |
text | string
| • | The exact text that will be published to this channel — markdown already flattened to plain network text at schedule time. Not the markdown source; that stays on the post. |
media | string[]
| • | The media asset ids this publication sends — the output's own list, or the post's when the output inherits — copied when it was scheduled. What the adapter uploads, character for character. |
state | stringone of: pending, publishing, published, failed, cancelled
| • | pending (queued) -> publishing (the reminder was sent; a person is publishing by hand) -> published (confirmed) | failed | cancelled. |
fields | object
| • | The network variant fields it goes out with — a YouTube title, a Reddit subreddit — copied from the post's approved output when it was scheduled. |
postId | string (uuid)
| • | The post being published. |
costUsd | number | null
| • | What publishing this costs in USD, computed when it was scheduled. On X a post carrying a link costs 13x a plain one. Null means the cost is not known — an auto channel refuses to dispatch it. |
warning | string | null
| • | Set on a published publication when something did not go as asked but the post is out — e.g. YouTube refused the cover as the thumbnail (an unverified channel, a file too large). The post is NOT retried; fix it on the network by hand. Null otherwise. |
attempts | integer
| • | How many dispatch attempts have been made. |
postType | string | null
| • | The post type this publication goes out as (video, short, reel, post, ...), copied from the post's approved variant when it was scheduled. Null on a publication scheduled before post targets existed. |
channelId | string (uuid)
| • | The channel it is published to. |
costBasis | string | nullone of: plain, link, free
| • | Which rate produced costUsd: `plain` the flat per-post rate, `link` the higher rate a post carrying a link is charged, `free` a channel whose rates are both zero. Null when the cost is not known. |
createdAt | string (date-time)
| • | ISO-8601 creation time. |
lastError | string | null
| • | The most recent failure, if any. |
updatedAt | string (date-time)
| • | ISO-8601 time of the last change. |
variantId | string
| • | The post output this publication is bound to (targets[].id). One live publication per post × channel × output. A publication scheduled before outputs existed carries the channel's network name. |
externalId | string | null
| • | The network's id for the published post, once known. |
externalUrl | string | null
| • | URL of the published post, once known. |
publishedAt | string (date-time) | null
| • | ISO-8601 time it was confirmed published. |
scheduledAt | string (date-time)
| • | ISO-8601 time the publication is due. |
coverMediaId | string | null
| • | The output's cover image asset id, copied when it was scheduled, or null for none. YouTube sets it as the video's thumbnail after the upload; a reminder links it for the person publishing by hand. |
publicationId | string (uuid)
| • | The publication id again, under the name the content/publication.settled event uses to report this publication finishing. Always identical to id. |
reminderCount | integer
| • | How many reminders have been sent for this publication. |
idempotencyKey | string
| • | The key that makes a retry the same publication. |
reminderSentAt | string (date-time) | null
| • | ISO-8601 time the most recent reminder went out, or null if none has. |
POST/content/publications/record
Record that a person already published one OUTPUT of an approved post by hand — no scheduling needed. This never publishes anything: it stores the fact, and the post's status follows (one of two outputs recorded → partially_published; every output published or skipped → published). When the output has a publication on its network that is queued, reminded or may already be live, THAT publication is confirmed (as content_publications_confirm would); otherwise a publication is stored already published, which nothing ever sends or reminds. An output goes to a network once: one already out on any channel of the network is refused with 409 already_published naming it, one being sent by the automatic publisher right now with 409 publication_in_flight (wait for its outcome), a skipped one with 409 output_skipped (content_posts_unskip_output first). Only for a post past approval (409 post_not_approved). A queued automatic publication the publisher has claimed but not started sending is confirmed too, and then never sent. Repeating the same call (same post, output and channel) returns the same publication; a record is stored once, so a repeat naming a different publishedAt or externalUrl is refused with 409 record_mismatch listing the stored values.
MCP tool: content_publications_record
Parameters
| Name | In | Type | Required | Description |
|---|
postId | body | string (uuid)
| • | The approved post one of whose outputs a person published by hand. |
variantId | body | string
| — | Which OUTPUT was published: a targets[].id from content_posts_get. Omit when the channel's network has exactly one output (or, without channelId, when the post has exactly one output). Several and none named: 409 variant_required listing them; an id of another network's output with a channelId: 409 network_not_targeted; an id the post does not have: 409 variant_not_found. |
channelId | body | string (uuid)
| — | The channel it went out on (content_channels_list). Omit to file it under the output's network's reminder channel; a network with none is refused with 409 no_reminder_channel. |
externalUrl | body | string (uri)
| — | URL of the post on the network. Worth supplying — it is what identifies the post later. |
externalId | body | string
| — | The network's own id for the post, if known. |
publishedAt | body | string (date-time)
| — | ISO-8601 time the post went out; now when omitted. A time in the future is refused with 400. |
Response
Returns object.
| Field | Type | Always | Description |
|---|
id | string (uuid)
| • | Publication id. |
text | string
| • | The exact text that will be published to this channel — markdown already flattened to plain network text at schedule time. Not the markdown source; that stays on the post. |
media | string[]
| • | The media asset ids this publication sends — the output's own list, or the post's when the output inherits — copied when it was scheduled. What the adapter uploads, character for character. |
state | stringone of: pending, publishing, published, failed, cancelled
| • | pending (queued) -> publishing (the reminder was sent; a person is publishing by hand) -> published (confirmed) | failed | cancelled. |
fields | object
| • | The network variant fields it goes out with — a YouTube title, a Reddit subreddit — copied from the post's approved output when it was scheduled. |
postId | string (uuid)
| • | The post being published. |
costUsd | number | null
| • | What publishing this costs in USD, computed when it was scheduled. On X a post carrying a link costs 13x a plain one. Null means the cost is not known — an auto channel refuses to dispatch it. |
warning | string | null
| • | Set on a published publication when something did not go as asked but the post is out — e.g. YouTube refused the cover as the thumbnail (an unverified channel, a file too large). The post is NOT retried; fix it on the network by hand. Null otherwise. |
attempts | integer
| • | How many dispatch attempts have been made. |
postType | string | null
| • | The post type this publication goes out as (video, short, reel, post, ...), copied from the post's approved variant when it was scheduled. Null on a publication scheduled before post targets existed. |
channelId | string (uuid)
| • | The channel it is published to. |
costBasis | string | nullone of: plain, link, free
| • | Which rate produced costUsd: `plain` the flat per-post rate, `link` the higher rate a post carrying a link is charged, `free` a channel whose rates are both zero. Null when the cost is not known. |
createdAt | string (date-time)
| • | ISO-8601 creation time. |
lastError | string | null
| • | The most recent failure, if any. |
updatedAt | string (date-time)
| • | ISO-8601 time of the last change. |
variantId | string
| • | The post output this publication is bound to (targets[].id). One live publication per post × channel × output. A publication scheduled before outputs existed carries the channel's network name. |
externalId | string | null
| • | The network's id for the published post, once known. |
externalUrl | string | null
| • | URL of the published post, once known. |
publishedAt | string (date-time) | null
| • | ISO-8601 time it was confirmed published. |
scheduledAt | string (date-time)
| • | ISO-8601 time the publication is due. |
coverMediaId | string | null
| • | The output's cover image asset id, copied when it was scheduled, or null for none. YouTube sets it as the video's thumbnail after the upload; a reminder links it for the person publishing by hand. |
publicationId | string (uuid)
| • | The publication id again, under the name the content/publication.settled event uses to report this publication finishing. Always identical to id. |
reminderCount | integer
| • | How many reminders have been sent for this publication. |
idempotencyKey | string
| • | The key that makes a retry the same publication. |
reminderSentAt | string (date-time) | null
| • | ISO-8601 time the most recent reminder went out, or null if none has. |
POST/content/publications/validate
Dry run: check one output of a post per channel WITHOUT scheduling anything. Per channel entry, variantId names the output (targets[].id); omit it when the channel's network has one output and it is inferred — with several and none named the entry reports variant_required and the outputs to pick from. Returns everything that would go wrong — a network the post has no output for, a required variant field left empty, a post type that needs a video, text too long, the output's media not stored yet, a link a network will not linkify — and what publishing would cost. On X a post carrying a link costs $0.20 against $0.015 for a plain one, so call this before you schedule.
MCP tool: content_publications_validate
Parameters
| Name | In | Type | Required | Description |
|---|
postId | body | string (uuid)
| • | The post to check. It does not have to be approved yet — this never publishes. |
channels | body | object[]
| • | One entry per channel you are considering. Check them all in one call. |
scheduledAt | body | string (date-time)
| — | The slot you intend to use, ISO-8601. Only the daily ceiling depends on it; omit for "now". |
Response
Returns object.
| Field | Type | Always | Description |
|---|
ok | boolean
| • | True when nothing blocks scheduling. False when any channel carries an issue of severity "error" — the same fact as the issue list below, stated once so it can be branched on. |
postId | string (uuid)
| • | The post that was checked. |
channels | object[]
| • | One result per requested channel, in the order they were given. |
errorCount | integer
| • | How many error-severity issues there are across every channel. Warnings are not counted: they do not block scheduling. |
firstErrorMessage | string | null
| • | The first error message in report order, ready to show a person or put in a notification. Null when there are no errors. |
Was this page helpful?Thanks for your feedback!