antworkDOCS

Crear publicaciones

Una petición crea una publicación por cada cuenta de destino. Cada una es independiente, con su propia programación y su propio resultado; solo comparten un id de campaña.

POSThttps://antwork.io/api/v1/postsPermisowrite · publish

Petición

curl https://antwork.io/api/v1/posts \
  -H "Authorization: Bearer $ANTWORK_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "accounts": ["acc_linkedin", "acc_x"],
    "text": "Shipped the API today.",
    "scheduledFor": "2026-10-01T09:00:00Z"
  }'

Respuesta

La respuesta es 202 Accepted, nunca 200. Nada se publica dentro de la petición: la publicación se encola y un worker la envía, así que la respuesta dice qué se ha aceptado, no qué se ha publicado.

202 Accepted
{
  "object": "post_batch",
  "campaignId": "b3f1...",
  "posts": [
    {
      "object": "post_result",
      "id": "pQ7x...",
      "accountId": "acc_linkedin",
      "platform": "linkedin",
      "status": "accepted",
      "reason": null,
      "message": null,
      "readiness": {
        "characterCount": 22,
        "characterLimit": 3000,
        "overLimit": false,
        "mediaRequired": false,
        "settingsRequired": false,
        "publishBlocker": null
      },
      "editUrl": "https://antwork.io/workspace/ws_1/compose?postId=pQ7x...",
      "settingsUrl": "https://antwork.io/workspace/ws_1/compose?postId=pQ7x...&focus=settings"
    },
    {
      "object": "post_result",
      "id": null,
      "accountId": "acc_x",
      "platform": "x",
      "status": "rejected",
      "reason": "text_too_long",
      "message": "@antwork: Text exceeds x character limit",
      "readiness": null,
      "editUrl": null,
      "settingsUrl": null
    }
  ]
}
Un destino falla solo. Una cuenta que se pasa del límite de caracteres no cancela las demás, así que un 202 puede contener rechazos. Lee todas las entradas de posts, no solo el código de estado. Solo cuando no se acepta ninguna la respuesta es un 400, y el array viaja igualmente con ella.

Una entrada por publicación

posts asigna una entrada a una publicación, así que cada una lleva su propio texto, hashtags, etiquetas, medios y ajustes. Los hashtags se añaden al texto donde la plataforma los publica (YouTube los guarda como etiquetas del vídeo). userTags etiqueta una página de empresa de LinkedIn ({"company": "https://www.linkedin.com/company/acme"}, con @Acme en el texto) o a personas en medios de Instagram ({"type": "photo_tag", "username": "acme", "x": 0.5, "y": 0.5}). Los medios tienen que ser archivos de la biblioteca de este espacio de trabajo; si no, la petición se rechaza con media_not_in_library. Más de una entrada forma una campaña.

POST /api/v1/posts
{
  "draft": true,
  "posts": [
    { "accountId": "acc_linkedin", "text": "Shipped the API today. @Acme", "hashtags": ["buildinpublic"],
      "userTags": [{ "company": "https://www.linkedin.com/company/acme" }] },
    { "accountId": "acc_youtube", "text": "How the API works", "media": ["https://firebasestorage.googleapis.com/..."],
      "options": { "privacyStatus": "unlisted", "title": "The Antwork API in 60 seconds" } },
    { "accountId": "acc_pinterest", "text": "Ship notes", "media": ["https://firebasestorage.googleapis.com/..."],
      "options": { "boardId": "1234567890" } }
  ]
}

Qué le falta a una publicación

La entrada de cada publicación creada lleva readiness y dos enlaces al editor de Antwork. readiness.publishBlocker es el motivo por el que se rechazaría publicarla ahora mismo (en un borrador: media_required, tiktok_privacy_required, pinterest_board_required, etc.), o null. mediaRequired, settingsRequired, overLimit, characterCount y characterLimit explican por qué. editUrl abre la publicación en el editor y settingsUrl abre sus ajustes. Una entrada rechazada tiene los tres a null.

post_result
{
  "object": "post_result",
  "id": "pQ7x...",
  "accountId": "acc_tiktok",
  "platform": "tiktok",
  "status": "accepted",
  "reason": null,
  "message": null,
  "readiness": {
    "characterCount": 42,
    "characterLimit": 2200,
    "overLimit": false,
    "mediaRequired": false,
    "settingsRequired": true,
    "publishBlocker": "tiktok_privacy_required"
  },
  "editUrl": "https://antwork.io/workspace/ws_1/compose?postId=pQ7x...",
  "settingsUrl": "https://antwork.io/workspace/ws_1/compose?postId=pQ7x...&focus=settings"
}

