# Publishing

Draft, schedule or publish now, follow each target, and edit, delete or restore what went out.

## Draft, schedule or publish now [#draft-schedule-or-publish-now]

`POST /v1/posts` (`create_post` in MCP, scope `posts:write`) makes a post in one of three ways:

| You send | The post | It goes out |
| --- | --- | --- |
| Nothing more | `draft` | Never, until you schedule or publish it |
| `"status": "scheduled"` and a future `scheduledAt` | `scheduled` | At `scheduledAt` |
| `"queue": true` | `scheduled` | At its profile's next free posting time ([Posting queues](/queues)) |

- To schedule a draft later, `PATCH /v1/posts/{id}` with `status` and `scheduledAt`, or `queue: true`. To take a scheduled post back, send `"status": "draft"`.
- `POST /v1/posts/{id}/publish` (`publish_post`) sends it now, draft or scheduled: it starts within seconds.
- A draft may still lack what a network needs, such as an image for Instagram. Scheduling or publishing it is refused (`400`) until it has it.
- A target whose account was moved to another profile since never goes out: scheduling or publishing the post is refused (`400`) until you take it out of the post's `targets`, and a post scheduled before the move fails that target.

## Statuses [#statuses]

Each target (an account and a format) goes out on its own, so one network failing never holds back the others.

| Target `status` | Meaning |
| --- | --- |
| `pending` | Waiting for its time |
| `publishing` | Going out now |
| `published` | Live: `url` links to it, when the network gives one |
| `failed` | Not published: `error` says why |
| `unknown` | The network did not confirm, and Rizamo could not tell whether it went out |

Once every target is done, the post becomes `published`, `partially_published` (some went out, or a chain stopped partway) or `failed` (none went out). A target that is `unknown` counts as not live until someone checks.

