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.
| Method | Path | Permission |
|---|---|---|
GET | /v1/queues | postlists:read |
POST | /v1/queues | postlists:write |
PATCH | /v1/queues/{id} | postlists:write |
POST | /v1/queues/{id}/delete | postlists:write |
POST | /v1/queues/{id}/packages | postlists:write |
PATCH | /v1/queues/{id}/packages/{packageId} | postlists:write |
POST | /v1/queues/{id}/packages/{packageId}/delete | postlists: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": []
}
}
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:
| Field | Required | What it is |
|---|---|---|
title | yes | 1 to 120 characters. A label for the list, not the post's own title. |
templateId | yes | The template this post is drawn with. |
templateVariant | no | Which cut. Absent is the template's first. |
templateVersion | no | An exact major.minor.patch, or "latest". Absent and "latest" both store null, which follows the newest build when the post fires. |
scheduledFor | yes | An ISO instant, or the string "now". |
data | yes | The content, in the template's own input shape. |
There is no field map here. A post is written straight into the template's inputs, so there is nothing to map from.
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, 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.
POST /v1/queues/{id}/delete removes the list and every post on it, and unsubscribes the channels
that were listening. The response says how many:
{ "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 list with nobody listening has no lengths
to write text for, and its posts carry no text at all until somebody listens.
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. Check
listeners first, or set enabled: false.