Publicaciones y trabajos
Una publicación es un contenido en un espacio de trabajo: todo lo que produce un elemento de feed, un evento de un feed propio o una publicación de una lista de publicaciones, para cada canal que lo recibe. Cada canal de una publicación sigue teniendo su propio render, en su propio formato e idioma, su propia aprobación donde el canal la pide y su propio estado. La API llama trabajo (job) a un canal de una publicación, por eso las rutas dicen /v1/jobs: un trabajo es lo que usted observa y sobre lo que actúa (cancelarlo, cambiar su texto, pedir que se retire), y la publicación del canal dentro de él es el registro de lo que ocurrió en ese canal.
Reelwire crea el video. Cuando una publicación vence, se entrega por las vías que su canal tiene activadas, en cualquier combinación: por correo electrónico o por webhook, y quien la recibe la publica y después lo comunica; o a través del conector de plataforma del canal, que la publica por sí mismo en la plataforma y lo comunica (Canales). Un canal con todas las vías desactivadas entrega sus publicaciones solo a los participantes de sindicación cuyo grupo lo incluye. Consulte Webhooks para el webhook, y más abajo Publicaciones entregadas por correo electrónico para el correo y su kit de publicación, cuyas direcciones no necesitan clave y no figuran en la tabla.
| Método | Ruta | Permiso |
|---|---|---|
GET | /v1/jobs | posts:read |
POST | /v1/jobs/{id}/cancel | posts:write |
POST | /v1/jobs/{id}/repost | posts:write |
POST | /v1/jobs/{id}/update | posts:write |
POST | /v1/jobs/{id}/resend-email | posts:write |
POST | /v1/jobs/{id}/resend-webhook | posts:write |
POST | /v1/jobs/{id}/resend-connector | posts:write |
POST | /v1/jobs/{id}/resend-participants | posts:write |
POST | /v1/jobs/{id}/retry | posts:write |
POST | /v1/jobs/{id}/retry-render | posts:write |
POST | /v1/jobs/{id}/mark-published | posts:write |
POST | /v1/jobs/{id}/skip | posts:write |
POST | /v1/jobs/{id}/mark-deleted | posts:write |
POST | /v1/jobs/{id}/archive | posts:write |
POST | /v1/jobs/{id}/unarchive | posts:write |
POST | /v1/jobs/{id}/delete | posts:write |
GET | /v1/jobs/{id}/download | posts:read |
GET | /v1/jobs/{id}/reuse | posts:read |
GET | /v1/posts | posts:read |
POST | /v1/posts/{id}/report | posts:write |
GET | /v1/posts/{id}/package | posts:read |
GET | /v1/pipeline | posts:read |
GET | /v1/pipeline/{id} | posts:read |
GET | /v1/overview | posts:read |
GET | /v1/calendar | schedule:read |
GET | /v1/calendar/still | schedule:read |
GET | /v1/calendar/export | schedule:read |
GET | /v1/calendar/export/link | schedule:read |
GET | /v1/approvals | approvals:read |
POST | /v1/approvals/{id} | approvals:write |
Listar publicaciones
GET /v1/jobs lista las publicaciones: una fila por publicación, con sus canales dentro. Pagina, busca y filtra como todo registro; consulte Listas y paginación. Con by=channel da en su lugar una fila por canal, que es lo que muestra la pantalla Publicaciones: consulte Filtros.
curl "http://localhost:4000/v1/jobs?perPage=1" \
-H "Authorization: Bearer rw_your_key_here"
Esta es la publicación del ejemplo completo de Enviar eventos, para dos canales: Channel 4 de se entregó por webhook y se informó como publicada, y Channel 10 en está a la espera de un revisor.
{
"jobs": [
{
"id": "01M2SQN507TDA0KNTJJABHBR9A",
"postKey": "01M2SQN507TDA0KNTJJABHBR9A",
"status": "AWAITING_APPROVAL",
"summary": "1 of 2 published",
"progress": 0,
"priority": false,
"createdAt": "2026-09-18T07:45:50.099Z",
"updatedAt": "2026-09-18T07:47:02.316Z",
"origin": "custom",
"details": "Enterprise 2 Stream 1",
"postTitle": null,
"brandName": "Brand 1",
"channelCount": 2,
"channelNames": ["Channel 4 de", "Channel 10 en"],
"caption": "The ten-year settled at 2.41 per cent.",
"title": "Bund yields close at a three-week high",
"email": "off",
"webhook": "partly",
"connector": "off",
"approvalId": "01M2SQPASCPHZ65QRPX9X628GD",
"approval": { "required": true, "state": "PENDING", "login": null, "name": null, "waiting": 1 },
"downloadable": true,
"archived": false,
"channels": [
{
"id": "01M2SQN50JQFC00PZE93M9E33R",
"postId": "01M2SQPANAT1W066PMYNXFTCNZ",
"status": "PUBLISHED",
"state": "PUBLISHED",
"progress": 1,
"createdAt": "2026-09-18T07:45:50.099Z",
"updatedAt": "2026-09-18T07:47:02.316Z",
"origin": "custom",
"details": "Enterprise 2 Stream 1",
"postTitle": null,
"channelId": "01M2985NX4V0WA1GV9MC63DEPJ",
"channelName": "Channel 4 de",
"brandName": "Brand 1",
"platform": "instagram",
"surfaceLabel": "Instagram Reels",
"ratio": "9x16",
"locale": "de",
"caption": "The ten-year settled at 2.41 per cent.",
"title": "Bund yields close at a three-week high",
"publishedAt": "2026-09-18T07:47:02.310Z",
"permalink": "https://www.instagram.com/reel/C9x2Lm4oQpR/",
"scheduledFor": "2026-09-18T07:46:28.711Z",
"error": null,
"templateName": "Big number",
"templateVariant": "Corporate glass",
"templateVersion": "1.0.0",
"renderJobId": "01M2SQN51TGR960ZKWNVKH1HQJ",
"deliverByEmail": false,
"deliverByWebhook": true,
"deliverByConnector": false,
"syndicated": false,
"email": "off",
"webhook": "sent",
"connector": "off",
"handedOver": true,
"deleteRequested": false,
"updatable": true,
"resendable": true,
"retryable": false,
"renderRetryable": false,
"renderRetryCounts": false,
"markable": false,
"skippable": false,
"deletable": true,
"markDeletable": false,
"repostable": true,
"downloadable": true,
"priority": true,
"archived": false,
"approvalId": null,
"approval": { "required": false, "state": null, "login": null, "name": null },
"clip": {
"id": "01M2SQPAJYJR7YB27466FV30SZ",
"mimeType": "video/mp4",
"url": "http://localhost:4000/v1/assets/01M2SQPAJYJR7YB27466FV30SZ/download?exp=...&sig=..."
}
},
{
"id": "01M2SQN50TAP4BXAYDZRC9WMBC",
"postId": "01M2SQPARBQPG1BRENTHBM1W08",
"status": "AWAITING_APPROVAL",
"state": "AWAITING_APPROVAL",
"progress": 1,
"createdAt": "2026-09-18T07:45:50.106Z",
"updatedAt": "2026-09-18T07:46:28.781Z",
"origin": "custom",
"details": "Enterprise 2 Stream 1",
"postTitle": null,
"channelId": "01M2985NXJRXVE5HKCCGKASP8P",
"channelName": "Channel 10 en",
"brandName": "Brand 1",
"platform": "instagram",
"surfaceLabel": "Instagram Reels",
"ratio": "9x16",
"locale": "en",
"caption": "The ten-year settled at 2.41 per cent.",
"title": "Bund yields close at a three-week high",
"publishedAt": null,
"permalink": null,
"scheduledFor": null,
"error": null,
"templateName": "Big number",
"templateVariant": "Corporate glass",
"templateVersion": "1.0.0",
"renderJobId": "01M2SQN51XWQ3D8E0T4B6RZK2M",
"deliverByEmail": false,
"deliverByWebhook": true,
"deliverByConnector": false,
"syndicated": false,
"email": "off",
"webhook": "waiting",
"connector": "off",
"handedOver": false,
"deleteRequested": false,
"updatable": false,
"resendable": false,
"retryable": false,
"renderRetryable": false,
"renderRetryCounts": false,
"markable": false,
"skippable": false,
"deletable": false,
"markDeletable": false,
"repostable": false,
"downloadable": true,
"priority": true,
"archived": false,
"approvalId": "01M2SQPASCPHZ65QRPX9X628GD",
"approval": { "required": true, "state": "PENDING", "login": null, "name": null },
"clip": {
"id": "01M2SQPAPH4YF66ZKK5Y5XA9Y7",
"mimeType": "video/mp4",
"url": "http://localhost:4000/v1/assets/01M2SQPAPH4YF66ZKK5Y5XA9Y7/download?exp=...&sig=..."
}
}
]
}
],
"total": 5,
"all": 5,
"page": 1,
"perPage": 1,
"stages": {
"rendering": 0,
"approval": 1,
"scheduled": 0,
"publishing": 0,
"handedOver": 0,
"published": 4,
"skipped": 0,
"failed": 0,
"rejected": 0,
"cancelled": 0,
"deleted": 0
},
"unstaged": 5,
"facets": {
"stage": ["rendering", "approval", "scheduled", "publishing", "handedOver", "published", "skipped", "failed", "rejected", "cancelled", "deleted"],
"archive": ["archived"],
"approval": ["upcoming", "pending", "approved", "rejected", "none"],
"channelId": ["01M2985NXJRXVE5HKCCGKASP8P", "01M2985NX4V0WA1GV9MC63DEPJ"],
"brandId": ["01M1VFRWE7VHP3H54ESRJQ0SAJ"],
"platform": ["instagram"],
"origin": ["custom"]
},
"labels": {
"channelId": {
"01M2985NXJRXVE5HKCCGKASP8P": "Channel 10 en",
"01M2985NX4V0WA1GV9MC63DEPJ": "Channel 4 de"
},
"brandId": { "01M1VFRWE7VHP3H54ESRJQ0SAJ": "Brand 1" }
}
}
Dos ids, para dos cosas. El id de una fila es la publicación, su postKey: el id que lleva cada entrega de ella. El id de un canal en channels es lo que admite cada acción /v1/jobs/{id}/... de abajo y lo que lee /v1/pipeline/{id}. El postId de un canal es la publicación de su canal, el id con el que informa un receptor de webhook.
Lo que dice una fila sobre toda la publicación:
statuses el de sus canales que más necesita a alguien. Consulte El estado de una publicación.summarydice con palabras hasta dónde ha llegado una publicación de varios canales: «2 of 3 published», «1 of 3 handed over» o «3 channels». Esnullpara una publicación de un solo canal.email,webhookyconnectorindican cómo fue cada vía de salida sobre sus canales:off,waiting(aún no entregada),sending,retrying,sent,failedopartly, cuando sus canales salieron en momentos distintos, como cuando un canal con un revisor sigue a los demás por su cuenta. Un canal lleva los mismos valores para sí mismo, sinpartly.approvalIdes la revisión en espera donde espera exactamente un canal, yapproval.waitingcuántos canales esperan.captionytitleson los de su primer canal,downloadableindica si algún canal tiene un video que dar, yarchivedes true cuando todos sus canales se archivaron.
Lo que dice un canal sobre sí mismo:
statusse calcula a partir de su render y su publicación. Consulte Estados y etapas.statees el estado almacenado de su publicación, como lo tieneGET /v1/posts, onullantes de que haya una publicación.captionytitleson el texto propio de su publicación. Cuando el contenido trajo su texto enmetaData, el texto viene en cuatro longitudes,captionTiny(200 caracteres),captionShort(500),captionMedium(1500) ycaptionLong(4000), y la publicación de cada canal toma la más larga para la que su plataforma tiene espacio junto al texto propio de la marca; consulte Plantillas. Un canal en Sin plataforma específica conserva las cuatro: su kit de publicación y su descarga las muestran una por tipo de plataforma, y una actualización las cambia concaptions.deliverByEmail,deliverByWebhookydeliverByConnectorson los interruptores del canal tal como están ahora, yemail,webhookyconnectorlo que hizo cada vía por él:off,waiting,sending,retrying,sentofailed. Para el conector,sentes publicada en la plataforma.syndicatedes true mientras un grupo de sindicación con alguien registrado incluye el canal: sus publicaciones pasan entonces también a esos participantes, digan lo que digan los interruptores.handedOveres true una vez que se entregó, sea lo que sea que se haya dicho de ella desde entonces, ydeleteRequestedmientras se entregó una eliminación y nadie ha dicho que la publicación ya no está.publishedAtes cuándo se entregó, hasta que alguien informa de que se publicó (o la publicó el conector de plataforma); entonces es cuándo ocurrió eso.permalinkes el enlace a la publicación, cuando alguien informó de uno o la publicó el conector.approvalindica si necesita un revisor y en qué punto está:stateesPENDING,APPROVED,REJECTED,CANCELLED,UPCOMINGmientras un canal que pide aprobación aún renderiza el clip, onulldonde no se pregunta a nadie;loginynameindican quién decidió.kindespost, oupdatepara un cambio en una publicación ya entregada, que lleva elchangepedido;neededes cuántas aprobaciones hacen falta (2 donde un canal Enterprise pide dos aprobadores),firstquién dio la primera de dos mientras se espera la segunda, yrequestedByquién escribió la publicación o pidió el cambio, cada uno comologinyname.archivedes true una vez que alguien la archivó. Consulte Archivar y desarchivar.errores por qué se detuvo, donde lo hizo: el motivo del render o de la publicación.
Los indicadores le dicen qué acción aceptará un canal ahora mismo. Léalos en lugar de deducirlo del estado y no tendrá que gestionar un 409:
| Indicador | Acción | Verdadero mientras |
|---|---|---|
updatable | update | Se entregó (entregada, publicada u omitida), y no hay ningún cambio ni eliminación en camino. |
resendable | resend-email, resend-webhook, resend-connector, resend-participants | Está entregada, publicada u omitida, y no hay nada más en camino; cada una necesita además su vía activada en el canal, y resend-participants un grupo de sindicación con alguien registrado que la incluya (syndicated). |
retryable | retry | Falló después de crearse su clip (POST_FAILED), y el clip sigue guardado. |
renderRetryable | retry-render | Su clip no se pudo renderizar (RENDER_FAILED), y su canal no se eliminó. renderRetryCounts indica si el reintento cuesta un video. |
markable | mark-published | Está entregada u omitida, y nadie ha pedido retirarla. Una publicación fallida se intenta de nuevo con retry en su lugar. |
skippable | skip | Está entregada, y nadie ha dicho todavía nada sobre ella. |
deletable | delete | Está entregada o publicada, y no hay ningún cambio ni eliminación en camino. |
markDeletable | mark-deleted | Se entregó una eliminación y nadie ha dicho que la publicación ya no está; o ninguna vía consiguió que pasara una eliminación (DELETE_FAILED), para una publicación que alguien retiró a mano. |
repostable | repost | Se entregó una vez, y su canal está activo con Webhook activado. |
downloadable | download | Su video sigue guardado. |
stage, archive y approval en facets son las opciones fijas. El resto de facets, y labels, se leen de las filas que realmente tiene el espacio de trabajo, no de una lista fija, de modo que un canal, marca, plataforma u origen que aparece ahí es uno que puede devolver algo.
Filtros
| Parámetro | Valores |
|---|---|
stage | rendering, approval, scheduled, publishing, handedOver, published, skipped, failed, rejected, cancelled, deleted |
channelId | Un id de canal |
brandId | Un id de marca |
platform | El formato del canal: youtube, facebook, instagram, tiktok, linkedin, telegram, discord, slack, threema, x, wechat, signal, pinterest, threads, whatsapp o download, que es Sin plataforma específica |
origin | shipped, custom, manual |
approval | upcoming (un canal que pide aprobación, con su clip aún en render), pending, approved, rejected, none |
by | channel: una fila por canal de una publicación en lugar de una por publicación |
archive | archived: solo lo que alguien archivó. Sin él, solo lo que nadie archivó |
Una publicación aparece en la lista cuando coincide uno de sus canales, y entonces con todos sus canales.
Con by=channel, cada canal es una fila propia: channels contiene ese único canal, el id de la fila es el id del canal (el que admite cada acción de abajo), y postKey sigue nombrando su publicación. Los filtros, la búsqueda y los recuentos de etapa cuentan entonces canales. Cómo se entrega una publicación no cambia: los canales de una publicación que van juntos siguen yendo en un correo y una llamada de webhook.
Estados y etapas
El status de un canal se calcula, no se almacena: el estado de su render hasta que existe el clip, luego el de su publicación, con una actualización o eliminación en camino superpuesta. Una etapa es un conjunto de estados, no un estado:
| Etapa | Estados del canal |
|---|---|
rendering | ACCEPTED, AWAITING_RENDERING, RENDERING, DOWNLOADING |
approval | AWAITING_APPROVAL |
scheduled | SCHEDULED |
publishing | HANDING_OVER, UPDATING |
handedOver | HANDED_OVER |
published | PUBLISHED |
skipped | SKIPPED |
failed | RENDER_FAILED, POST_FAILED, UPDATE_FAILED, DELETE_FAILED |
rejected | REJECTED |
cancelled | CANCELLED, REPLACED |
deleted | DELETE_REQUESTED, DELETED |