Follow a post with `GET /v1/posts/{id}` (`get_post`), or take the `post.target.published`, `post.target.failed`, `post.published` and `post.failed` [webhooks](/webhooks#event-types) instead of asking.

## When it goes out [#when-it-goes-out]

- Rizamo looks for posts due every minute, so a post goes out within a minute or so of its time.
- A target that cannot go out within 30 minutes of its time fails, rather than going out late.
- It waits longer while a network's [daily or minute cap](/limits#publishing) has no room, or while its [video copy](/media#what-rizamo-changes-for-each-network) is made: its window then starts again.
- When a network refuses for a moment, Rizamo tries again within the window.
- After an answer that is not clear, the call is never sent again blindly: Rizamo first looks for the post on the account, and if it cannot tell, the target is `unknown`. A post the network confirms is not there is sent again.

## Publish again [#publish-again]

`POST /v1/posts/{id}/publish` also sends every `failed` target again. Fix the cause first: a new login for the account, or new text, media or options with `PATCH /v1/posts/{id}`.

A target that is `unknown` is left alone. Check the account on the network: if the post is not there, make a new post for that account.

## Edit [#edit]

- **Before it goes out:** change anything with `PATCH /v1/posts/{id}` (`update_post`). Targets not live yet take the new version, even while they wait for their time.
- **After it went out:** its status and time no longer change (`409`). A live target's text changes only with `"applyToPublished": true`, and only where the network allows it: today on Facebook, stories aside, on accounts with `edit_posts` in their `capabilities`. Options, files and a first comment already posted never change.
- The answer's `edits` say what happened to each target: `applied`, `not_supported` (the network cannot make this change), `skipped` (live, and `applyToPublished` was not set) or `failed` (send the same update again).

## Delete and restore [#delete-and-restore]

- `DELETE /v1/posts/{id}` (`delete_post`) keeps the post, marked deleted, and stops it going out.
- Live targets are taken off their networks where the network and the account allow it (`delete_posts` in the account's `capabilities`). Facebook stories and TikTok posts cannot be removed through the networks' APIs: delete those in the network's app. Each target's `removal` is `removed`, `not_supported` or `failed`, and deleting the post again tries the failed ones. A post moved to Deleted because its account was removed stays live on the network: `kept`. A live target whose account was moved to another profile since is taken off only by someone who reaches that profile; for anyone else it stays live and `failed`, saying why.
- `POST /v1/posts/{id}/restore` (`restore_post`) brings it back within 30 days. A scheduled post whose time has passed, or with a target whose account was moved to another profile, comes back as a draft. After those days a deleted post goes for good, unless a target is still live on its network.

## First comment [#first-comment]

A target's `firstComment` is posted right after the post goes live: on Threads, as its first reply. One the network would not take is refused when the post is made (`400`), with the reason, never dropped later:

- Stories take none, on Instagram and Facebook alike.
- Rizamo does not comment on YouTube videos yet.
- A TikTok draft (`draft: true`) has no post to comment on yet, and a TikTok post with `disableComments` takes none.
- It is longer than the network takes for a comment (each network's page says how long), or on Threads it has more links than a post takes.

When the network refuses it once the post is live, the post still counts as published, and nothing reports it yet: check the post on the network.

## Stories and text [#stories-and-text]

Instagram and Facebook stories take no text through their APIs. A story target's own `text` is refused (`400`). A story that shares the post's text, beside a feed post or a reel, goes out without it: `validate` warns about it, and nothing is refused.

## Chains [#chains]

A post's `chain` holds more posts after the first, each published as a reply to the one before: a thread on Threads, up to 24 more. A target can have a chain of its own.

- Networks that publish chains get every part; the others get the first post alone, which `validate` warns about.
- A part the network refuses stops the chain: the parts before it stay live, the target's `chainResults` say where it stopped, and the post is `partially_published`. Publishing again goes on from that part.
- A target with a chain takes no `firstComment`: its next part is its first reply.

## Check before you post [#check-before-you-post]

`POST /v1/posts/validate` (`validate_post`) takes what `POST /v1/posts` takes and creates nothing:

- `ok`: creating it would work.
- `problems`: everything that would stop it now, all at once.
- `warnings`: what would stop it later, such as a draft for Instagram with no image, and what goes out other than sent, such as a story without the post's text.
- `targets`: each target's format, and the `changes` Rizamo makes to its files.
- `scheduledAt`: when it would go out, in UTC; with `queue: true`, the time the queue would give it now.

It counts toward the [limits](/limits) like any other call.

## Time zones [#time-zones]

- A `scheduledAt` with `Z` or an offset (`2026-12-01T09:00:00+01:00`) is that exact moment.
- One without (`2026-12-01T09:00`) is a local time, read in the post's `timeZone` or else its profile's. When the clock skips an hour for summer time, a time inside the gap moves later by the gap; when it repeats an hour, the first one counts.
- Every time Rizamo answers is in UTC.
- A profile's `timeZone` is a name such as `Europe/Lisbon`. A profile made with the API is in `UTC` unless you send one. The "Default" profile a team starts with takes its owner's time zone on their first visit to the web app, before anything is planned in it.

## Accounts that cannot publish [#accounts-that-cannot-publish]

| Account | What happens |
| --- | --- |
| `billing`: `awaiting_payment` | Connected, waiting for its payment. Drafts are fine; scheduling or publishing to it is refused (`400`) until an owner pays in the web app. Rizamo reads nothing of it until then. |
| `billing`: `paused` | The plan no longer covers it. Drafts are fine; scheduling or publishing is refused. Rizamo still reads its posts, numbers and comments for 30 days after it was paused. |
| `status`: `disconnected` | The network ended Rizamo's access. Its targets fail until someone [logs in again](/connecting-accounts#log-in-again). |
| Removed | Its posts are dealt with when it is removed ([Account](/concepts#account)). A target on it that is scheduled or published anyway, such as a post restored from Deleted, is refused or fails saying the account was removed, until it is connected again. |

A post scheduled before its account stopped being active fails at its time, with the reason in `error`.
