Saltar al contenido principal

Listas de publicaciones

Una lista de publicaciones es la tercera forma en que llegan los datos: alguien los escribe, en lugar de que un feed los introduzca. La lista es un nombre; las publicaciones que contiene eligen cada una su propia plantilla, contienen el contenido escrito en las entradas de esa plantilla e indican cuándo salen. En ese momento una publicación se convierte en un evento como cualquier otro y sigue el mismo camino, en cada canal que recibe la lista.

La API las llama queues, y una publicación en una es un package. En el panel están en el Editor de publicaciones, en su tarjeta Listas de publicaciones manuales, donde las palabras son lista de publicaciones y publicación; el área de permisos es Editor de publicaciones (postlists).

MétodoRutaPermiso
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

Crear una lista​

Una lista es un nombre y nada más. Sus publicaciones se añaden de una en una.

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": [],
"brands": []
}
}

201. name tiene de 1 a 80 caracteres y debe estar libre en el espacio de trabajo; una repetición es un 409. PATCH /v1/queues/{id} admite name y enabled, y responde con la lista como queue.

Una lista desactivada (enabled: false) no retiene nada: una publicación que vence mientras está desactivada sale igualmente, no llega a ningún canal y no se vuelve a enviar más tarde. Desactívela para detener una lista, no para ponerla en pausa; para retener una publicación, cambie su hora.

Un espacio de trabajo tiene hasta 50 listas y 5000 publicaciones entre todas, salvo que Reelwire lo haya fijado de otra manera; por encima, un 409 nombra el número. Las publicaciones que ya salieron cuentan hasta que se eliminan.

Programar una publicación​

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_39bc5fecfa42",
"templateVariant": "Corporate glass",
"scheduledFor": "2026-09-25T08:00:00Z",
"data": {
"clipData": {
"title": "Weekly market wrap",
"items": [
{ "text": "Bund yields up", "detail": "The ten-year closed at 2.41 per cent" },
{ "text": "Euro steady" },
{ "text": "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_39bc5fecfa42",
"templateVariant": "Corporate glass",
"templateVersion": null,
"data": {
"clipData": {
"items": [
{ "text": "Bund yields up", "detail": "The ten-year closed at 2.41 per cent" },
{ "text": "Euro steady" },
{ "text": "Oil lower" }
],
"title": "Weekly market wrap",
"source": "Orchid Invest"
},
"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,
"waitingForPicturesSince": null,
"waitingFor": [],
"outcome": null
}
}

El cuerpo:

CampoObligatorioQué es
titlesíDe 1 a 120 caracteres. El nombre de la publicación en la lista, no el título propio de la publicación, que es metaData.title.
templateIdsíLa plantilla con la que se renderiza esta publicación, una que se ofrezca a este espacio de trabajo. Cualquier otra es un 404.
templateVariantnoQué corte, por su nombre, como Corporate glass. Si falta, es el primero de la plantilla. Un corte que la plantilla no tiene es un 400 que nombra los que tiene.
templateVersionnoUna major.minor.patch exacta, o "latest". Ausente y "latest" guardan ambos null, que sigue la compilación más reciente cuando sale la publicación. Una versión que no existe es un 400. Por ahora todas las plantillas están en 1.0.0.
scheduledForsíUn instante ISO, o la cadena "now".
datasíEl contenido, en la forma de entrada propia de la plantilla.

201. Aquí no hay mapa de campos. Una publicación se escribe directamente en las entradas de la plantilla, así que no hay nada desde lo que asignar: pregunte a GET /v1/formats/template/{templateId} cuáles son, con ?variant= para una plantilla cuyas variantes toman cada una su propio contenido. El ejemplo es Lista, cuyo clip es un title y hasta doce items, cada uno un text con un detail opcional. Una imagen en data es un archivo de la biblioteca multimedia, { "url": "media://...", "alt": "..." }; una dirección web se rechaza. Consulte Enviar eventos para ver qué va en clipData y metaData.

Las copias de idioma viajan dentro de data, bajo un bloque locales que comprueba la plantilla, no en un campo locales de la solicitud. La respuesta siempre muestra locales: null; esa columna ya no se escribe a partir de una solicitud.

El contenido se comprueba al guardar, no cuando sale​

El mismo validador que protege un stream comprueba lo que envía, contra la compilación con la que se renderizaría realmente la publicación, en el momento de guardarla. Una publicación que se guarda es una publicación que se renderiza.

curl http://localhost:4000/v1/queues/01M2SQRG1HVVSB4P1D04WT0T8Z/packages \
-H "Authorization: Bearer rw_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"title": "Missing its points",
"templateId": "tpl_39bc5fecfa42",
"scheduledFor": "now",
"data": { "clipData": { "title": "Missing its points" } }
}'
{
"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 items" }]
}
}

