antworkDOCS

Creating posts

One request creates one post per target account. Each is an independent post with its own schedule and its own outcome; they share only a campaign id.

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

Request

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"
  }'

Response

The answer is 202 Accepted, never 200. Nothing publishes inside the request: a post is queued and a worker sends it, so the response tells you what was accepted rather than what was published.

202 Accepted
{
  "object": "post_batch",
  "campaignId": "b3f1...",
  "posts": [
    {
      "object": "post_result",
      "id": "pQ7x...",
      "accountId": "acc_linkedin",
      "platform": "linkedin",
      "status": "accepted",
      "reason": null,
      "message": null
    },
    {
      "object": "post_result",
      "id": null,
      "accountId": "acc_x",
      "platform": "x",
      "status": "rejected",
      "reason": "text_too_long",
      "message": "@antwork: Text exceeds x character limit"
    }
  ]
}
A target fails alone. One account over its character limit does not cancel the others, so a 202 can contain rejections. Read every entry in posts, not just the status code. Only when nothing at all was accepted is the answer a 400, and the array still ships with it.

What each target status means

  • accepted — written, and scheduled (or saved, for a draft). Poll the id to see what happened.
  • rejected — refused before anything was written. Nothing exists; fix reason and send again.
  • failed — the post was created but could not be scheduled. It exists and will not go out.

Scheduling

Omit scheduledFor to publish immediately. Supply an ISO 8601 time to schedule, at most 30 days ahead. The queue behind this will not accept a task further out, and the error names the latest time it will take. A time in the past is refused with scheduled_for_in_past.

Request body

accountsstring[]required
Required. Account ids from GET /api/v1/accounts, at most 25. One post is created per account.
textstring
The post text, used for every account that has no entry in texts.
textsobject
Per-account text, keyed by account id. Overrides text for that account. Use it when one platform needs a shorter version.
mediastring[]
Antwork media URLs, not arbitrary ones. A file has to be in your library before it can be attached; an external URL is refused with media_not_hosted rather than accepted and then failed at publish time. Uploading through the API is not available yet.
scheduledForISO 8601
ISO 8601 time to publish at, at most 30 days ahead. Omit it to publish immediately.
draftboolean
true creates a draft and sends nothing, and needs only the write scope. Anything else creates a post that will go out, which needs publish.
optionsobject
Platform-specific settings, keyed by platform. See Platform options.
workspaceIdstring
The workspace to post from. Required only when you own more than one workspace: without it the request is refused with workspace_required. Every account must belong to it.
campaignIdstring
Groups the posts under one id. Set for you when more than one account is accepted; pass an existing id to add these posts to that group.
The API publishes to LinkedIn and X (Twitter) accounts. An account on any other platform is refused on its own with platform_not_available, and the rest of the request goes ahead. GET /api/v1/accounts lists every connected account, so check platform before sending.

Platform options

Anything platform-specific goes in options, keyed by platform and applied to every account of that platform in the request. Only the platforms listed here accept options through the API.

PlatformAccepts
linkedinaccepts no options
xcommunityId, shareWithFollowers
options
{
  "accounts": ["acc_x"],
  "text": "Shipped the API today.",
  "options": {
    "x": { "communityId": "1493446837214", "shareWithFollowers": true }
  }
}
Unknown keys are refused rather than ignored. A typo is a 400 naming the field, not a post that quietly goes out without the setting you asked for.

Idempotency

Send an Idempotency-Key header with every create, so a retry cannot post twice. How the key behaves is on the Idempotency page. Idempotency →