Posts
A post is one publication on one channel. Posting the same thing to two
channels creates two posts that share a group; the group is what the
calendar shows as a single card, and what the delete routes take. The
composer, the agent, the CLI and your own scripts all
go through the same routes and the same validation.
POST /posts create, schedule, publish now, or updateGET /posts list a date rangePUT /posts/{id}/status draft ⇄ scheduledGET /posts/{id}/missing recent provider items, to repair a missing releaseIdPUT /posts/{id}/release-id record the provider idDELETE /posts/{id} delete a post and its groupDELETE /posts/group/{group} delete a groupCreate a post
Section titled “Create a post”POST /postsRequest body
Section titled “Request body”| Field | Type | Required | Description |
|---|---|---|---|
type |
string |
Yes | draft, schedule, now or update (see below) |
date |
string |
Yes | Publish date, UTC ISO 8601. Ignored by now, which publishes immediately |
shortLink |
boolean |
Yes | Rewrite the links of the text as tracked short links |
tags |
Tag[] |
Yes | [] when none |
posts |
PostItem[] |
Yes | At least one |
order |
string |
No | Order of the posts |
inter |
number |
No | Repeat every n days (a repeating post) |
creationMethod |
string |
No | API (default) or CLI: how the post is tagged in analytics |
type decides what happens next:
type |
Effect |
|---|---|
schedule |
Queued, published at date |
now |
Published immediately; date is replaced by the current time |
draft |
Saved, nothing runs. Still validated like a scheduled post |
update |
Rewrites an existing post (value[].id names it, group its group) without restarting its publishing workflow |
| Field | Type | Required | Description |
|---|---|---|---|
value |
string |
Yes | Tag name |
label |
string |
Yes | Tag label |
PostItem
Section titled “PostItem”| Field | Type | Required | Description |
|---|---|---|---|
integration |
{ id } |
Yes | The channel |
value |
PostContent[] |
Yes | At least one. The first is the post; the following ones publish as comments under it |
settings |
object |
Yes | Provider settings, with __type naming the platform. Validated on every type, drafts included |
group |
string |
No | Reuse a group id to add this post to an existing group (with update) |
PostContent
Section titled “PostContent”| Field | Type | Required | Description |
|---|---|---|---|
content |
string |
Yes | The text. HTML is accepted and sanitised |
image |
Media[] |
Yes | [] when none. Instagram and TikTok both need at least one |
id |
string |
No | The post this item updates (type: update) |
delay |
number |
No | Minutes to wait before publishing this comment after the previous item |
| Field | Type | Required | Description |
|---|---|---|---|
id |
string |
Yes | Media id, from an upload |
path |
string |
Yes | Public URL ending in .png, .jpg, .jpeg, .gif, .webp or .mp4 |
alt |
string |
No | Alt text |
thumbnail |
string |
No | Thumbnail URL |
Settings per platform
Section titled “Settings per platform”settings.__type must match the channel’s platform. The two platforms you can
publish to are documented on their own pages:
Instagram (instagram-standalone) and
TikTok (tiktok).
Examples
Section titled “Examples”{ "type": "schedule", "date": "2026-11-03T10:00:00.000Z", "shortLink": false, "tags": [], "posts": [ { "integration": { "id": "your-instagram-id" }, "value": [ { "content": "Trois choses que personne ne dit sur Malte.", "image": [{ "id": "med-123", "path": "https://media.usenadia.com/reel.mp4" }] } ], "settings": { "__type": "instagram-standalone", "post_type": "post" } } ]}{ "type": "now", "date": "2026-11-03T10:00:00.000Z", "shortLink": false, "tags": [], "posts": [ { "integration": { "id": "your-tiktok-id" }, "value": [ { "content": "Trois choses que personne ne dit sur Malte. #malte #expat", "image": [{ "id": "med-123", "path": "https://media.usenadia.com/reel.mp4" }] } ], "settings": { "__type": "tiktok", "content_posting_method": "DIRECT_POST", "privacy_level": "PUBLIC_TO_EVERYONE", "duet": true, "stitch": true, "comment": true, "autoAddMusic": "no", "brand_content_toggle": false, "brand_organic_toggle": false } } ]}{ "type": "schedule", "date": "2026-11-03T10:00:00.000Z", "shortLink": false, "tags": [{ "value": "malte", "label": "Malte" }], "posts": [ { "integration": { "id": "your-instagram-id" }, "value": [ { "content": "Trois choses que personne ne dit sur Malte.", "image": [{ "id": "med-123", "path": "https://media.usenadia.com/reel.mp4" }] }, { "content": "Le guide complet est en bio.", "image": [], "delay": 5 } ], "settings": { "__type": "instagram-standalone", "post_type": "post" } } ]}Response
Section titled “Response”201 — one entry per post created:
[ { "postId": "cm4post0001", "integration": "your-instagram-id" }]Errors
Section titled “Errors”| Status | When |
|---|---|
400 |
A field is malformed, a channel id is unknown, or the platform refuses the post. A platform refusal carries provider, name (the channel) and error (what to fix) |
413 |
Body over 50 MB: media was inlined instead of uploaded first |
429 |
The hourly limit |
A post that names an automation in its
Instagram settings is checked at creation too: a missing variable, or
keywords sent without automationFlowId, are a 400.
List posts
Section titled “List posts”GET /posts?startDate=2026-10-01T00:00:00.000Z&endDate=2026-10-31T23:59:59.000Z| Parameter | Required | Description |
|---|---|---|
startDate |
Yes | UTC ISO 8601 |
endDate |
Yes | UTC ISO 8601 |
integration |
No | Only this channel |
group |
No | Only the channels of this brand (see Brands and scope) |
customer |
No | Legacy: the Customer model of the original project, which the app no longer writes |
Response 200, { "posts": [ … ] }, one row per post (comments are not
listed; a repeating post appears once per occurrence, each with actualDate):
| Field | Type | Description |
|---|---|---|
id |
string |
|
content |
string |
The text, HTML |
publishDate |
string |
UTC |
state |
string |
QUEUE, PUBLISHED, ERROR, DRAFT, or SENT_UNCONFIRMED (a TikTok post sent to the app inbox, not yet published by hand) |
releaseURL |
string | null |
URL of the published post |
releaseId |
string | null |
Provider id of the published post; missing when the provider returned none |
group |
string |
What the delete routes take |
intervalInDays |
number | null |
For a repeating post |
creationMethod |
string |
Where it came from (API, CLI…) |
tags |
[{ tag }] |
Each tag is the tag record (name, color) |
integration |
object |
id, providerIdentifier, name, picture |
There is no settings in the list. The settings of a post are not readable
over the API today.
Change the status
Section titled “Change the status”PUT /posts/{id}/status{ "status": "schedule" }schedule sets the state to QUEUE and (re)starts the publishing workflow
at the stored date; draft sets it to DRAFT and stops the workflow. The date
does not change. Response 200:
{ "id": "cm4post0001", "state": "QUEUE" }An unknown post is a 400 (Post not found).
Repair a missing release id
Section titled “Repair a missing release id”Sometimes a post publishes but the platform returns no usable id: the post then
has releaseId: "missing", and its analytics cannot be read. Two routes repair it.
GET /posts/{id}/missingLists the account’s recent items on the provider, so you can recognise the
right one. Response 200:
[ { "id": "7321456789012345678", "url": "https://p16-sign.tiktokcdn-us.com/obj/cover.jpeg" }]The answer is [] when the post is not in that state, or when the provider
cannot list its items: today only TikTok can.
PUT /posts/{id}/release-id{ "releaseId": "7321456789012345678" }Records the id against the post. A TikTok post in SENT_UNCONFIRMED becomes
PUBLISHED, and the content card tied to
its group moves with it. Response 200: the post row.
Delete
Section titled “Delete”DELETE /posts/{id}DELETE /posts/group/{group}Both delete the whole group: the post named, every channel it was published
with, and its comments. Any running publishing workflow is stopped. Response
200:
{ "id": "cm4post0001" }A 404 on a delete means the post was already gone; it is safe to ignore.