El fallo llega a la persona que puede solucionarlo, en lugar de a un worker a las seis de un domingo. Cada detalle nombra un campo y lo que le pasa: falta, no es uno que admita esta plantilla, supera su límite o no es uno de los valores que acepta. Se comprueba contra la variante propia de la publicación, ya que las variantes de una plantilla sectorial (Finance, Influencer, Retail, Tech, Public bodies) toman cada una su propio contenido. La comprobación mira la forma de una imagen, no la biblioteca: una referencia media:// a un archivo que la biblioteca no tiene se guarda y se renderiza como imagen ausente. POST /v1/templates/{templateId}/validate también las nombra, antes de guardar.

Se rechaza una hora en el pasado​

Una hora que pasó hace más de un minuto es un error, no una instrucción:

{
"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": []
}
}

Envíe "scheduledFor": "now" cuando quiera decir de inmediato.

Leer una lista de vuelta​

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", "locale": "en", "brandId": "01M1VFRWDZ6KBJ1T4Q3V5N8X2C", "brandName": "Brand 1" },
{ "channelId": "01M2985NXBN97TVSF6AN19TDN2", "channelName": "Channel 7 en", "locale": "en", "brandId": "01M1VFRWDZ6KBJ1T4Q3V5N8X2C", "brandName": "Brand 1" },
{ "channelId": "01M2985NXSH4KRQHCP6WJ2KMFR", "channelName": "Channel 13 en", "locale": "en", "brandId": "01M1VFRWDZ6KBJ1T4Q3V5N8X2C", "brandName": "Brand 1" }
],
"brands": [
{
"id": "01M1VFRWDZ6KBJ1T4Q3V5N8X2C",
"name": "Brand 1",
"text": {
"description": "Trading involves risk.",
"link": { "label": "Read more", "url": "https://example.com/more" },
"hashtags": ["markets"]
},
"locales": { "de": { "description": "Handel birgt Risiken.", "hashtags": ["maerkte"] } }
}
]
}
]
}

Las listas vienen por orden de nombre, y las publicaciones de cada una (posts, con la forma de arriba) por la hora a la que salen. listeners es cada canal que recibe la lista, con su idioma (locale) y su marca: esos idiomas son aquellos para los que merece la pena escribir los locales de una publicación, ya que cada canal renderiza la copia de su propio idioma superpuesta a la predeterminada. brands es cada marca de los canales que reciben, una vez, con lo que añade después del texto propio de cada publicación (text: su description, link y hashtags), y lo mismo en cada idioma en el que la marca escribió su propio texto de publicación (locales, por código de idioma).

El state de una publicación es SCHEDULED hasta su hora, luego FIRED; CANCELLED una vez cancelada, y FAILED, con el motivo en error, cuando no pudo salir en absoluto, por ejemplo porque una plantilla se quedó sin compilación con la que renderizar.