Qué significa cada estado

  • accepted — creada y programada (o guardada, si es un borrador). Consulta el id para ver qué pasa.
  • rejected — rechazada antes de escribir nada. No existe; corrige reason y vuelve a enviar.
  • failed — se creó pero no se pudo programar. Existe y no va a salir.

Programación

Omite scheduledFor para publicar de inmediato. Indica una hora ISO 8601 para programar: hasta 30 días vista en Free, sin límite en los planes de pago. En Free, una hora posterior se rechaza con scheduled_for_too_far, y el error indica la última hora que admite. Una hora en el pasado se rechaza con scheduled_for_in_past.

Cuerpo de la petición

accountsstring[]
Obligatorio salvo que envíes posts. Ids de cuenta de GET /api/v1/accounts, como máximo 25. Se crea una publicación por cuenta.
postsobject[]
En lugar de accounts: una entrada por publicación, como mucho 25. Cada una admite accountId (obligatorio), text, hashtags, goal, userTags, media y options. Nombra una cuenta dos veces para tener dos versiones en ella. No se combina con accounts, text, texts, media ni options.
textstring
El texto de la publicación, usado en cada cuenta que no tenga entrada en texts.
textsobject
Texto por cuenta, indexado por id de cuenta. Sustituye a text para esa cuenta. Úsalo cuando una plataforma necesite una versión más corta.
mediastring[]
Ids de medios de GET /api/v1/media o POST /api/v1/media, o URLs de medios de Antwork, no cualquiera. Un archivo tiene que estar en tu biblioteca antes de adjuntarlo: añade primero un archivo externo con POST /api/v1/media. Una URL externa se rechaza con media_not_hosted, y un id desconocido con media_not_found, en lugar de aceptarse y fallar al publicar.
scheduledForISO 8601
Hora ISO 8601 de publicación (hasta 30 días vista en Free, sin límite en los planes de pago). Omítela para publicar de inmediato.
draftboolean
true crea un borrador y no envía nada, y solo necesita el permiso write. Cualquier otro valor crea una publicación que se enviará, lo que necesita publish.
optionsobject
Ajustes específicos de cada plataforma, indexados por plataforma (options.youtube.title). Consulta Opciones por plataforma. En posts, las options de cada entrada llevan directamente los ajustes de su plataforma.
workspaceIdstring
El espacio de trabajo desde el que publicar. Solo es obligatorio si tienes más de un espacio de trabajo: sin él la petición se rechaza con workspace_required. Todas las cuentas deben pertenecer a él.
campaignIdstring
Agrupa las publicaciones bajo un id. Se asigna solo cuando se acepta más de una cuenta; pasa un id existente para añadir estas publicaciones a ese grupo.
La API publica en cuentas de X (Twitter), LinkedIn, Facebook, Threads, Instagram, YouTube Shorts, TikTok, Pinterest, Bluesky, Discord, Mastodon, Telegram, Google Business Profile y Slack. Una publicación de TikTok recibe las elecciones del propio usuario en el options de su entrada de posts[] (quién puede verla, cada interacción, la declaración de contenido comercial y userConsented: true cuando acepta la declaración de TikTok). Una publicación de TikTok que vaya a salir sin ellas se rechaza con tiktok_choices_needed, cuyo tiktokChoices lista las opciones actuales del creador para que tu interfaz pregunte. platform_not_available ya no se devuelve.

Opciones por plataforma

Todo lo específico de una plataforma va en options, indexado por plataforma y aplicado a todas las cuentas de esa plataforma en la petición. Son los ajustes que ofrece el editor de Antwork, con los mismos valores y límites. Los ajustes de TikTok los elige el usuario en cada publicación, así que un options.tiktok de nivel superior se rechaza con tiktok_settings_need_the_creator: envíalos por publicación, en posts[].options.

PlataformaAcepta
xthreadParts
linkedinfirstComment
facebookpublishType, firstComment
threadsfirstComment
instagrampublishType, postToGrid, coverUrl, firstComment
youtubeprivacyStatus, title, categoryId, madeForKids, containsSyntheticMedia, thumbnailUrl, tags, playlist, captions, firstComment
tiktokno acepta opciones
pinterestboardId, pinTitle, link
blueskyfirstComment
discordno acepta opciones
mastodonfirstComment
telegramno acepta opciones
google_businesscallToAction, callToActionUrl
slackchannel
Las claves desconocidas se rechazan, no se ignoran. Una errata es un 400 que nombra el campo, no una publicación que sale sin el ajuste que pediste.

Idempotencia

Envía una cabecera Idempotency-Key en cada creación, para que un reintento no publique dos veces. Cómo se comporta la clave está en la página de Idempotencia. Idempotencia →

¿Algo no cuadra? Dinos cuál.