publishing se está entregando ahora mismo, y handedOver está entregada y aún sin informe. La antigua etapa emailed, de cuando solo un correo podía entregar una publicación, sigue funcionando y significa handedOver.
HANDED_OVER no es publicada: significa que una vía de salida entregó la publicación y nadie ha dicho todavía si se publicó. PUBLISHED llega solo cuando alguien lo dice, o cuando la publicó el conector de plataforma del canal. SKIPPED es entregada y deliberadamente no publicada. POST_FAILED es una publicación que no se pudo entregar tras todos los intentos, o de la que quien la publica informó de que no se pudo publicar. DELETE_REQUESTED es una eliminación entregada que espera a que alguien diga que la publicación ya no está, y DELETED es cuando lo han dicho, o cuando el conector de plataforma la retiró.
Una publicación está en una etapa cuando lo está uno de sus canales, y cada etapa cuenta publicaciones, de modo que una publicación puede contar en dos etapas.
El período en el que cae una publicación es cuándo le ocurrió algo, no solo cuándo llegó su contenido: since y until coinciden con una publicación que llegó, se renderizó o cuyos canales cambiaron dentro de la ventana. Una publicación que llegó ayer y se publicó hace diez minutos está en la última hora.
El estado de una publicación
El status de una publicación es el de sus canales que más necesita a alguien: primero un fallo; luego el menos avanzado de los canales que siguen en marcha, en el orden ACCEPTED, AWAITING_RENDERING, RENDERING, DOWNLOADING, AWAITING_APPROVAL, SCHEDULED, HANDING_OVER, UPDATING, DELETE_REQUESTED, HANDED_OVER, que deja en último lugar al que espera la palabra de alguien; luego uno terminado, publicada antes que omitida, eliminada, rechazada y cancelada.
Una fila por canal
GET /v1/posts es el mismo registro al nivel de la publicación de un canal: una publicación en un canal, en un idioma y un formato. Filtra por state y platform.
curl "http://localhost:4000/v1/posts?perPage=1" \
-H "Authorization: Bearer rw_your_key_here"
{
"posts": [
{
"id": "01M2SQPANAT1W066PMYNXFTCNZ",
"postGroupId": "01M2SQN50JQFC00PZE93M9E33R",
"origin": "custom",
"details": "Enterprise 2 Stream 1",
"postTitle": null,
"state": "PUBLISHED",
"platform": "instagram",
"surfaceLabel": "Instagram Reels",
"channelName": "Channel 4 de",
"brandName": "Brand 1",
"locale": "de",
"ratio": "9x16",
"title": "Bund yields close at a three-week high",
"caption": "The ten-year settled at 2.41 per cent.",
"hashtags": ["bunds", "rates"],
"createdAt": "2026-09-18T07:46:28.705Z",
"scheduledFor": "2026-09-18T07:46:28.711Z",
"publishedAt": "2026-09-18T07:47:02.310Z",
"platformPostId": "17912345678901234",
"permalink": "https://www.instagram.com/reel/C9x2Lm4oQpR/",
"failureReason": null,
"connected": true,
"assets": [
{
"assetId": "01M2SQPAJYJR7YB27466FV30SZ",
"url": "http://localhost:4000/v1/assets/01M2SQPAJYJR7YB27466FV30SZ/download?exp=...&sig=...",
"expiresAt": "2026-09-18T08:50:22.000Z"
}
],
"clipStored": true
}
],
"total": 7,
"all": 7,
"page": 1,
"perPage": 1,
"facets": { "state": ["AWAITING_APPROVAL", "PUBLISHED"], "platform": ["instagram"] }
}
id es la publicación del canal, el postId con el que informa un receptor de webhook, y postGroupId es el id de su canal en GET /v1/jobs, que admiten las acciones. platformPostId y permalink son lo que informó quien la publicó, o lo que respondió el conector de plataforma al publicarla, y viajan con una actualización o eliminación posterior para poder volver a encontrar la publicación. Las URL de recursos están firmadas y caducan; expiresAt indica cuándo, así que genere una nueva en lugar de guardar la URL.
El state de la publicación de un canal es uno de DRAFT, PENDING_RENDER, RENDERED, AWAITING_APPROVAL, APPROVED, REJECTED, SCHEDULED, PUBLISHING (se está entregando), HANDED_OVER (una vía de salida la entregó, y nadie ha dicho todavía si se publicó), PUBLISHED (alguien dijo que está publicada, o la publicó el conector de plataforma), SKIPPED (entregada y deliberadamente no publicada), FAILED (no se pudo entregar, o quien la publica dijo que no se pudo publicar, y failureReason indica por qué), RECALLED (retirada, y alguien o el conector de plataforma lo dijo), REPLACED y CANCELLED (nunca saldrá: alguien la canceló, o su clip se eliminó para hacer sitio).
clipStored: false significa que el video se eliminó para hacer sitio en la cuota de almacenamiento del espacio de trabajo, o se borró en Clips renderizados. La publicación sigue siendo un registro de lo que salió; no queda ningún archivo que descargar. Es null para una publicación que nunca tuvo clip.
connected indica si una vía de salida propia del espacio de trabajo puede llevar la publicación: su canal tiene Correo activado, o la dirección del webhook del espacio de trabajo está definida y activada.
Seguir una publicación mientras avanza
GET /v1/pipeline es la vista en vivo: lo que se está renderizando, lo que se está entregando y lo que terminó recientemente. Le dice con qué frecuencia preguntar. since y until la limitan a publicaciones que llegaron en una ventana; sin ellos, los recuentos abarcan todo lo que tiene el espacio de trabajo.
curl http://localhost:4000/v1/pipeline \
-H "Authorization: Bearer rw_your_key_here"
{
"at": "2026-09-18T07:50:22.201Z",
"pollMs": 2000,
"render": { "counts": { "COMPLETED": 7 }, "inFlight": [], "recent": [] },
"posts": {
"counts": { "PUBLISHED": 7 },
"clipCounts": { "PUBLISHED": 7 },
"inFlight": [],
"recent": [
{
"id": "01M2SQN50TAP4BXAYDZRC9WMBC",
"kind": "postGroup",
"state": "PUBLISHED",
"templateId": "tpl_dd7e6b264fb8",
"brand": "Brand 1",
"channelName": "Channel 10 en",
"streamId": "st_30472168d941",
"source": "st_30472168d941",
"title": "Bund yields close at a three-week high",
"createdAt": "2026-09-18T07:46:28.748Z",
"stageSeconds": 4,
"stageSince": "2026-09-18T07:50:18.611Z",
"error": null,
"clips": [
{
"id": "01M2SQPARBQPG1BRENTHBM1W08",
"platform": "instagram",
"locale": "en",
"ratio": "9x16",
"state": "PUBLISHED",
"scheduledFor": "2026-09-18T07:50:17.386Z",
"publishedAt": "2026-09-18T07:50:18.608Z",
"permalink": "https://www.instagram.com/reel/C9x3Nq7pRtS/",
"error": null
}
]
}
]
},
"limit": 12
}
pollMs es el intervalo que sugiere el servidor. Respételo en lugar de elegir uno propio. Cada carril muestra como máximo limit canales, y counts los cubre todos, de modo que un carril puede decir «6, mostrando 4».
GET /v1/pipeline/{id} es todo lo que se sabe de un canal de una publicación, por el id del canal: de dónde vinieron sus datos, la marca, el canal, la fuente y la plantilla que lo renderizaron, el render, la aprobación y su publicación con cada entrega que la llevó, por correo, por webhook o por conector de plataforma. También lleva lo que da el kit de publicación: los textos para copiar, los enlaces del video y de la portada, y un prompt para un asistente de IA, mientras el video siga guardado.
GET /v1/overview es el recuento de todo, útil como panel de estado:
{
"brands": 3,
"channels": 15,
"subscriptions": 57,
"templates": { "mirrored": 14 },
"ingest": { "total": 1, "last24h": 1 },
"renderJobs": { "COMPLETED": 7 },
"assets": 7,
"posts": { "AWAITING_APPROVAL": 1, "PUBLISHED": 6 },
"attention": { "pendingApprovals": 1, "deadDeliveries": 0 }
}
Vigile attention en lugar de calcularlo usted: una entrega muerta es una entrega que nunca llegó, y una aprobación pendiente es una publicación que espera a una persona.
Aprobaciones
Un canal puede configurarse para retener cada publicación a la espera de un revisor. Esas publicaciones quedan en AWAITING_APPROVAL, renderizadas y listas, hasta que alguien decide. Lo mismo ocurre con una actualización de una publicación ya entregada, en un canal así (consulte Actualizar una publicación ya entregada). La aprobación viene con Small business y Enterprise: en los planes inferiores no se puede configurar un canal así (un 409) y a una clave no se le ofrece el permiso Posts - Approvals.
curl "http://localhost:4000/v1/approvals?perPage=3" \
-H "Authorization: Bearer rw_your_key_here"
{
"approvals": [
{
"id": "01M2SQPASCPHZ65QRPX9X628GD",
"requestedAt": "2026-09-18T07:46:28.781Z",
"channelName": "Channel 10 en",
"templateId": "tpl_dd7e6b264fb8",
"templateVersion": null,
"posts": [
{
"id": "01M2SQPARBQPG1BRENTHBM1W08",
"platform": "instagram",
"locale": "en",
"ratio": "9x16",
"caption": "The ten-year settled at 2.41 per cent.",
"state": "AWAITING_APPROVAL",
"assets": [
{
"assetId": "01M2SQPAPH4YF66ZKK5Y5XA9Y7",
"url": "http://localhost:4000/v1/assets/01M2SQPAPH4YF66ZKK5Y5XA9Y7/download?exp=...&sig=...",
"expiresAt": "2026-09-18T08:50:12.000Z"
}
]
}
]
}
],
"total": 1,
"all": 1,
"page": 1,
"perPage": 3,
"facets": {}
}
La lista contiene lo que espera ahora, lo más antiguo primero, porque una cola se trabaja desde el principio. templateVersion es null cuando el canal sigue la compilación más reciente. Cada solicitud indica además:
kind:POST, una publicación que espera a ser entregada, oUPDATE, un cambio en una publicación ya entregada. UnUPDATEllevachange, la actualización tal como se pidió (los mismos campos que el cuerpo de la actualización), de modo que un revisor aprueba las propias palabras.requestedBy: quién escribió la publicación o pidió el cambio, una dirección de inicio de sesión o el id de una clave de API.needed: cuántas aprobaciones hacen falta,1, o2donde el canal pide dos aprobadores (Enterprise).firstBynombra quién dio la primera de dos mientras espera la segunda, y esnullen los demás casos.
Decídala con POST /v1/approvals/{id}. El cuerpo admite state, que es APPROVED o REJECTED y por defecto APPROVED, y una note opcional.
curl http://localhost:4000/v1/approvals/01M2SQPASCPHZ65QRPX9X628GD \
-H "Authorization: Bearer rw_your_key_here" \
-H "Content-Type: application/json" \
-d '{"state":"APPROVED","note":"Figures checked against the agency release."}'
{ "postIds": ["01M2SQPARBQPG1BRENTHBM1W08"], "state": "APPROVED" }
Una aprobación que no es de este espacio de trabajo es un 404, y una ya decidida, un 409.
Aprobar un UPDATE entrega el cambio, por las vías pedidas, como lo habría hecho la persona que lo pidió; rechazarlo deja la publicación como salió. Cuando la publicación ha cambiado desde entonces de modo que el cambio ya no se puede hacer, aprobarlo es un 409 que indica por qué, y sigue esperando.
En Enterprise se cumplen dos reglas (expuestas en la página Cumplimiento). Nadie aprueba una publicación o actualización que escribió él mismo: aprobar una cuyo requestedBy es usted es un 403. Un canal que pide dos aprobadores toma a dos personas distintas: la primera aprobación responde
{ "postIds": [], "state": "PENDING", "approvals": 1, "needed": 2 }
y la solicitud espera, con firstBy establecido, a otra persona; que la misma persona apruebe de nuevo es un 403. Rechazar no está sujeto a ninguna de las dos reglas: cualquiera que pueda aprobar detiene una publicación, su autor incluido, en cualquiera de los dos pasos. Cada decisión es una entrada del registro de cumplimiento.
Un canal aprobado se entrega por su cuenta, en cuanto vence: no espera a los demás canales de su publicación, y ellos nunca lo esperaron a él. Un rechazo es definitivo: la publicación de un canal rechazado nunca se entrega ni se reintenta. Aprobar necesita approvals:write; es un permiso distinto de posts:write a propósito, porque la persona que da el visto bueno a una publicación a menudo no es la que puede editarla. La conexión de un asistente de IA lo tiene solo cuando la persona que conecta lo activó también; consulte La conexión de un asistente.
Actuar sobre un canal
Cada acción admite el id de un canal, channels[].id en GET /v1/jobs, y actúa solo sobre la publicación de ese canal: los demás canales de la publicación quedan intactos. Cada una responde con lo que hizo, y un 409 con una frase cuando el canal no está en un estado que la acepte. Un id que no es de una publicación de sus canales recibe el mismo 409 con una frase que lo dice, salvo en archive, unarchive y reuse, que responden 404.
Cancelar
POST /v1/jobs/{id}/cancel detiene todo lo que aún está por ocurrir a la publicación de un canal: un render que no ha terminado, una revisión aún no decidida, una entrega aún no enviada, incluida una que espera a reintentarse. Los demás canales de la publicación siguen sin él. No puede llegar al pasado: una vez entregada la publicación, no queda nada que cancelar.
curl -X POST http://localhost:4000/v1/jobs/01M2SQN50JQFC00PZE93M9E33R/cancel \
-H "Authorization: Bearer rw_your_key_here"
{
"error": {
"type": "https://reelwire.io/errors/conflict",
"code": "conflict",
"message": "There is nothing left to cancel: every post of this job has already finished.",
"requestId": "01M2SR0913DRD5SJWP0TR3JTBD",
"details": []
}
}
Eso es un 409, y el estado del canal es lo que le habría permitido saberlo de antemano. Dos momentos se rechazan con una petición de volver a intentarlo en breve: el segundo o dos en que el clip se copia de vuelta desde el renderizador, y el instante en que se está enviando su entrega.
Actualizar una publicación ya entregada
POST /v1/jobs/{id}/update cambia el texto de la publicación de un canal una vez entregada, publicada o no, y opcionalmente el video. Entrega el cambio por las vías que el canal tiene activadas (nunca una desactivada), o las de ellas que se nombren en ways. Su correo sale como «This post changed», con un kit de publicación que muestra el texto nuevo y, donde se sustituyó, el video nuevo, y el webhook recibe un post.update firmado: quien la publicó la cambia donde está. A través del conector de plataforma, Reelwire cambia el texto de la publicación que el propio conector publicó, donde la plataforma permite a una aplicación hacerlo; allí nunca sustituye un video, así que para cambiar el video en una plataforma así, envíe una solicitud de eliminación y publíquela de nuevo. La publicación toma el texto nuevo una vez que el cambio ha salido, y mientras tanto lee UPDATING. En un canal con todas las vías desactivadas, el cambio es de la propia publicación de inmediato, y aun así llega a sus participantes de sindicación.
curl http://localhost:4000/v1/jobs/01M2SQN50JQFC00PZE93M9E33R/update \
-H "Authorization: Bearer rw_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"description": "The ten-year settled at 2.41 per cent. Corrected: the high is a three-week high, not a three-month one.\n\n#bunds #rates"
}'
{ "ok": true, "queued": 1 }
El cuerpo:
| Campo | Qué es | Límite |
|---|---|---|
description | La descripción completa tal como debe leerse: las palabras propias de la publicación, la línea de la marca y los hashtags en un solo texto. | 5000 caracteres |
caption | El texto de la publicación por sí solo, cuando no envía description. | 4000 caracteres |
hashtags | Un array de etiquetas, con o sin #. | 60 etiquetas, de 60 caracteres cada una |
title | Cuando la plataforma tiene un campo de título propio. | 400 caracteres |
disclaimer | La línea propia de la marca. | 300 caracteres |
final | true cuando caption, hashtags y disclaimer ya contienen todo lo que debe decir la publicación. | |
link | { "url": "...", "label": "..." }, o una url vacía para ningún enlace. Si falta, se conserva el enlace que tiene la publicación. | url 500 caracteres, etiqueta 40 |
mediaFileId | Un video de la biblioteca multimedia que publicar en lugar del actual. Si falta, se conserva el video. Un archivo que no es un video, o que ya no está en la biblioteca, es un 409. | |
ways | Algunas de las vías que el canal tiene activadas: EMAIL, WEBHOOK, CONNECTOR. Si falta, son todas. Una vía que el canal tiene desactivada es un 409 que nombra su interruptor; una lista vacía o cualquier otra palabra es un 400. Los participantes de sindicación reciben el cambio sea lo que sea lo que se elija. | |
captions | Solo para un canal en Sin plataforma específica: { "tiny": "...", "short": "...", "medium": "...", "long": "..." }, un texto por tipo de plataforma. El mediano es también el texto propio de la publicación. | 200, 500, 1500 y 4000 caracteres |
Un texto que supera un límite se rechaza, nunca se recorta: un texto acortado por el camino es uno que nadie escribió. El rechazo es un 409 con una frase que nombra el límite.
description es la publicación tal como se leerá, y no se le añade nada de la marca. Con caption en su lugar, la descripción y los hashtags de la marca se añaden tal como estaban cuando la publicación salió por primera vez, salvo que envíe final: true: entonces los campos son la publicación tal como se leerá, y si envía hashtags sin uno que alguien eliminó, sigue eliminado.
Un cambio que ya está en camino es un 409 hasta que haya salido. También lo es una publicación que nunca se entregó: aún no hay nada ahí fuera que cambiar.
En un canal que pide aprobación, el cambio espera a un revisor. Se comprueba como se ha indicado, luego se conserva en una solicitud de aprobación propia, y aún no se entrega nada:
{ "ok": true, "queued": 0, "awaitingApproval": true, "approvalId": "01M3B6A2K9W4N8D1F5H7J3Q0RT" }
La solicitud está en GET /v1/approvals con kind UPDATE, y la approval del canal en GET /v1/jobs lee kind update. Una vez aprobado, el cambio se entrega por las vías nombradas aquí; rechazado, la publicación queda como salió. Consulte Aprobaciones. Solo espera una actualización a la vez: pedir otra mientras una espera es un 409. Una eliminación nunca se retiene.
Reutilizar
GET /v1/jobs/{id}/reuse responde con aquello de lo que se hizo una publicación, para crearla de nuevo con un cambio. No lee ni cambia nada más:
{
"reuse": {
"kind": "post",
"templateId": "tpl_dd7e6b264fb8",
"templateName": "Big number",
"variant": "Corporate glass",
"version": "1.0.0",
"pinnedVersion": null,
"brandId": "01M38Z6Y4T0WTFVAX5XET1S17P",
"ratio": "9x16",
"locale": "en",
"data": { "clipData": { "label": "Points the DAX gained this week", "value": 412 }, "locales": { "de": { "clipData": { "label": "Punkte, die der DAX diese Woche zulegte" } } } },
"title": "Copy of Weekly market wrap",
"queueId": "01M38Z6Y8H5G7M4Q2X8V1R0C9D"
}
}
data es el contenido en la forma propia de la plantilla, con imágenes como referencias media:// y locales para cada otro idioma. Una publicación de una lista de publicaciones responde con el contenido tal como se escribió, su pinnedVersion (null es Latest) y el queueId de su lista; una publicación de un feed o un stream responde con el contenido tal como se renderizó y los idiomas que traía el evento, y su queueId es null. Para publicarla de nuevo, envíe templateId, variant como templateVariant, pinnedVersion como templateVersion y data a POST /v1/queues/{id}/packages; para renderizar solo el video, envíe templateId, variant, version, data, brandId, ratio y locale tal cual a POST /v1/direct-export/video. Para un asistente de IA es reelwire_reuse, que lee un clip renderizado o una exportación directa de la misma manera.
Republicar
POST /v1/jobs/{id}/repost entrega de nuevo la publicación de un canal, ahora, como una publicación nueva propia con el mismo video y el mismo texto con los que se lee ahora la publicación. No se renderiza nada de nuevo, y el original queda como está. La nueva publicación sale por todas las vías que el canal tiene activadas, el conector de plataforma incluido, y la aprobación de un revisor del original se transfiere. El postGroupId de la respuesta es el id del canal de la nueva publicación.
Solo se puede republicar un canal con Webhook activado. Un canal sin él se envía de nuevo con su propio reenvío (consulte Enviar una publicación de nuevo), y republicar ahí es un 409 que lo dice. El canal tiene que estar activo: un canal en pausa o eliminado también es un 409 que lo dice.
Eliminar
POST /v1/jobs/{id}/delete pide que se retire la publicación de un canal. Entrega la solicitud por las vías que el canal tiene activadas. Su correo sale como «Please take this post down», nombrando dónde se publicó si alguien lo dijo, y el webhook recibe un post.delete firmado: quien la publicó la retira. A través del conector de plataforma, Reelwire retira la publicación que el propio conector publicó, donde la plataforma permite a una aplicación hacerlo, y la comunica como eliminada; donde la plataforma no lo permite, o el conector nunca la publicó, esa vía falla con un motivo que indica retirarla a mano. Un cuerpo { "ways": ["WEBHOOK"] } la envía solo por algunas de esas vías, como hace ways en una actualización: EMAIL, WEBHOOK y CONNECTOR, nunca una que el canal tenga desactivada (un 409); una lista vacía o cualquier otra palabra es un 400. Sin cuerpo sale por todas las vías que el canal tiene activadas, y los participantes de sindicación que tenían la publicación la reciben sea lo que sea lo que se elija.
El canal lee entonces DELETE_REQUESTED hasta que alguien dice que la publicación ya no está: mark-deleted más abajo, Marcar como eliminada en el kit de publicación o en la pantalla Publicaciones, el receptor que informa deleted, o el conector de plataforma que la retiró. Entonces lee DELETED. Una eliminación que ninguna vía consiguió entregar tras todos los intentos lee DELETE_FAILED y deja la publicación como estaba; mark-deleted se sigue aceptando entonces, para una publicación que alguien retiró a mano. Pedirlo de nuevo mientras una eliminación está en camino es un 409; una vez que salió, pedirlo de nuevo la envía otra vez.
Marcar como publicada, omitir y marcar como eliminada
Lo que solo una persona o un receptor pueden saber, dicho para un canal:
POST /v1/jobs/{id}/mark-publisheddice que la publicación está publicada. El cuerpo es opcional:{ "url": "https://..." }, el enlace a la publicación, de hasta 2000 caracteres, que entonces aparece en la pantalla Publicaciones y viaja con cualquier actualización o eliminación posterior. Se acepta desde entregada, omitida o fallida, y mientras aún se está entregando;publishedAtpasa a ser ese momento. Un enlace que no es una dirección web es un 400.POST /v1/jobs/{id}/skipdice que se entregó y no se publicará. Se acepta desde entregada. Una publicación omitida aún puede marcarse como publicada más tarde.POST /v1/jobs/{id}/mark-deleteddice que se retiró, normalmente después de entregarse una eliminación. Se acepta desde entregada o publicada.
Estas tres son el mismo informe que hace un receptor de webhook con POST /v1/posts/{id}/report, con las mismas reglas. Para un asistente de IA son reelwire_report_post, con estado published, skipped o deleted. Cada acción responde:
| Acción | En caso de éxito |
|---|---|
cancel | { "ok": true, "cancelledPosts": 1, "renderStopped": true } |
repost | { "ok": true, "postGroupId": "01M..." } (el canal de la nueva publicación) |
update | { "ok": true, "queued": 1 } |
delete | { "ok": true, "queued": 1 } |
resend-email | { "ok": true, "count": 1 } |
resend-webhook | { "ok": true, "count": 1 } |
resend-connector | { "ok": true, "count": 1 } |
resend-participants | { "ok": true, "count": 1 } |
retry | { "ok": true, "count": 1 } |
mark-published | { "ok": true, "count": 1, "state": "PUBLISHED" } |
skip | { "ok": true, "count": 1, "state": "SKIPPED" } |
mark-deleted | { "ok": true, "count": 1, "state": "RECALLED" } |
RECALLED es el estado almacenado que el estado del canal lee como DELETED.
Archivar y desarchivar
POST /v1/jobs/{id}/archive archiva la publicación de un canal: GET /v1/jobs la omite salvo que se pida archive=archived, y la fila lleva entonces "archived": true (la fila de una publicación cuando lo están todos sus canales). POST /v1/jobs/{id}/unarchive la recupera. Ninguna de las dos cambia nada más: una publicación que sigue en camino continúa, y se entrega como se habría entregado. Ambas responden { "ok": true, "archived": true } o false, y un 404 para un id que no es de una publicación de sus canales. Para un asistente de IA son reelwire_archive_post, que recupera una con archived false, y reelwire_list_posts admite archive.
Informar de vuelta
POST /v1/posts/{id}/report es como un receptor de webhook, un script o cualquiera que publique a mano dice qué ocurrió con la publicación de un canal, con una clave de API que tenga posts:write:
{ "status": "published", "url": "https://www.instagram.com/reel/C9x2Lm4oQpR/", "platformPostId": "17912345678901234" }
status es published, skipped, failed (con un reason que muestra la pantalla Publicaciones) o deleted. url es una dirección web de hasta 2000 caracteres, platformPostId hasta 200 y reason hasta 1000; un cuerpo que no encaja es un 400. El id es la publicación del canal (postId), o el id del canal de GET /v1/jobs, y uno que no es suyo es un 404. Responde con ok, postId, postGroupId, state y permalink. Decir lo mismo de nuevo está bien y conserva el enlace más reciente; todo lo que el estado de la publicación no permite es un 409 que nombra el estado. La regla completa, estado por estado, está en Webhooks.
Publicaciones entregadas por correo electrónico
Un canal con Correo activado entrega sus publicaciones por correo electrónico. Cuando una publicación vence, el propietario del espacio de trabajo y todos los que tienen Operaciones reciben un correo por la entrega, sea cual sea el número de canales que contenga. Su asunto es «Ready to post:» y los nombres de los canales. El correo en sí no lleva ni el video ni el texto: indica qué publicación está lista para qué canales, nombra los canales de la publicación que no están en él y por qué, y su botón Open the post kit lleva al kit de publicación. Consulte Publicar un video a mano para ver el correo y el kit de publicación completos.
El kit de publicación es una página en /v1/kit/{token} que no requiere iniciar sesión: el token de su dirección es la credencial. Para cada canal contiene el video para reproducir, descargar o compartir desde un teléfono, su imagen de portada, cada texto listo para copiar (el título cuando lo hay y el texto tal como sale, o, para un canal en Sin plataforma específica, la descripción en cada una de sus cuatro longitudes, una por tipo de plataforma), los ajustes de publicación del canal, y Marcar como publicada, con el enlace, u Omitir (en el correo de una eliminación, Marcar como eliminada en su lugar); encima, un prompt para un asistente de IA que abarca cada canal aún por publicar. Muestra la publicación tal como se lee ahora, así que un kit anterior sigue a una actualización. Sus botones informan a través de POST /v1/kit/{token}/{postGroupId}/published, /skip y /deleted, con las mismas reglas que informar de vuelta. Marcar como eliminada es solo para el kit de una eliminación, y Marcar como publicada y Omitir para cualquier otro kit: el equivocado es un 409. Un enlace funciona catorce días desde el correo que lo llevaba, y cada correo lleva el suyo; después la página dice que ha caducado (un 410), y la publicación sigue en la pantalla Publicaciones. Pedida con Accept: application/json, la misma dirección responde con los datos del kit como JSON, con expiresAt, y un enlace caducado responde entonces 403 en lugar de la página. Cada dirección puede pedir como máximo 60 veces por minuto.
Nadie aquí puede saber si se publicó ni dónde, así que la publicación del canal lee HANDED_OVER hasta que alguien lo diga. Un día después del correo, si uno de sus canales sigue entregado y nadie ha dicho nada, las mismas personas reciben un recordatorio breve, «Still to post:», con un enlace propio al kit de publicación.
Una actualización y una eliminación también van por correo, donde Correo está activado: una actualización con el asunto «Changed:» y el título «This post changed», una eliminación con «Take down:» y «Please take this post down», cada una con su kit de publicación.
Enviar una publicación de nuevo
Cada vía de salida tiene su propio reenvío, y la sindicación uno propio, para la publicación de un canal que se entregó, publicó u omitió:
POST /v1/jobs/{id}/resend-emailenvía el correo de nuevo, con la publicación tal como se lee ahora, con el asunto «Sent again:». Se acepta mientrasresendablees true y el canal tiene Correo activado (desactivado es desactivado: de lo contrario, un 409); también un 409 mientras una actualización, una eliminación o un correo reenviado anterior sigue en camino. Para un asistente de IA esreelwire_resend_postcon la víaemail.POST /v1/jobs/{id}/resend-webhookenvía de nuevo elpost.publishde la publicación a la dirección del webhook del espacio de trabajo, construido tal como se lee ahora, con undeliveryIdnuevo, de modo que un receptor que conserva los ids que ha procesado actúe de nuevo sobre él. Se acepta mientrasresendablees true y el canal tiene Webhook activado; un 409 cuando está desactivado, cuando el espacio de trabajo no tiene dirección de webhook, o mientras una llamada de webhook de la publicación sigue en camino. El estado de la publicación no cambia. Para un asistente de IA esreelwire_resend_postcon la víawebhook.POST /v1/jobs/{id}/resend-connectorla republica: el conector de plataforma del canal publica el video y su texto, tal como se lee ahora la publicación, de nuevo en la plataforma como una publicación nueva. La publicación anterior se queda donde está. Cuando la nueva está publicada, la publicación se comunica como publicada con su enlace, y una actualización o eliminación posterior actúa sobre ella. Se acepta mientrasresendablees true y el canal tiene Conector de plataforma activado; un 409 cuando está desactivado, o mientras el conector sigue trabajando en la publicación. Para un asistente de IA esreelwire_resend_postcon la víaconnector.POST /v1/jobs/{id}/resend-participantsenvía la publicación de nuevo, tal como se lee ahora, a cada participante de sindicación cuyo grupo incluye el canal, por las vías que permite cada grupo. Pensado para un canal con Correo, Webhook y Conector de plataforma desactivados, cuyos únicos lectores son sus participantes (una fila de canal dicesyndicatedcuando lo incluye un grupo con alguien registrado); un 409 donde ninguno lo hace. El estado de la publicación no cambia. Para un asistente de IA esreelwire_resend_postcon la víaparticipants.
Un canal con todas las vías desactivadas entrega su publicación en el momento en que vence: está HANDED_OVER sin correo, sin llamada de webhook y sin publicación de conector del propio espacio de trabajo (email, webhook y connector leen off). Una actualización es de la propia publicación de inmediato, y una eliminación lee DELETE_REQUESTED en cuanto se pide, ya que nada del propio espacio de trabajo se entregará nunca para que cualquiera de las dos sea cierta; ambas siguen llegando a sus participantes.
Republicar en un canal sin Webhook activado es un 409: reenviar el correo envía lo mismo a las mismas personas, y resend-connector la publica de nuevo en la plataforma.
Reintentar una publicación fallida
POST /v1/jobs/{id}/retry reintenta la publicación de un canal que falló (POST_FAILED: su correo, llamada de webhook o conector de plataforma nunca llegó tras todos los intentos, o su receptor la informó como failed). Su solicitud de publicación se entrega de nuevo, tal como se lee ahora la publicación, como una entrega propia: el correo lee «Ready to post:», la llamada de webhook es un post.publish con un deliveryId nuevo, y el conector de plataforma la publica. La publicación pasa de FAILED de vuelta a entregándose, y luego continúa como lo haría una primera entrega, a HANDED_OVER, o PUBLISHED donde la publicó el conector; si todas las vías vuelven a fallar, está FAILED otra vez con el motivo más reciente. El cuerpo es opcional: { "ways": ["EMAIL", "WEBHOOK", "CONNECTOR"] }, algunas de las vías que el canal tiene activadas, todas si se omite; una vía que tiene desactivada es un 409. A los participantes de sindicación no se les envía de nuevo, ya que la primera entrega les llegó. Se acepta mientras retryable es true; un 409 en caso contrario, cuando el canal tiene todas las vías desactivadas, cuando se elige Webhook y el espacio de trabajo no tiene dirección de webhook, o mientras la publicación ya se está reintentando. En la pantalla Publicaciones es Reenviar solicitud de publicación en una publicación fallida. Para un asistente de IA es reelwire_retry_post.
Renderizar de nuevo un clip fallido
POST /v1/jobs/{id}/retry-render renderiza de nuevo el clip de un canal después de que su renderizado fallara (RENDER_FAILED: el renderizador informó de un fallo, o el renderizado no terminó a tiempo). No lleva cuerpo. El mismo trabajo de renderizado vuelve a la cola con attempt uno más alto y se renderiza con el contenido, la marca y la plantilla de la publicación tal como están ahora, así que una plantilla o una marca corregida mientras tanto surte efecto. Una vez guardado el clip, la publicación se crea a partir de él y sigue como lo habría hecho la primera vez: AWAITING_APPROVAL donde el canal pide una aprobación, si no SCHEDULED, y luego se entrega cuando toca, como una entrega propia si los demás canales de la publicación salieron sin ella. Responde { "ok": true, "renderJobId": "...", "attempt": 2, "counted": false }. No cuesta ningún video, ya que el intento fallido ya se contó, salvo donde ese intento se rechazó porque no quedaban videos (counted es entonces true, y es un 409 mientras siga sin quedar ninguno). Se acepta mientras renderRetryable sea true en el canal; si no, un 409, también cuando el canal está en pausa (reanúdelo antes) o mientras el renderizado ya se está reintentando; un 404 para una publicación que no existe. En la pantalla Publicaciones es Reintentar el renderizado, en Más. Para un asistente de IA es reelwire_retry_post con render true.
El calendario de publicaciones
GET /v1/calendar muestra lo que se espera que se publique en una ventana: los canales, sus feeds programados y las publicaciones manuales en espera. from y to son instantes ISO obligatorios, la ventana es de como máximo 367 días (cabe un año por delante desde ahora), y todas las horas son UTC. Necesita schedule:read, un permiso propio, de modo que una clave que lee el calendario no pueda además actuar sobre publicaciones.
curl "http://localhost:4000/v1/calendar?from=2026-09-18T00:00:00Z&to=2026-09-19T00:00:00Z" \
-H "Authorization: Bearer rw_your_key_here"
Pase channelIds como lista separada por comas para acotar. Vacío significa todos los canales. kind conserva solo los feeds predefinidos (BROADCAST) o solo las listas de publicaciones (QUEUE); all es el valor predeterminado. Una ventana de más de 367 días, to antes de from, que falte alguno de los dos o no sea una fecha, u otro kind es un 400.
La respuesta es un plan, no una promesa: un feed publica cuando llegan sus datos, y estos son los momentos en que se espera que lo haga. channels describe cada canal una vez (marca, plataforma, formato, idioma, aprobación). entries es una línea por publicación esperada, cada una con su channelId, la source y su group, el momento at, allDay cuando el calendario de un feed nombra un día pero no una hora, y un scheduleLabel; las publicaciones de una lista de publicaciones son entradas a sus horas exactas, con su etiqueta indicando si cada una está programada, enviada o no se pudo enviar. unscheduled contiene lo que recibe un canal y no tiene hora que mostrar: un feed descrito en lugar de con hora, o un feed propio, que publica cuando su sistema le envía algo. Los canales en pausa y las fuentes en pausa no publican nada, así que se omiten.
Cada entrada indica además con qué se renderiza: templateId, variant y version (null para la compilación más reciente), feedId en la de un feed predefinido, y packageId y state en la de una lista de publicaciones. detail=posts añade templateName a cada entrada y, en la de una lista de publicaciones, post: lo que dice la publicación, como lo muestra la lista del Calendario.
{
"kind": "QUEUE",
"at": "2026-10-26T15:00:00.000Z",
"templateName": "Text card",
"variant": "Bento grid",
"post": {
"title": "Real ticks and real spreads for MetaTrader 5",
"kicker": "MT5 Tick Data",
"lines": [
{ "label": null, "text": "Real ticks. Real spreads. Real results.", "extras": [{ "label": "Words to mark", "text": "Real spreads." }] },
{ "label": "Attribution", "text": "FEA Trading" }
],
"caption": "Tick history for the MetaTrader 5 Strategy Tester, updated every month.",
"hashtags": ["metatrader5"]
}
}
lines siguen el orden propio de la plantilla y llevan sus etiquetas; el aspecto, las imágenes y los videos se omiten. caption y hashtags son lo que publica el canal junto al clip, el texto con la longitud que admite su plataforma.
Una vista previa de una publicación
GET /v1/calendar/still?channelId=...&subscriptionId=...&packageId=... responde con un PNG: la publicación de una lista de publicaciones renderizada con sus propias palabras en la marca, el formato y el idioma de su canal, un fotograma del último tercio del clip. Sin packageId, para un feed propio, renderiza en su lugar el contenido de ejemplo de la plantilla, ya que aún no se sabe lo que enviará su sistema. No cuenta contra sus videos y no se conserva en ninguna parte; una publicación sin cambios responde 304 al ETag que ya tiene. Un feed predefinido se previsualiza con sus propios clips, GET /v1/feeds/{feedId}/reference/{ratio}.
El calendario como página para descargar
GET /v1/calendar/export admite los mismos from, to, channelIds y kind y responde con una página HTML con una tabla de cada publicación de la ventana (lo que dice cada publicación de una lista de publicaciones, la regla de cada feed) y debajo las fuentes sin un calendario preciso, en el idioma de quien lo pide, como adjunto. Se abre en cualquier navegador y se imprime; nada en ella se obtiene de ningún sitio. Con format=pdf responde lo mismo como PDF en A4 apaisado (format=html, el valor predeterminado, es la página).
curl -OJ "http://localhost:4000/v1/calendar/export?from=2026-10-24T00:00:00Z&to=2026-11-23T23:59:59Z" \
-H "Authorization: Bearer rw_your_key_here"
curl -OJ "http://localhost:4000/v1/calendar/export?from=2026-10-24T00:00:00Z&to=2026-11-23T23:59:59Z&format=pdf" \
-H "Authorization: Bearer rw_your_key_here"
GET /v1/calendar/export/link responde en su lugar { url, expiresAt, from, to }: un enlace a la misma página, o con format=pdf al PDF, que funciona sin credencial durante quince minutos, para entregárselo a una persona, que es lo que hace reelwire_calendar del asistente de IA con download (y format pdf para el PDF). La ventana, los canales, el tipo, el idioma y el formato van firmados en él, así que un enlace editado se rechaza.
Con imágenes de cada publicación
Tres imágenes de cada publicación, de su inicio, su mitad y su keyframe, renderizadas con la marca de su canal, tardan unos segundos cada una, por lo que ese archivo se crea como un trabajo. POST /v1/calendar/export/jobs admite from, to, channelIds (una lista), kind, format y stills: true y responde 202 { jobId }. GET /v1/calendar/export/jobs/{jobId} responde { jobId, state, done, total, ahead, error, format, stills, from, to, kind, channels, askedAt, keptUntil, fileName }, donde state es queued (con ahead trabajos delante), working, done o failed y done de total las imágenes renderizadas hasta ahora; una vez que es done, GET /v1/calendar/export/jobs/{jobId}/file es el archivo. Los trabajos de un espacio de trabajo son una cola, que se hacen uno tras otro, como máximo cinco en espera o en curso a la vez (un sexto es un 409). GET /v1/calendar/export/jobs lista los suyos, en el orden en que los pidió, como { jobs: [...] }, y POST /v1/calendar/export/jobs/{jobId}/cancel cancela uno que espera o se está haciendo, o saca de la lista uno terminado. Cada una de estas responde solo por los trabajos que pidió la misma clave o persona. Las imágenes se renderizan para las primeras 100 publicaciones. Un trabajo terminado se conserva una hora desde que terminó, hasta keptUntil, y después se olvida; uno que espera o se está haciendo se conserva hasta que termina. Los trabajos se guardan en memoria, así que un reinicio de la API, una actualización incluida, los olvida. GET /v1/calendar/export/link?jobId={jobId} responde con un enlace al archivo de un trabajo terminado, { url, expiresAt, from, to, fileName }, que funciona sin credencial durante quince minutos mientras se conserve el trabajo; un trabajo que aún se está haciendo es un 409. Para un asistente de IA es reelwire_calendar con download y stills, y después exportJobId para preguntar cómo va y entregar el archivo.
curl -X POST http://localhost:4000/v1/calendar/export/jobs \
-H "Authorization: Bearer rw_your_key_here" -H "Content-Type: application/json" \
-d '{"from":"2026-10-24T00:00:00Z","to":"2026-11-23T23:59:59Z","format":"pdf","stills":true}'
Descargar una publicación
GET /v1/jobs/{id}/download y GET /v1/posts/{id}/package devuelven ambas un ZIP, no JSON:
HTTP/1.1 200 OK
content-type: application/zip
content-disposition: attachment; filename="2026-09-18--07-46-28 - Brand 1 - Channel 10 en - Instagram Reels - 9x16.zip"
content-length: 741635
x-request-id: 01M2SR0G3PCP3E63SZ6DZBZBJ0
Son para lectores distintos:
/v1/jobs/{id}/downloades para una persona y admite elidde un canal. Contiene el video y una página HTML que muestra la publicación como su kit de publicación: marca, formato, estado y horas, el clip y cada texto para pegar bajo su propia etiqueta con un botón Copiar. Un canal en Sin plataforma específica recibe el texto para cada tipo de plataforma. La página funciona sin conexión y se puede reenviar tal cual. Cuando no hay video, porque el render sigue en marcha, falló o el clip se eliminó para hacer sitio en el almacenamiento, la página dice por qué donde estaría el clip./v1/posts/{id}/packagees para una máquina y admite el id de publicación de un canal. Contiene el video, una pequeña página que lo reproduce y un JSON de la publicación:meta(creada, estado, marca, canal, plataforma, aprobación, publicada) ydescription(idioma, relación de aspecto, título, texto, descripción de la marca, enlace, hashtags ytext, todos juntos tal como se publican). Una publicación cuyo clip ya no está guardado es un 404, porque no hay nada que empaquetar.
Pase ?tz=Europe/Berlin para tener las horas de los nombres de archivo y del interior de los archivos en su propia zona. El video es el que muestra ahora la publicación: un reemplazo de la biblioteca multimedia donde una actualización puso uno, y si no el clip renderizado.
Errores frecuentes
Consultar /v1/jobs en un bucle cerrado. Use /v1/pipeline y respete pollMs, o use el webhook y deje de consultar.
Guardar una URL de recurso. Están firmadas y caducan en menos de una hora. Conserve el assetId y pregunte de nuevo.
Suponer que una publicación es un canal. Una publicación va a cada canal que recibe su fuente, y sus canales pueden estar en estados distintos: uno publicado, otro esperando a un revisor.
Actuar sobre el id de la publicación. Las acciones admiten el id de un canal de channels, nunca el id de la fila, que es el postKey de la publicación.
Llamar a una acción y tratar el 409 como una ruta de error. updatable, resendable, retryable, markable, skippable, deletable, markDeletable y repostable en el canal lo indican de antemano.
Leer HANDED_OVER como publicada. Significa que una vía de salida la entregó a una persona o a un receptor. PUBLISHED llega cuando alguien lo dice, o cuando la publicó el conector de plataforma del canal.
Esperar que /v1/posts/{id}/package devuelva JSON. Es un ZIP con un JSON dentro.
Tratar stage como un estado. Es un grupo de estados. Filtre por stage, lea status.