Posts

Create post draft

Creates a workspace post draft, optionally assigned to a LinkedIn profile, company page or X account. Never publishes by itself. For an X account pass exactly one of content, tweets or article.

POST/api/public/posts
cURLPOST /api/public/posts
curl -X POST https://postiv.ai/api/public/posts \
  -H "Authorization: Bearer pk_postiv_..." \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Launch lessons",
    "content": "Three things we learned from launch week...",
    "humanize": true
  }'
200Response
{
  "success": true,
  "post": {
    "id": "post1...",
    "channel": "linkedin",
    "status": "draft",
    "content": "Most founders think..."
  },
  "humanized": false
}

Authorization

Requires an organization API key with the posts:write scope, sent as Authorization: Bearer or x-api-key.

Body parameters

typestring

Only 'document' is accepted; omit to default.

One of:document
titlestring

Max 240 chars.

contentstringdefault: ""

LinkedIn post text, max 100000 chars. For an X account, plain text that is auto-split into tweets.

tweetsstring|array

X accounts only, instead of content: a tweet string, or an array (a thread) of tweet strings or { text, assets? }, where assets is [{ assetId, alt (max 2000) }]. Needs an X integrationId (else 400 x_account_required).

articleobject

X accounts only, instead of content: { headline (max 300), body (max 100000) }. headline is plain text; body is markdown: # ## ### headings, bold, *italic*, text links, - and 1. lists, > quotes, a blank line between paragraphs and a single newline for a line break. Markdown images are refused (400 invalid_x_content): place images with attach-post-media.

integrationIdint | null

Alias: integration_id. A LinkedIn profile, company page or X account. Omit or pass null for an unassigned LinkedIn draft; an X draft needs an X account. Scheduling requires a connected account.

templateIduuid

Alias: template_id, from search_templates. LinkedIn only: with an X account it returns 400 template_linkedin_only.

planItemIduuid

Alias: plan_item_id, from get_content_plan_item.

humanizebooleandefault: false

Opt-in: runs the humanizer pass over content before saving. Defaults to false in both REST and MCP create_post. LinkedIn only: with an X account it returns 400 humanize_linkedin_only.

linkPreviewUrlstring | null

LinkedIn only: which link in content publishes as a preview card (title and thumbnail). Omit to put the card on the first link, pass one of the links in content to choose another, or null for no card. Media (image, video, carousel, PDF) publishes instead of the card. 400 invalid_link_preview_url when the link is not in content; 400 link_preview_linkedin_only with an X account.

Response

The created post.

postobject

Full post object: same shape as GET /api/public/posts/:id.

humanizedboolean

Whether the humanizer changed the content.

Errors

400content_too_long_to_humanize: content over 20000 chars with humanize on
400invalid_x_content: the tweets, article or content do not make a valid X post, or more than one of content, tweets and article was given (details.issues lists the problems)
400humanize_linkedin_only / template_linkedin_only: humanize or templateId sent with an X account
400x_account_required: tweets or article sent without an X account
402subscription_required (from checkSubscriptionAccess inside createPostDraft)
404linkedin_profile_not_found (LinkedIn) / integration_not_found (X account missing or needing reconnection) / post_template_not_found

Good to know

  • For LinkedIn posts, Markdown emphasis (bold, *italic*) is always converted to Unicode glyphs before saving: LinkedIn renders everything else as literal characters. planItemId links the draft back to Bob's plan and marks that item drafted.
  • X drafts save as written: no humanizer and no Markdown-to-Unicode conversion. A tweet over 280 characters (a link counts 23) is allowed in a draft, but scheduling refuses it. The returned post carries channel and format.