Skip to content

Publications

Validate and schedule approved variants, confirm reminder posts, and handle publication states and conflicts.

A publication records one approved post variant, its channel and its scheduled time. Use it to track delivery state, cost, reminders, attempts and metrics. Scheduling creates the record; publication completes later, after a person confirms a reminder or delivery settles.

The shape of a publication

Field
What it is
id
Publication id
publicationId
The same id again, under the name the settled event uses
postId, channelId
What is being published, and where
text
Approved variant text rendered as plain network text
postType
Variant post type captured at scheduling; null for publications created before targets were supported
fields
Variant fields captured at scheduling, such as a title or subreddit
costUsd
What publishing this costs, computed when it was scheduled, or null when unknown
costBasis
plain, link or free, or null
scheduledAt
ISO-8601 slot it is due in
state
pending, publishing, published, failed or cancelled
attempts
Dispatch attempts made so far
idempotencyKey
The key that makes a retry the same publication
externalId, externalUrl
The network’s id and permalink, once known
reminderSentAt, reminderCount
When the most recent reminder went out, and how many have
publishedAt
When it was confirmed published
lastError
The most recent failure, if any
createdAt, updatedAt
ISO-8601 timestamps

The post retains the Markdown source. The publication stores the exact rendered text, post type and fields selected from the approved version, so a subsequent change cannot alter the queued content.

States

pending ──▶ publishing ──▶ published
   │             │
   └──────┬──────┴──▶ failed
          └────────▶ cancelled

published, failed and cancelled are terminal; nothing moves out of them.

For reminders, publishing means a person has been notified and still needs to publish and confirm. Confirmation moves the publication to published.

Validate before you schedule

Call content_publications_validate with a postId and up to 20 entries in channels: [{ channelId }]. It checks each network’s current variant against the channel’s capabilities and publishes nothing. The report includes:

  • ok — true when nothing blocks scheduling.
  • errorCount — how many error-severity issues there are across every channel. Warnings are not counted; they do not block.
  • firstErrorMessage — the first error in report order, ready to show a person.
  • channels[] — one result per requested channel, in the order given, each with the network, the display name, the Markdown that was checked, the renderedText that would actually go out, the issue list, the cost, and the daily ceiling that was checked.

You can validate before approval. Validation reads the current draft; scheduling uses the approved version.

Issue codes

The set is stable and append-only.

Code
Meaning
text_empty
There is no text to publish
text_too_long
Longer than the channel’s character limit
too_many_media
More attachments than the channel accepts
link_not_supported
The network will not take a link
multiple_links
More links than LinkedIn will preview (advisory)
media_missing
An attached asset does not exist
media_not_ready
An attached asset is not stored yet
media_not_supported
The network will not take this kind of attachment
video_required
The post type requires video, but no video is attached
cost_unknown
The cost cannot be computed, which blocks publishing
daily_ceiling_exceeded
The channel’s daily ceiling is already spent for that slot
network_not_targeted
The post has no target for this channel’s network
post_type_not_supported
The channel does not accept the variant’s post type
field_required
A required variant field is empty
field_too_long
A variant field exceeds its maximum length

Each issue includes severity (error or warning), a readable message and the affected input when applicable. Field issues identify fields.<id>, such as fields.title. YouTube uploads and Shorts, Instagram and Facebook Reels, and TikTok video posts require an attached video.

The ceiling report says where the limit came from: channel (an explicit override), adapter (a live figure from the network) or catalog (the built-in fallback).

Cost

Validation reports the calculated cost before you schedule. Scheduling stores that cost on the publication. Both use the channel descriptor’s current rates, including your overrides, rather than reading the built-in rates directly.

costBasis names the rate that was applied:

  • plain — the flat per-post rate.
  • link — the higher rate a post carrying a link is charged. The default X rates are $0.20 with a link and $0.015 without one.
  • free — the channel’s rates are both zero.

A post counts as carrying a link when its text contains an explicit http:// or https:// URL, a host beginning www., or a bare host ending in a recognized top-level domain. Handles and hashtags are not links. A new or unrecognized top-level domain can be missed and priced as plain text. Include https:// to make link detection explicit.

Scheduling and publishing

Call content_publications_schedule with the approved post, a channel and scheduledAt. A past time queues it for the next scheduler tick. A post already in scheduled state can be scheduled to additional target channels without another approval.

