Skip to main content

Post lists

A post list is the third way data arrives: somebody writes it, rather than a feed pushing it. The list is a name; the posts on it each choose their own template, hold the content typed into that template's inputs, and say when they go out. At that moment a post becomes an event like any other and takes the same path, on every channel listening to the list.

The API calls these queues, and a post on one is a package. The words on the screens are post list and post.

MethodPathPermission
GET/v1/queuespostlists:read
POST/v1/queuespostlists:write
PATCH/v1/queues/{id}postlists:write
POST/v1/queues/{id}/deletepostlists:write
POST/v1/queues/{id}/packagespostlists:write
PATCH/v1/queues/{id}/packages/{packageId}postlists:write
POST/v1/queues/{id}/packages/{packageId}/deletepostlists:write

Create a list​

A list is a name, and nothing else. Its posts are added one at a time.

curl http://localhost:4000/v1/queues \
-H "Authorization: Bearer rw_your_key_here" \
-H "Content-Type: application/json" \
-d '{"name":"Docs examples list"}'
{
"queue": {
"id": "01M2SQRG1HVVSB4P1D04WT0T8Z",
"name": "Docs examples list",
"enabled": true,
"posts": [],
"subscribers": 0,
"listeners": []
}
}

201. name is 1 to 80 characters and must be free within the workspace; a repeat is a 409. PATCH /v1/queues/{id} takes name and enabled.

Schedule a post​

curl http://localhost:4000/v1/queues/01M2SQRG1HVVSB4P1D04WT0T8Z/packages \
-H "Authorization: Bearer rw_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"title": "Weekly market wrap",
"templateId": "tpl_a9c3fdb1b5c9",
"templateVariant": "Standard",
"scheduledFor": "2026-09-25T08:00:00Z",
"data": {
"clipData": {
"headline": "Weekly market wrap",
"subline": "Three things that moved the week",
"bullets": ["Bund yields up", "Euro steady", "Oil lower"],
"source": "Orchid Invest"
},
"metaData": {
"title": "Weekly market wrap",
"captionShort": "Three things that moved the week.",
"hashtags": ["markets"]
}
}
}'
{
"package": {
"id": "01M2SQRR3PB0MQ2M2GP9A0ZT82",
"title": "Weekly market wrap",
"templateId": "tpl_a9c3fdb1b5c9",
"templateVariant": "Standard",
"templateVersion": null,
"data": {
"clipData": {
"source": "Orchid Invest",
"bullets": ["Bund yields up", "Euro steady", "Oil lower"],
"subline": "Three things that moved the week",
"headline": "Weekly market wrap"
},
"metaData": {
"title": "Weekly market wrap",
"hashtags": ["markets"],
"captionShort": "Three things that moved the week."
}
},
"locales": null,
"scheduledFor": "2026-09-25T08:00:00.000Z",
"state": "SCHEDULED",
"firedAt": null,
"ingestEventId": null,
"error": null,
"outcome": null
}
}

The body:

FieldRequiredWhat it is
titleyes1 to 120 characters. A label for the list, not the post's own title.
templateIdyesThe template this post is drawn with, one this workspace is offered. Any other is a 404.
templateVariantnoWhich cut. Absent is the template's first.
templateVersionnoAn exact major.minor.patch, or "latest". Absent and "latest" both store null, which follows the newest build when the post fires. A version that does not exist is refused.
scheduledForyesAn ISO instant, or the string "now".
datayesThe content, in the template's own input shape.

201. There is no field map here. A post is written straight into the template's inputs, so there is nothing to map from. A picture in data is a file from the media library, { "url": "media://...", "alt": "..." }; a web address is refused. See Sending events for what goes in clipData and metaData.

Language copies travel inside data, under a locales block that the template checks, not in a locales field of the request. The response always shows locales: null; that column is no longer written from a request.

Content is checked when you save, not when it fires​

The same validator that guards a stream checks what you send, against the build the post would actually draw with, at the moment you save it. A post that saves is a post that renders.