Una publicación cuyo contenido nombra una imagen (media://) que la biblioteca multimedia no tiene nunca sale sin ella: cuando llega su hora permanece SCHEDULED, waitingForPicturesSince indica desde cuándo, waitingFor lista los nombres de archivo, y sale en cuanto todos están en la biblioteca, comprobado aproximadamente una vez por minuto. Ambos son null y [] para una publicación que no espera. Se puede escribir una publicación que nombre una imagen que aún no existe, de modo que se pueda hacer un plan antes de tomar las fotos; a quien publica se le envía un correo unos días antes de que venza mientras falte una imagen. Cambiar su data o scheduledFor la reinicia.

Cada publicación lleva un outcome una vez que ha salido, contado sobre los canales a los que llegó: channels, rendering, rendered, renderFailed, published, handedOver (entregada por correo o webhook, y nadie ha informado aún de que se publicó), emailed (el mismo número, con el nombre que tenía mientras el correo era la única vía), awaitingApproval y publishFailed.

outcome es null para una publicación que aún no ha salido. Una publicación sale tanto si alguien recibe como si no, así que «enviada» por sí solo no puede decir si se creó un video: esto sí.

Cambiar y cancelar​

PATCH /v1/queues/{id}/packages/{packageId} acepta title, scheduledFor, templateId, templateVariant, templateVersion, data y state, donde state es "CANCELLED" (cualquier otro estado es un 400). Responde con la publicación como package. Una plantilla, corte, compilación o contenido nuevos se comprueban de nuevo en su conjunto, porque un contenido que encajaba en la plantilla antigua puede no encajar en la nueva.

Dos cosas que conviene saber. Solo se puede cambiar una publicación que sigue en SCHEDULED; una publicación que salió es el registro de algo que ocurrió, y editarla reescribiría la historia. Y cambiar la hora vuelve a validar el contenido, porque una plantilla que imprime cuándo ocurrió el hecho lo lee del contenido, donde se escribe para ella la hora a la que sale una publicación (occurredAt, solo para una plantilla que admite una).

POST /v1/queues/{id}/packages/{packageId}/delete elimina una publicación de la lista y responde { "ok": true }.

POST /v1/queues/{id}/delete elimina la lista y todas sus publicaciones, y da de baja a los canales que la recibían, junto con los trabajos de publicación que hicieron a partir de ella y sus clips. La respuesta indica cuántos canales:

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

Conseguir que se publique una lista​

Crear una lista de publicaciones no publica nada por sí solo. Un canal tiene que suscribirse a ella, con kind: "QUEUE"; consulte Canales. Una publicación sale tanto si alguien recibe como si no, y una que nadie recibió no produjo nada: su outcome dice channels: 0. El canal no fija nada para una lista: cada publicación elige su propia plantilla, corte y compilación. Si una publicación espera entonces a que alguien la apruebe depende del ajuste de aprobación propio del canal, como con cualquier otra fuente.

Lo que dice cada publicación junto a su clip es lo que se escribió en sus metaData: el título y el texto con la longitud que encaja con la plataforma de cada canal que recibe (las cuatro longitudes y qué plataformas toman cuál están en Plantillas y formatos). Escriba las longitudes que necesitan sus canales; una omitida se rellena a partir de la siguiente más corta escrita o, si no, de la siguiente más larga. No se escribe nada a espaldas del autor en una publicación manual: un texto o unos hashtags dejados vacíos salen vacíos. Solo se rellena un título vacío, a partir del título propio del clip (su clipData title y label, o headline) o del nombre de la lista donde no hay ninguno, porque algunas plataformas no aceptan un video sin uno.

Crear una publicación de nuevo​

GET /v1/jobs/{id}/reuse responde con aquello de lo que se hizo una publicación terminada. Envíe su templateId, variant como templateVariant, pinnedVersion como templateVersion y data a POST /v1/queues/{id}/packages, con un title y un scheduledFor, y la publicación sale de nuevo con lo que haya cambiado; para una publicación que vino de una lista, queueId nombra la lista. En el panel, Reutilizar en la pantalla Publicaciones, Como nueva publicación, abre Nueva publicación en el Editor de publicaciones con todo rellenado.

Planificar las próximas semanas​

GET /v1/queues/{id}/plan responde cuándo pueden salir las publicaciones de las próximas semanas de esta lista y cuántas caben: sobre lo que se construye el plan de contenido de un asistente (reelwire_plan_posts). No lee ni cambia nada más, y nunca dice qué debe decir una publicación.

ConsultaQué es
weeksCuántas semanas hacia delante, de 1 a 13. Por defecto 12.
fromCuándo empieza el plan, ISO 8601. Por defecto ahora.
timeZoneDónde vive la audiencia, una zona IANA como Europe/Berlin. Por defecto, una suposición a partir del idioma del primer canal que recibe, respondida con timeZoneAssumed: true.
postsPerWeekDe 1 a 21. Por defecto lo que paga la cuota, como máximo tres.
daysDías de la semana, Mon,Wed,Fri o 1,3,5, en lugar de repartir postsPerWeek de forma uniforme.
timesHoras locales, HH:MM entre 06:00 y 20:00, como máximo tres. Por defecto 09:00.

Cada hueco cumple tres reglas. La cuota, período a período: una publicación se renderiza una vez por cada canal que recibe la lista, y lo que le queda a un período son sus videos menos los usados, menos lo que los canales ya tienen en camino (las entradas del Calendario de publicaciones, feeds y listas por igual), menos lo que añade el plan. La rampa, por canal y día local: como máximo una publicación al día en la primera semana de un canal, dos en las semanas dos y tres, tres desde la semana cuatro, contadas desde su primera publicación. La luz del día: cada hora está entre las 06:00 y las 20:00 donde vive la audiencia. Un hueco que incumple una está en dropped, con why (allowance o ramp).

{
"queue": { "id": "01M2SQRG1HVVSB4P1D04WT0T8Z", "name": "Default post list", "enabled": true },
"window": { "from": "2026-10-26T00:00:00.000Z", "to": "2027-01-18T00:00:00.000Z", "weeks": 12 },
"timeZone": "Europe/Berlin",
"timeZoneAssumed": false,
"rendersPerPost": 1,
"postsPerWeek": 3,
"slots": [
{ "at": "2026-10-26T08:00:00.000Z", "local": "Mon 2026-10-26 09:00", "week": 1 },
{ "at": "2026-10-28T08:00:00.000Z", "local": "Wed 2026-10-28 09:00", "week": 1 }
],
"dropped": [],
"droppedCount": { "allowance": 0, "ramp": 0 },
"periods": [
{ "start": "2026-10-12T09:30:00.000Z", "end": "2026-11-12T09:30:00.000Z", "allowance": 15, "used": 2, "booked": 0, "planned": 5, "left": 8, "morePosts": 8 }
],
"channels": [
{ "id": "01M1W0W9Q1ZB5Y8WQF8R8C3V7K", "name": "Default channel", "locale": "de", "firstPostAt": null, "weekAtStart": 1, "postsPerDayAtStart": 1 }
]
}

Escribir un plan completo de una vez​

POST /v1/queues/{id}/packages/batch admite posts, una lista de hasta 100, cada una con los campos de una publicación individual (title, templateId, templateVariant, templateVersion, scheduledFor, data). Cada publicación se comprueba exactamente como una sola, y no se escribe nada salvo que quepan todas: un rechazo es un 400 cuyos details nombran por su número cada publicación que no encaja, /posts/3/clipData/title. La respuesta es packages, las publicaciones escritas, por orden de hora, con un 201.

Errores frecuentes​

Esperar un fallo de validación en el momento de salir. No lo hay. El contenido se comprueba al guardarlo, contra la compilación con la que se renderizaría.

Programar en el pasado para publicar ahora. Se rechaza. Use "now".

Enviar locales junto a data. Las copias de idioma van dentro de data, donde la plantilla las valida.

Editar una publicación que ya salió. Un 409. Cree una publicación nueva.

Fijar templateVersion y olvidarlo. Una fijación a una compilación exacta deja de seguir las nuevas. Envíe "latest", o déjelo sin indicar, salvo que tenga un motivo.

Eliminar una lista para ordenar. Todos los canales que la reciben pierden esa suscripción, y los trabajos de publicación que creó se van con ella. Compruebe primero listeners, o establezca enabled: false, que conserva las suscripciones.

Desactivar una lista para ponerla en pausa. Una publicación que vence mientras la lista está desactivada sale hacia nadie y no se envía más tarde. Mueva en su lugar la hora de la publicación, o cancélela.