Docs › API reference › Drafts
Drafts API: autosave, then publish or schedule
Save a post as you write it, autosave changes safely, then publish or schedule it in one call.
A draft is text you have started but not sent. The API calls it an idea, and the paths say /v1/ideas.
The OmniPosts composer saves to it as you type. You can use it the same way, or to hand a draft from one tool to
another.
A draft has a status: draft (you can still change it), promoted (it was sent as a post), or discarded.
targetDrafts is a map from channel id to that channel's tweaks. Each tweak can have body, mediaIds and
settings, the same as a target in Posts.
{
"id": "0192a3b4-0000-7000-8000-0000000000d1",
"body": "Draft of the launch post",
"mediaIds": [],
"status": "draft",
"targetDrafts": {
"0192a3b4-0000-7000-8000-000000000001": { "body": "Shorter version for this channel" }
},
"createdAt": "2026-10-14T09:00:00.120331Z",
"updatedAt": "2026-10-14T09:00:00.120331Z"
}
POST /v1/ideas
Create a draft
POST /v1/ideas takes body, mediaIds and targetDrafts, all optional. The answer is 201 with the draft.
curl https://omniposts.dev/v1/ideas \
-H "Authorization: Bearer omniposts_..." \
-H "Content-Type: application/json" \
-d '{ "body": "Draft of the launch post" }'
GET /v1/ideas
List and get drafts
GET /v1/ideas lists drafts, newest first. Filter with status=draft or status=promoted. Discarded drafts are
never listed. It is paginated: see Pagination.
GET /v1/ideas/{id} returns one draft. An id from another organization is 404.
PATCH /v1/ideas/{id}
Autosave a draft
PATCH /v1/ideas/{id} saves changes. Send only the fields you want to change: a field you leave out stays as it
was. A field you send replaces the old value completely (mediaIds and targetDrafts are replaced as a whole,
not merged).
Send If-Match with the draft's updatedAt so two tabs cannot overwrite each other:
The answer is 200 with the draft, including its new updatedAt. Use that value for your next save.
- Send
updatedAtback exactly as received. It has microsecond precision. Keep it as a string; do not round-trip it through a date type that keeps only milliseconds. - If the draft changed since, you get
409 stale_writeanddetails.currentholds the draft as it is now. Merge your change into it and save again. - If the draft is no longer a
draft, you get409 not_editable, withdetails.current. If-Matchis optional. Without it the save always wins.
Autosave has its own, lower rate limit, 30 saves a minute for your organization. See Rate limits. Save when the person pauses, not on every key press.
curl -X PATCH https://omniposts.dev/v1/ideas/0192a3b4-0000-7000-8000-0000000000d1 \
-H "Authorization: Bearer omniposts_..." \
-H 'If-Match: "2026-10-14T09:00:00.120331Z"' \
-H "Content-Type: application/json" \
-d '{ "body": "Draft of the launch post, second pass" }'
POST /v1/ideas/{id}/promote
Publish a draft
POST /v1/ideas/{id}/promote turns a draft into a post. It behaves exactly like
POST /v1/groups: the same 201, 207, 200 and 422 answers, the same
rejections, and the same Idempotency-Key header. The text and media come from the draft.
| Field | Meaning |
|---|---|
targets |
The channels to post to, as in a normal post. A channel with saved tweaks in the draft's targetDrafts uses them, unless you set that field here |
scheduledFor, timezone |
When to send, as in a normal post |
queue |
true to take the next free posting time. Not with scheduledFor |
A draft is published once:
- Send an
Idempotency-Key. If your request times out, send the same call again with the same key and you get the same post back with200andIdempotent-Replayed: true. See Idempotency. - Without a matching key, promoting a draft that is already published (or discarded) gives
409 already_promoted. Nothing is sent twice. - If nothing can be published (
422 nothing_to_publish), the draft stays a draft, so you can fix it and try again. - After a successful promote the draft's
statusispromoted, and it can no longer be changed.
curl https://omniposts.dev/v1/ideas/0192a3b4-0000-7000-8000-0000000000d1/promote \
-H "Authorization: Bearer omniposts_..." \
-H "Idempotency-Key: promote-launch-draft" \
-H "Content-Type: application/json" \
-d '{ "targets": [{ "channelId": "0192a3b4-0000-7000-8000-000000000001" }], "queue": true }'
DELETE /v1/ideas/{id}
Discard a draft
DELETE /v1/ideas/{id} discards a draft. The answer is 204, and the draft no longer appears in the list.
Discarding twice is not an error. A draft that was already published cannot be discarded: you get
409 not_editable, because it is part of the record of what was sent.