List posts (keyset paginated)
Newest first, keyset-paginated via `next_cursor`. Narrow with `statuses`, a scheduled-time range, `campaign`, `label`, or `channel` (a channel id or a provider name like `linkedin`) — so "my scheduled LinkedIn posts" is one call, not a range pulled down and filtered locally. Rows are excerpts and carry no fit verdict unless you ask: `include=ready` adds `ready` per row — absent means "not checked", which is not the same as `false` — and caps the page at 25, because each judged row then costs a full post read. Read one post in full with `GET /posts/{id}`.
/postsNewest first, keyset-paginated via next_cursor. Narrow with statuses, a scheduled-time range, campaign, label, or channel (a channel id or a provider name like linkedin) — so "my scheduled LinkedIn posts" is one call, not a range pulled down and filtered locally. Rows are excerpts and carry no fit verdict unless you ask: include=ready adds ready per row — absent means "not checked", which is not the same as false — and caps the page at 25, because each judged row then costs a full post read. Read one post in full with GET /posts/{id}.
Authorization
bearerAuth In: header
Query Parameters
1 <= length1 <= length1 <= length1 <= value <= 200100Organization slug (OAuth actors with several orgs); API keys imply theirs.
Response Body
application/json
application/json
application/json
curl -X GET "https://example.com/posts"{ "items": [ { "id": "string", "status": "draft", "scheduled_at": "string", "excerpt": "string", "ready": true, "channels": [ { "channel_id": "string", "provider": "string", "name": "string", "format": "post", "status": "draft", "url": "string" } ], "labels": [ { "id": "string", "name": "string" } ], "campaign": "string", "created_at": "string", "updated_at": "string", "published_at": "string" } ], "next_cursor": "string"}{ "error": { "code": "string", "message": "string", "reason": "string", "index": 0, "request_id": "string" }}{ "error": { "code": "string", "message": "string", "reason": "string", "index": 0, "request_id": "string" }}Per-channel effective capabilities + provider manifests GET
Previous Page
Create a post — draft, scheduled, or published now POST
One call for one or many channels (`channels` takes ids or provider names, mixed). Copy goes in `content` as markdown — one field, one form. Text with no markup posts exactly as typed (handles, URLs and `snake_case` are safe); `\*` shows a literal delimiter. `scheduled_at` decides the mode: omitted saves a draft; a future instant (ISO-8601 with offset, or relative shorthand `"30m"`/`"2h"`/`"3d"`/`"1w"`) books it; `"now"` publishes immediately. Arming refuses atomically when the post is not `ready` — nothing is saved, and the 400 carries the refused post as `error.post` (`reason: "not_ready"`), so read its per-channel `errors[]` and `fixes[]` there rather than re-sending the write without `scheduled_at`. The response carries per-channel validation: each entry's `errors[]` name a `code`, the `field` to change, a human message with the real numbers, and a machine `fix` where one exists. `ready` is the server's only publishability signal — a draft with errors saves fine; only arming refuses. `autofix` (default true) applies suggested fixes server-side and reports them in `fixes_applied`.