The publication takes the approved variant’s text, post type and fields. Schedule, publish and validate do not accept replacement text: an extra text field receives 400 over REST or an unrecognized-key error over MCP. Set targets[].text on the post and have it approved before scheduling a different network version.

Scheduling rejects a channel outside the post’s targets (network_not_targeted) or a variant that fails the channel’s type, required-field, field-length or video checks. Validate first to see these issues before queueing.

Only one pending, publishing or published publication is allowed for a post on a channel. A second scheduling request returns 409 already_scheduled with the existing publicationId and scheduledAt. Reschedule that publication to change its time, or cancel it before starting again. Once it is published, write a new post to publish on that channel again. Failed and cancelled publications do not reserve the channel.

Concurrent scheduling requests follow the same rule: one succeeds and the other receives already_scheduled. If needed, list publications filtered by postId to inspect the result. Publications created before Content 0.12.1 were not changed, so older records may show more than one live publication for the same post and channel.

content_publications_publish uses the same queue with the current time. Both calls return a queued record. Wait for content/publication.settled and match its publicationId to the returned id to learn the outcome.

Idempotency

Supply a product-unique idempotencyKey to identify a publishing intent. Without one, Content derives a key from the post, channel and time. Repeating a successful request returns its publication before duplicate-scheduling checks run.

A key whose publication failed or was cancelled is spent. Reusing it returns 409 idempotency_key_spent, including publicationId and scheduledAt, and queues nothing. To try again, use a new key or a different scheduled time.

Reminders and confirmation

At the scheduled time, a reminder channel emits content/publication.reminder-due, notifies a person and moves the publication to publishing. The reminder includes the post type and variant fields above the text. Members control these notifications under the publishing scope.

After publishing on the network, call content_publications_confirm. Supply externalUrl to identify the published post and externalId if available. Confirmation stays available while the publication is open, including when the person published earlier but forgot to confirm.

If the first reminder was missed, call content_publications_remind. Content never repeats a reminder automatically because the person might publish twice. The call requires an existing reminder and a reminder channel; an automatic channel returns 409 not_a_reminder_channel.

Retries and failure

On an auto channel, Content sends the queued publication through its bound connector when the slot is due. Read the publication state or wait for content/publication.settled to learn the result.

Automatic publications allow up to five attempts. The four retry delays are 1 minute, 5 minutes, 15 minutes and 1 hour, without jitter. If all attempts are used, this spans a little over an hour.

Retrying is only ever done when it is known to be safe: the publication carries no network id and owes no reconciliation, or the network has been asked and has answered that nothing was published, or the adapter itself reported that nothing was sent.

When the attempts are spent the publication becomes failed and lastError says why. content/publication.failed carries the same short diagnostic, which never contains a credential.

A paused channel holds its publications for later dispatch. Content rechecks the pause instead of marking those publications failed.

Cancelling and rescheduling

Use content_publications_cancel to stop an unfinished publication. For a reminder channel, cancellation stays available after the reminder until confirmation. The response contains the cancelled record, so no settled event is needed to learn that result.

Use content_publications_reschedule to move the same unfinished publication to another time. It returns to the queue, and a new reminder is sent at the new time. Finished publications (published, failed or cancelled) cannot be cancelled or moved.

An automatic publication in publishing state is being sent and its outcome is unknown. Cancel, reschedule, confirm and remind return 409 publication_in_flight. Read content_publications_get or wait for content/publication.settled before acting. This restriction does not apply to reminder publications in publishing state.

If a publication changes between a call’s read and write, the action is checked against its new state. A finished or in-flight publication returns the corresponding conflict; other changes return 409 publication_changed. Read the record again and retry only if the action still applies.

Listing

content_publications_list returns publications newest slot first. Filter by state, postId, channelId, and the slot window scheduledFrom / scheduledTo; page with limit (1 to 100, default 50) and offset. A window whose start is after its end is rejected.

Tools

Tool
What it does
content_publications_validate
Dry run against one or more channels
content_publications_schedule
Queue an approved post for a slot
content_publications_publish
Queue it for this instant
content_publications_list
List publications, newest slot first
content_publications_get
Read one publication
content_publications_cancel
Cancel one that has not finished
content_publications_reschedule
Move it to a new slot
content_publications_confirm
Record that a person published it by hand
content_publications_remind
Send the reminder again

Next steps

  • Channels: where the limits and the rates come from.
  • Metrics: what a published publication reports back.
Was this page helpful?
Esc

Start typing to search the docs.

↑↓navigate↵select