antworkDOCS

Sending a post

Publish or schedule a post you already created, one at a time or a whole campaign, without waiting for the platform.

POSThttps://antwork.io/api/v1/posts/{id}/publishScopepublish

Publishing now

POST /api/v1/posts/{id}/publish sends a stored post, typically a draft made with draft: true or posts[]. It is committed with a delivery time of now and queued; the answer is a 202 with the post, and nothing waits for the platform. Poll GET /api/v1/posts/{id} until status is published or failed. Needs the publish scope. Send an Idempotency-Key: a retried request must not send the post twice.

curl -X POST https://antwork.io/api/v1/posts/pQ7x.../publish \
  -H "Authorization: Bearer $ANTWORK_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{}'
202 Accepted
{
  "object": "post",
  "id": "pQ7x...",
  "status": "scheduled",
  "scheduledFor": "2026-11-05T10:00:00.000Z",
  "readiness": { "characterCount": 112, "characterLimit": 280, "overLimit": false,
                 "mediaRequired": false, "settingsRequired": false, "publishBlocker": null },
  "editUrl": "https://antwork.io/workspace/ws_.../compose?postId=pQ7x...",
  "retried": false,
  ...
}

A post whose publish failed is retried instead: the platform's error is cleared and it is queued a minute out, with retried: true. The same thing the MCP publish_post tool does with a failed post.

Scheduling

POST /api/v1/posts/{id}/schedule with {"scheduledFor": "..."} (ISO 8601, in the future, at most 30 days ahead) schedules a stored post through the same checks; a scheduled post moves to the new time. 202, and the post as it now stands. Needs publish.

curl -X POST https://antwork.io/api/v1/posts/pQ7x.../schedule \
  -H "Authorization: Bearer $ANTWORK_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "scheduledFor": "2026-11-05T10:00:00Z"
  }'

POST /api/v1/posts/{id}/unschedule moves a scheduled post back to draft and cancels its queued publish, the same as PATCH /api/v1/posts/{id} with status: "draft". It answers 200 with the post at once, and unscheduled: false when it was not scheduled. Only write is needed, since it takes work out of the queue.

When a post is not sent

The reasons are the ones POST /api/v1/posts gives per target. A post that breaks a platform rule is a 400 (text_too_long, media_required, pinterest_board_required…); fix it with PATCH /api/v1/posts/{id} or at its editUrl. When your plan or the account has no room, it is a 409 (quota_exceeded, daily_cap_reached, schedule_queue_full, duplicate_post, posting_too_close). A published post is a 409 post_already_published. Nothing is queued in any of these cases.

409 Conflict
{
  "error": {
    "type": "invalid_request_error",
    "code": "daily_cap_reached",
    "message": "This X account has reached its daily cap ..."
  }
}
TikTok requires the account owner to choose each post's audience. A TikTok post is refused with tiktok_privacy_required until the owner picks it at the post's settingsUrl; after that, these endpoints send it.

Campaigns

POST /api/v1/campaigns/{id}/publish sends every post of a campaign (the campaignId a create returns), each on its own: one refused post does not hold the others back, and a failed one is retried. At most 10 posts per campaign. The answer is the create's batch: a post_result per post, 202 when any was accepted, and a 400 with the first reason when none was. Poll each post.

curl -X POST https://antwork.io/api/v1/campaigns/camp-1a2b3c4d5e6f/publish \
  -H "Authorization: Bearer $ANTWORK_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{}'
202 Accepted
{
  "object": "post_batch",
  "campaignId": "camp-1a2b3c4d5e6f",
  "posts": [
    { "object": "post_result", "id": "pQ7x...", "platform": "linkedin", "status": "accepted", "reason": null, ... },
    { "object": "post_result", "id": "pR2k...", "platform": "x", "status": "rejected", "reason": "text_too_long", ... }
  ]
}

POST /api/v1/campaigns/{id}/schedule with {"scheduledFor": "..."} schedules them all at one time. A campaign that is not yours answers 404 campaign_not_found.