curl http://localhost:4000/v1/queues/01M2SQRG1HVVSB4P1D04WT0T8Z/packages \
-H "Authorization: Bearer rw_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"title": "Missing headline",
"templateId": "tpl_a9c3fdb1b5c9",
"scheduledFor": "now",
"data": { "clipData": { "subline": "only a subline" } }
}'
{
"error": {
"type": "https://reelwire.io/errors/schema_validation",
"code": "schema_validation",
"message": "The post does not fit the template",
"requestId": "01M2SQS36D5F7W5S7WSZPHMADF",
"details": [{ "path": "/clipData", "message": "missing headline" }]
}
}

The failure lands with the person who can fix it, rather than in a worker at six on a Sunday.

A time in the past is refused​

A time more than a minute gone is a mistake, not an instruction:

{
"error": {
"type": "https://reelwire.io/errors/schema_validation",
"code": "schema_validation",
"message": "That time has already passed. Pick Right away, or a time to come.",
"requestId": "01M2SQS34CCN1K6S6MZXKD631Y",
"details": []
}
}

Send "scheduledFor": "now" where you mean immediately.

Read a list back​

curl http://localhost:4000/v1/queues \
-H "Authorization: Bearer rw_your_key_here"
{
"queues": [
{
"id": "01M2985NYG2GJ450CQAF3DP5YK",
"name": "Enterprise 2 Post list 1",
"enabled": true,
"posts": [],
"subscribers": 3,
"listeners": [
{ "channelId": "01M1VFRWE7EWSY5D7QDQ0KNK1S", "channelName": "Channel 1 en", "brandName": "Brand 1" },
{ "channelId": "01M2985NXBN97TVSF6AN19TDN2", "channelName": "Channel 7 en", "brandName": "Brand 1" },
{ "channelId": "01M2985NXSH4KRQHCP6WJ2KMFR", "channelName": "Channel 13 en", "brandName": "Brand 1" }
]
}
]
}

Each post carries an outcome once it has fired, counted over the channels it reached: channels, rendering, rendered, renderFailed, published, emailed (sent by email to be posted by hand, and not yet marked published), awaitingApproval and publishFailed.

outcome is null for a post still to go. A post fires whether or not anybody listens, so "sent" alone cannot say whether a video was made: this can.

Changing and cancelling​

PATCH /v1/queues/{id}/packages/{packageId} accepts title, scheduledFor, templateId, templateVariant, templateVersion, data, and state where state is "CANCELLED".

Two things to know. Only a post still in SCHEDULED can be changed; a post that went out is a record of something that happened, and editing it would rewrite history. And changing the time re-validates the content, because when a post fires is written into its content as when the fact happened.

POST /v1/queues/{id}/packages/{packageId}/delete removes a post from the list and answers { "ok": true }.

POST /v1/queues/{id}/delete removes the list and every post on it, and unsubscribes the channels that were listening, together with the posting jobs they made from it and their clips. The response says how many channels:

{ "ok": true, "removedFromChannels": 3 }

Getting a list published​

Making a post list does not publish anything on its own. A channel has to subscribe to it, with kind: "QUEUE"; see Channels. A post fires whether or not anybody listens, and one nobody listened to made nothing: its outcome says channels: 0.

What each post says beside its clip is what was written in its metaData: the title, and the caption in the length that fits each listening channel's platform. Nothing is written for a manual post behind the writer's back: a caption or hashtags left empty go out empty. Only an empty title is filled in from the headline, because some platforms will not take a video without one.

Mistakes people make​

Waiting for a validation failure at fire time. There is not one. The content is checked when you save it, against the build it would draw with.

Scheduling into the past to publish now. It is refused. Use "now".

Sending locales beside data. Language copies go inside data, where the template validates them.

Editing a post that already fired. A 409. Create a new post.

Pinning templateVersion and forgetting it. A pin to an exact build stops following new ones. Send "latest", or leave it out, unless you have a reason.

Deleting a list to tidy up. Every channel listening to it loses that subscription, and the posting jobs it made go with it. Check listeners first, or set enabled: false.