{"openapi":"3.1.0","info":{"title":"Postel API","description":"Schedule, publish and write social posts from your own code, Zapier, n8n or Make.\n\nAuthenticate with an API key from **Settings → API & MCP** in Postel, sent as `Authorization: Bearer pstl_…`. A key acts for one workspace.\n\n- **Errors** are JSON: `{ \"error\", \"code\", \"requestId\" }`. `error` is a sentence you can show to a person; `code` is stable.\n- **Rate limits**: 60 requests a minute per key. Over it you get 429 with `Retry-After`.\n- **Retries**: send an `Idempotency-Key` header on writes that support it, and a retry returns the first response instead of creating twice.\n- **Request ids**: every response has an `X-Request-Id` header. Quote it when you contact support.\n- **Versioning**: v1 only grows. Fields, codes and parameters are never renamed or removed; a breaking change would ship as /v2.","version":"1.0.0","termsOfService":"https://www.postel.app/legal/terms","contact":{"name":"Postel support","url":"https://www.postel.app/contact"}},"servers":[{"url":"https://www.postel.app/api","description":"Production"}],"externalDocs":{"description":"Guides and examples","url":"https://www.postel.app/docs/api"},"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"pstl_…","description":"A Postel API key, `Authorization: Bearer pstl_…`. Create one in Postel under **Settings → API & MCP**; it acts for the workspace it was made in."}},"headers":{"RequestId":{"description":"Identifies this request: the X-Request-Id you sent (letters, digits, `_`, `-`, `=`, up to 255), else one Postel made. Also in every error body as `requestId`.","schema":{"type":"string","examples":["req_8f14e45fceea167a5a36dedd4bea2543"]}},"RetryAfter":{"description":"Seconds to wait before trying again","schema":{"type":"integer","examples":[12]}},"IdempotentReplayed":{"description":"`true` when this is the stored response to an earlier request with the same Idempotency-Key; nothing new was created","schema":{"type":"string","enum":["true"]}}},"schemas":{"Channel":{"type":"object","properties":{"id":{"type":"string","description":"Use as `platform` when posting"},"name":{"type":"string"},"account":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The connected account's name or handle"},"connected":{"type":"boolean"},"status":{"type":"string","enum":["ok","expiring","expired","disconnected"],"description":"expired: reconnect it in Postel before posting"},"label":{"type":"string","description":"For dropdowns: \"LinkedIn (Robin Faraj)\", \"Bluesky (not connected)\""}},"required":["id","name","account","connected","status","label"]},"CreatePostRequest":{"type":"object","properties":{"platform":{"type":"string","enum":["x","linkedin","linkedin_page","bluesky","mastodon","threads","youtube","instagram","tiktok"],"description":"Network to post to; the live ones are x, linkedin, bluesky, mastodon, youtube, tiktok. GET /v1/channels lists them with their accounts."},"text":{"description":"The post. On X, Bluesky and Mastodon a line with only --- starts the next part of a thread.","type":"string","maxLength":25000},"thread":{"description":"A thread, one item per part (X, Bluesky, Mastodon). Use instead of text.","minItems":1,"maxItems":25,"type":"array","items":{"type":"string","maxLength":25000}},"mediaUrls":{"description":"Public image or video URLs, added to the first part. X: up to 4 images, or one video or GIF. LinkedIn: images, or one video.","maxItems":4,"type":"array","items":{"type":"string","maxLength":2048,"format":"uri"}},"firstComment":{"description":"LinkedIn only: a comment Postel posts under the post right after it goes out. Up to 1250 characters.","type":"string","maxLength":1250},"scheduledAt":{"description":"Schedule it for this time (needs a paid plan). Leave out to save a draft.","type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"publishNow":{"description":"Publish right away (needs a paid plan)","type":"boolean"}},"required":["platform"],"description":"Send `text` or `thread`, not both. Add `scheduledAt` or `publishNow` (not both) to publish; leave both out for a draft."},"CreateViralTakeRequest":{"type":"object","properties":{"viralPostId":{"description":"An X post from GET /v1/viral/posts to use as the template","type":"string","minLength":1},"format":{"description":"Or a format from GET /v1/viral/formats","type":"string","minLength":1},"answer":{"default":"","description":"Your answer to the format's question (required with a format, optional with a post): the facts the post is about","type":"string","maxLength":2000},"platforms":{"default":["linkedin"],"description":"One draft per network","minItems":1,"maxItems":2,"type":"array","items":{"type":"string","enum":["x","linkedin"]}}},"description":"Send a `viralPostId` or a `format`, not both."},"Error":{"type":"object","properties":{"error":{"type":"string","description":"What went wrong and what to do, in plain words"},"code":{"type":"string","description":"Stable machine-readable reason"},"postId":{"description":"Set when the post was saved before the step failed","type":"string"},"requestId":{"type":"string","description":"The request's X-Request-Id. Quote it when you contact support"}},"required":["error","code","requestId"]},"Event":{"type":"object","properties":{"id":{"type":"string","description":"Unique per occurrence; dedupe on it"},"type":{"type":"string","enum":["post.published","post.failed","draft.ready","viral.new"]},"occurredAt":{"type":"string","description":"UTC ISO-8601"},"post":{"anyOf":[{"$ref":"#/components/schemas/Post"},{"type":"null"}]},"draft":{"anyOf":[{"$ref":"#/components/schemas/ReadyDraft"},{"type":"null"}]},"viralPost":{"anyOf":[{"$ref":"#/components/schemas/NicheViralPost"},{"type":"null"}],"description":"viral.new: the post that took off in your niche"}},"required":["id","type","occurredAt","post","draft","viralPost"]},"GeneratePostRequest":{"type":"object","properties":{"prompt":{"type":"string","minLength":1,"maxLength":5000,"description":"What the post should be about: an idea, notes, a link's gist"},"platform":{"type":"string","enum":["x","linkedin","linkedin_page","bluesky","mastodon","threads","youtube","instagram","tiktok"],"description":"Network to post to; the live ones are x, linkedin, bluesky, mastodon, youtube, tiktok. GET /v1/channels lists them with their accounts."},"thread":{"description":"Write a thread instead of one post (X only)","type":"boolean"},"voiceProfileId":{"description":"Voice profile to write in; leave out for your own voice","type":"string"}},"required":["prompt","platform"]},"NicheViralPost":{"type":"object","properties":{"id":{"type":"string","description":"Postel's id for the viral post; use it to write your take"},"network":{"type":"string","enum":["x","youtube","tiktok","instagram"]},"url":{"type":"string","description":"The post on its network"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Videos: the title"},"text":{"type":"string"},"author":{"type":"object","properties":{"handle":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]},"followers":{"anyOf":[{"type":"number"},{"type":"null"}]}},"required":["handle","name","followers"]},"postedAt":{"type":"string"},"views":{"anyOf":[{"type":"number"},{"type":"null"}]},"likes":{"anyOf":[{"type":"number"},{"type":"null"}]},"comments":{"anyOf":[{"type":"number"},{"type":"null"}]},"reposts":{"anyOf":[{"type":"number"},{"type":"null"}]},"outlierScore":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"How far it outran normal: X, views ÷ followers; videos, views ÷ the creator's usual (or followers, or the keyword's usual: see outlierBasis)"},"outlierBasis":{"anyOf":[{"type":"string","enum":["followers","usual","topic"]},{"type":"null"}]},"format":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"X: the post format it follows (GET /v1/viral/formats)"},"isShort":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Videos: short-form"},"fit":{"type":"number","description":"0 to 1: how well it fits your niche, as Postel scored it"}},"required":["id","network","url","title","text","author","postedAt","views","likes","comments","reposts","outlierScore","outlierBasis","format","isShort","fit"]},"Post":{"type":"object","properties":{"id":{"type":"string","description":"Postel post id. Stable; use it to dedupe."},"platform":{"type":"string","description":"x, linkedin, bluesky, …"},"status":{"type":"string","enum":["draft","scheduled","published","pending_approval","idea"],"description":"draft, scheduled, published, or pending_approval (waiting for an owner or admin to approve it)"},"text":{"type":"string","description":"The whole post as plain text; thread parts joined by a blank line"},"thread":{"type":"array","items":{"type":"string"},"description":"Each part of the thread in order; one item for a single post"},"firstComment":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"LinkedIn: comment posted under the post right after it goes out"},"mediaCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"scheduledAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"UTC ISO-8601"},"publishedAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"UTC ISO-8601"},"url":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Link to the published post on its network"},"failure":{"anyOf":[{"type":"object","properties":{"message":{"type":"string"},"type":{"type":"string"},"at":{"type":"string"},"partlyPublished":{"type":"boolean","description":"Part of a thread went out before the rest failed: the post stays published, so don't send it again"}},"required":["message","type","at","partlyPublished"]},{"type":"null"}],"description":"Why the last publish failed. The post is back in drafts, unless part of a thread already went out"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"}},"required":["id","platform","status","text","thread","firstComment","mediaCount","scheduledAt","publishedAt","url","failure","createdAt","updatedAt"]},"PostResult":{"type":"object","properties":{"id":{"type":"string","description":"Postel post id. Stable; use it to dedupe."},"platform":{"type":"string","description":"x, linkedin, bluesky, …"},"status":{"type":"string","enum":["draft","scheduled","published","pending_approval","idea"],"description":"draft, scheduled, published, or pending_approval (waiting for an owner or admin to approve it)"},"text":{"type":"string","description":"The whole post as plain text; thread parts joined by a blank line"},"thread":{"type":"array","items":{"type":"string"},"description":"Each part of the thread in order; one item for a single post"},"firstComment":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"LinkedIn: comment posted under the post right after it goes out"},"mediaCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"scheduledAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"UTC ISO-8601"},"publishedAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"UTC ISO-8601"},"url":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Link to the published post on its network"},"failure":{"anyOf":[{"type":"object","properties":{"message":{"type":"string"},"type":{"type":"string"},"at":{"type":"string"},"partlyPublished":{"type":"boolean","description":"Part of a thread went out before the rest failed: the post stays published, so don't send it again"}},"required":["message","type","at","partlyPublished"]},{"type":"null"}],"description":"Why the last publish failed. The post is back in drafts, unless part of a thread already went out"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"message":{"description":"Set when the post waits for approval, or the network is still processing it","type":"string"}},"required":["id","platform","status","text","thread","firstComment","mediaCount","scheduledAt","publishedAt","url","failure","createdAt","updatedAt"]},"ReadyDraft":{"type":"object","properties":{"id":{"type":"string"},"platform":{"type":"string"},"text":{"type":"string"},"writtenAt":{"type":"string"},"plannedFor":{"anyOf":[{"type":"string"},{"type":"null"}]},"reviewUrl":{"type":"string","description":"Where to review, edit and schedule it"}},"required":["id","platform","text","writtenAt","plannedFor","reviewUrl"]},"SchedulePostRequest":{"type":"object","properties":{"scheduledAt":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","description":"When to publish, ISO-8601 with a timezone (2026-11-03T09:30:00Z or 2026-11-03T10:30:00+01:00). Must be in the future."},"firstComment":{"description":"LinkedIn only: replaces the saved first comment; empty clears it","type":"string","maxLength":1250}},"required":["scheduledAt"]},"ViralFormat":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"why":{"type":"string","description":"Why the shape works"},"question":{"type":"string","description":"What Postel asks before writing it: send the reply as `answer`"},"placeholder":{"type":"string"}},"required":["id","name","why","question","placeholder"]},"ViralPost":{"type":"object","properties":{"id":{"type":"string","description":"Postel's id for the viral post; use it to write your take"},"network":{"type":"string","enum":["x","youtube","tiktok","instagram"]},"url":{"type":"string","description":"The post on its network"},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Videos: the title"},"text":{"type":"string"},"author":{"type":"object","properties":{"handle":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]},"followers":{"anyOf":[{"type":"number"},{"type":"null"}]}},"required":["handle","name","followers"]},"postedAt":{"type":"string"},"views":{"anyOf":[{"type":"number"},{"type":"null"}]},"likes":{"anyOf":[{"type":"number"},{"type":"null"}]},"comments":{"anyOf":[{"type":"number"},{"type":"null"}]},"reposts":{"anyOf":[{"type":"number"},{"type":"null"}]},"outlierScore":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"How far it outran normal: X, views ÷ followers; videos, views ÷ the creator's usual (or followers, or the keyword's usual: see outlierBasis)"},"outlierBasis":{"anyOf":[{"type":"string","enum":["followers","usual","topic"]},{"type":"null"}]},"format":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"X: the post format it follows (GET /v1/viral/formats)"},"isShort":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Videos: short-form"}},"required":["id","network","url","title","text","author","postedAt","views","likes","comments","reposts","outlierScore","outlierBasis","format","isShort"]}}},"tags":[{"name":"Account","description":"Who an API key acts for. Use it to test a connection."},{"name":"Channels","description":"The networks Postel posts to and whether the workspace has each one connected."},{"name":"Posts","description":"Drafts, scheduled and published posts. Create one, schedule a draft, or publish it now."},{"name":"Writing","description":"Postel writes posts in your voice and saves them as drafts. Each draft uses 1 AI credit."},{"name":"Viral","description":"The viral library: posts and videos that took off, and the formats they keep using."},{"name":"Events","description":"Polling triggers for automations: published and failed posts, drafts ready for review, and new viral posts in your niche."}],"paths":{"/v1/me":{"get":{"operationId":"getMe","tags":["Account"],"summary":"Get the key's account","description":"The person and workspace the API key acts for, and whether the plan can schedule and publish. Integrations call this to test a connection and use `label` as its name. Free on every plan.","responses":{"200":{"description":"The key's account","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"type":"object","properties":{"user":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"email":{"type":"string"}},"required":["id","name","email"]},"workspace":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"slug":{"anyOf":[{"type":"string"},{"type":"null"}]},"timezone":{"type":"string"}},"required":["id","name","slug","timezone"]},"canPublish":{"type":"boolean","description":"The plan includes scheduling and publishing"},"label":{"type":"string","description":"Name to show for this connection"}},"required":["user","workspace","canPublish","label"]},"examples":{"account":{"value":{"user":{"id":"usr_2b9f6c1e","name":"Robin Faraj","email":"robin@example.com"},"workspace":{"id":"org_7d41a0c3","name":"Acme","slug":"acme","timezone":"Europe/Berlin"},"canPublish":true,"label":"Acme (robin@example.com)"}}}}}},"401":{"description":"No valid Postel API key","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"invalid_api_key":{"value":{"error":"This API key isn't valid. Create one in Postel under Settings → API & MCP and paste it in.","code":"invalid_api_key","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}},"429":{"description":"Too many requests (60 a minute per key; 3 posts being written at once per workspace)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"rate_limited":{"value":{"error":"Too many requests with this API key. Try again in 12 seconds.","code":"rate_limited","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}},"500":{"description":"Something broke on Postel's side; never an HTML page","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"server_error":{"value":{"error":"Something went wrong on Postel's side. Try again in a minute.","code":"server_error","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}}}}},"/v1/channels":{"get":{"operationId":"listChannels","tags":["Channels"],"summary":"List channels","description":"Every network Postel can post to, and whether this workspace has it connected. Connected channels come first, the order a dropdown should show. Use a channel's `id` as `platform` when you create a post.","responses":{"200":{"description":"Channels","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"type":"object","properties":{"channels":{"type":"array","items":{"$ref":"#/components/schemas/Channel"}}},"required":["channels"]},"examples":{"channels":{"value":{"channels":[{"id":"linkedin","name":"LinkedIn","account":"Robin Faraj","connected":true,"status":"ok","label":"LinkedIn (Robin Faraj)"},{"id":"x","name":"X","account":"@postelapp","connected":true,"status":"expiring","label":"X (@postelapp)"},{"id":"bluesky","name":"Bluesky","account":null,"connected":false,"status":"disconnected","label":"Bluesky (not connected)"}]}}}}}},"401":{"description":"No valid Postel API key","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"invalid_api_key":{"value":{"error":"This API key isn't valid. Create one in Postel under Settings → API & MCP and paste it in.","code":"invalid_api_key","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}},"429":{"description":"Too many requests (60 a minute per key; 3 posts being written at once per workspace)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"rate_limited":{"value":{"error":"Too many requests with this API key. Try again in 12 seconds.","code":"rate_limited","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}},"500":{"description":"Something broke on Postel's side; never an HTML page","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"server_error":{"value":{"error":"Something went wrong on Postel's side. Try again in a minute.","code":"server_error","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}}}}},"/v1/posts":{"get":{"operationId":"listPosts","tags":["Posts"],"summary":"List posts","description":"The workspace's posts, newest first, 25 a page. Filter by `status` and `platform`. When `nextCursor` isn't null, pass it as `cursor` for the next page.","responses":{"200":{"description":"Posts","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"type":"object","properties":{"posts":{"type":"array","items":{"$ref":"#/components/schemas/Post"}},"nextCursor":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Pass as `cursor` for the next page; null on the last"}},"required":["posts","nextCursor"]},"examples":{"page":{"value":{"posts":[{"id":"cmhb0y2kq0003lb08m7d1c9va","platform":"x","status":"scheduled","text":"Most launch posts fail for one reason.\n\nThey explain the product before the problem.","thread":["Most launch posts fail for one reason.","They explain the product before the problem."],"firstComment":null,"mediaCount":0,"scheduledAt":"2026-11-05T09:30:00.000Z","publishedAt":null,"url":null,"failure":null,"createdAt":"2026-11-03T09:14:02.000Z","updatedAt":"2026-11-03T09:14:03.000Z"},{"id":"cmhb0x7rd0001lb08q2k5w3zt","platform":"linkedin","status":"draft","text":"We shipped our public API today. Three things we learned building it:","thread":["We shipped our public API today. Three things we learned building it:"],"firstComment":"Docs: https://www.postel.app/docs/api","mediaCount":0,"scheduledAt":null,"publishedAt":null,"url":null,"failure":null,"createdAt":"2026-11-03T09:12:44.000Z","updatedAt":"2026-11-03T09:12:44.000Z"}],"nextCursor":"cmhb0x7rd0001lb08q2k5w3zt"}}}}}},"400":{"description":"The request isn't valid","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"invalid_request":{"value":{"error":"text: Send either text or thread","code":"invalid_request","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}},"text_too_long":{"value":{"error":"The post is 312 characters; X allows 280. Longer posts need X Premium; split it into a thread with a line of --- instead.","code":"text_too_long","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}},"401":{"description":"No valid Postel API key","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"invalid_api_key":{"value":{"error":"This API key isn't valid. Create one in Postel under Settings → API & MCP and paste it in.","code":"invalid_api_key","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}},"429":{"description":"Too many requests (60 a minute per key; 3 posts being written at once per workspace)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"rate_limited":{"value":{"error":"Too many requests with this API key. Try again in 12 seconds.","code":"rate_limited","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}},"500":{"description":"Something broke on Postel's side; never an HTML page","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"server_error":{"value":{"error":"Something went wrong on Postel's side. Try again in a minute.","code":"server_error","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}}},"parameters":[{"in":"query","name":"status","schema":{"type":"string","enum":["draft","scheduled","published","pending_approval"]},"description":"Only posts with this status"},{"in":"query","name":"platform","schema":{"type":"string","enum":["x","linkedin","linkedin_page","bluesky","mastodon","threads","youtube","instagram","tiktok"]},"description":"Only posts for this network"},{"in":"query","name":"limit","schema":{"examples":[25],"type":"integer","minimum":1,"maximum":100},"description":"Posts per page, 1 to 100. Default 25"},{"in":"query","name":"cursor","schema":{"examples":["cmhb0x7rd0001lb08q2k5w3zt"],"type":"string"},"description":"nextCursor from the previous page"}]},"post":{"operationId":"createPost","tags":["Posts"],"summary":"Create a post","description":"Saves a draft; with `scheduledAt` it's scheduled, with `publishNow` Postel publishes it within a minute (the response comes back at once with status scheduled; post.published or post.failed events say how it went). Scheduling and publishing need a paid plan; drafts work on every plan.\n\nWhen the workspace needs approval, a scheduled post waits for an owner or admin (status pending_approval) and `publishNow` is refused with `approval_required`. Text is checked against the account's real limit per part before anything is saved. Media URLs are downloaded and attached to the first part. If scheduling fails, the draft is removed again, so a retry can't double up.\n\nSend an `Idempotency-Key` header to make retries safe: the same key within 24 hours returns the first response instead of creating another post.","parameters":[{"in":"header","name":"Idempotency-Key","required":false,"description":"Makes retries safe. The first request with a key runs; the same key from the same workspace within 24 hours gets that first response back (with `Idempotent-Replayed: true`) instead of creating again. Use a value unique to the run, up to 255 characters. Only successes are remembered. A key still running answers 409 `request_in_progress`; a key reused with a different body answers 422 `idempotency_key_reused`.","schema":{"type":"string","maxLength":255},"example":"zap-7f3a9c-run-2026-11-03T09:00"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePostRequest"},"examples":{"draft":{"summary":"Save a draft","value":{"platform":"linkedin","text":"We shipped our public API today. Three things we learned building it:","firstComment":"Docs: https://www.postel.app/docs/api"}},"thread":{"summary":"Schedule an X thread","value":{"platform":"x","thread":["Most launch posts fail for one reason.","They explain the product before the problem."],"scheduledAt":"2026-11-05T10:30:00+01:00"}},"publishNow":{"summary":"Publish now, with an image","value":{"platform":"x","text":"Shipping beats polishing. Every time.","mediaUrls":["https://example.com/launch.png"],"publishNow":true}}}}}},"responses":{"201":{"description":"The post","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Idempotent-Replayed":{"$ref":"#/components/headers/IdempotentReplayed"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostResult"},"examples":{"draft":{"summary":"A draft","value":{"id":"cmhb0x7rd0001lb08q2k5w3zt","platform":"linkedin","status":"draft","text":"We shipped our public API today. Three things we learned building it:","thread":["We shipped our public API today. Three things we learned building it:"],"firstComment":"Docs: https://www.postel.app/docs/api","mediaCount":0,"scheduledAt":null,"publishedAt":null,"url":null,"failure":null,"createdAt":"2026-11-03T09:12:44.000Z","updatedAt":"2026-11-03T09:12:44.000Z"}},"scheduled":{"summary":"A scheduled thread","value":{"id":"cmhb0y2kq0003lb08m7d1c9va","platform":"x","status":"scheduled","text":"Most launch posts fail for one reason.\n\nThey explain the product before the problem.","thread":["Most launch posts fail for one reason.","They explain the product before the problem."],"firstComment":null,"mediaCount":0,"scheduledAt":"2026-11-05T09:30:00.000Z","publishedAt":null,"url":null,"failure":null,"createdAt":"2026-11-03T09:14:02.000Z","updatedAt":"2026-11-03T09:14:03.000Z"}},"publishing":{"summary":"Publishing now","value":{"id":"cmhb0zq1m0005lb08t4n2x8rk","platform":"x","status":"scheduled","text":"Shipping beats polishing. Every time.","thread":["Shipping beats polishing. Every time."],"firstComment":null,"mediaCount":1,"scheduledAt":"2026-11-03T09:15:15.000Z","publishedAt":null,"url":null,"failure":null,"createdAt":"2026-11-02T17:40:11.000Z","updatedAt":"2026-11-03T08:00:04.000Z","message":"Postel is publishing it now. It goes out within a minute; the Post Published or Post Failed trigger tells you how it went."}}}}}},"400":{"description":"The request isn't valid","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"invalid_request":{"value":{"error":"text: Send either text or thread","code":"invalid_request","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}},"text_too_long":{"value":{"error":"The post is 312 characters; X allows 280. Longer posts need X Premium; split it into a thread with a line of --- instead.","code":"text_too_long","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}},"401":{"description":"No valid Postel API key","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"invalid_api_key":{"value":{"error":"This API key isn't valid. Create one in Postel under Settings → API & MCP and paste it in.","code":"invalid_api_key","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}},"402":{"description":"Needs a paid plan, or AI credits ran out","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"plan_required":{"value":{"error":"Scheduling and publishing need a paid Postel plan. Pick one at https://www.postel.app/app/acme/settings/billing, then run this step again. Saving drafts works on every plan.","code":"plan_required","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}},"out_of_credits":{"value":{"error":"Out of AI credits. Upgrade your Postel plan for more credits.","code":"out_of_credits","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}},"409":{"description":"The post or channel isn't in a state that allows this","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"channel_not_connected":{"value":{"error":"Connect LinkedIn in Postel (Settings → Channels), then run this step again.","code":"channel_not_connected","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}},"not_a_draft":{"value":{"error":"Only drafts can be scheduled; this post is scheduled.","code":"not_a_draft","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}},"request_in_progress":{"value":{"error":"A request with this Idempotency-Key is still running. Wait for it to finish.","code":"request_in_progress","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}},"422":{"description":"The network refused the post, or an Idempotency-Key was reused for a different request","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"idempotency_key_reused":{"value":{"error":"This Idempotency-Key was already used for a different request. Use a new key.","code":"idempotency_key_reused","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}},"429":{"description":"Too many requests (60 a minute per key; 3 posts being written at once per workspace)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"rate_limited":{"value":{"error":"Too many requests with this API key. Try again in 12 seconds.","code":"rate_limited","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}},"500":{"description":"Something broke on Postel's side; never an HTML page","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"server_error":{"value":{"error":"Something went wrong on Postel's side. Try again in a minute.","code":"server_error","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}},"502":{"description":"The network, scheduler or writer failed; try again","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"schedule_failed":{"value":{"error":"Postel couldn't schedule this post just now. Try again in a minute.","code":"schedule_failed","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}},"generate_failed":{"value":{"error":"Postel couldn't write the post just now. Nothing was charged; try again in a minute.","code":"generate_failed","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}}}}},"/v1/posts/generate":{"post":{"operationId":"generatePost","tags":["Writing"],"summary":"Write a post in your voice","description":"Postel writes a post from your prompt in your own voice and saves it as a draft. Uses 1 AI credit (none when the workspace uses its own AI key); works on every plan. Takes 5 to 30 seconds, so give the request a 60-second timeout. Up to 3 at a time per workspace; a fourth gets 429. Send an `Idempotency-Key` header so a retried step returns the first draft instead of writing (and charging) again.","parameters":[{"in":"header","name":"Idempotency-Key","required":false,"description":"Makes retries safe. The first request with a key runs; the same key from the same workspace within 24 hours gets that first response back (with `Idempotent-Replayed: true`) instead of creating again. Use a value unique to the run, up to 255 characters. Only successes are remembered. A key still running answers 409 `request_in_progress`; a key reused with a different body answers 422 `idempotency_key_reused`.","schema":{"type":"string","maxLength":255},"example":"zap-7f3a9c-run-2026-11-03T09:00"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GeneratePostRequest"},"examples":{"post":{"summary":"One LinkedIn post","value":{"prompt":"Why we ship a public API before a mobile app","platform":"linkedin"}},"thread":{"summary":"An X thread","value":{"prompt":"Notes from our launch: 3 things that moved signups","platform":"x","thread":true}}}}}},"responses":{"201":{"description":"The new draft","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Idempotent-Replayed":{"$ref":"#/components/headers/IdempotentReplayed"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Post"},"examples":{"draft":{"value":{"id":"cmhb0x7rd0001lb08q2k5w3zt","platform":"linkedin","status":"draft","text":"We shipped our public API today. Three things we learned building it:","thread":["We shipped our public API today. Three things we learned building it:"],"firstComment":"Docs: https://www.postel.app/docs/api","mediaCount":0,"scheduledAt":null,"publishedAt":null,"url":null,"failure":null,"createdAt":"2026-11-03T09:12:44.000Z","updatedAt":"2026-11-03T09:12:44.000Z"}}}}}},"400":{"description":"The request isn't valid","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"invalid_request":{"value":{"error":"text: Send either text or thread","code":"invalid_request","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}},"text_too_long":{"value":{"error":"The post is 312 characters; X allows 280. Longer posts need X Premium; split it into a thread with a line of --- instead.","code":"text_too_long","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}},"401":{"description":"No valid Postel API key","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"invalid_api_key":{"value":{"error":"This API key isn't valid. Create one in Postel under Settings → API & MCP and paste it in.","code":"invalid_api_key","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}},"402":{"description":"Needs a paid plan, or AI credits ran out","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"plan_required":{"value":{"error":"Scheduling and publishing need a paid Postel plan. Pick one at https://www.postel.app/app/acme/settings/billing, then run this step again. Saving drafts works on every plan.","code":"plan_required","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}},"out_of_credits":{"value":{"error":"Out of AI credits. Upgrade your Postel plan for more credits.","code":"out_of_credits","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}},"409":{"description":"The post or channel isn't in a state that allows this","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"channel_not_connected":{"value":{"error":"Connect LinkedIn in Postel (Settings → Channels), then run this step again.","code":"channel_not_connected","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}},"not_a_draft":{"value":{"error":"Only drafts can be scheduled; this post is scheduled.","code":"not_a_draft","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}},"request_in_progress":{"value":{"error":"A request with this Idempotency-Key is still running. Wait for it to finish.","code":"request_in_progress","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}},"422":{"description":"The network refused the post, or an Idempotency-Key was reused for a different request","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"idempotency_key_reused":{"value":{"error":"This Idempotency-Key was already used for a different request. Use a new key.","code":"idempotency_key_reused","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}},"429":{"description":"Too many requests (60 a minute per key; 3 posts being written at once per workspace)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"rate_limited":{"value":{"error":"Too many requests with this API key. Try again in 12 seconds.","code":"rate_limited","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}},"500":{"description":"Something broke on Postel's side; never an HTML page","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"server_error":{"value":{"error":"Something went wrong on Postel's side. Try again in a minute.","code":"server_error","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}},"502":{"description":"The network, scheduler or writer failed; try again","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"schedule_failed":{"value":{"error":"Postel couldn't schedule this post just now. Try again in a minute.","code":"schedule_failed","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}},"generate_failed":{"value":{"error":"Postel couldn't write the post just now. Nothing was charged; try again in a minute.","code":"generate_failed","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}}}}},"/v1/posts/{id}":{"get":{"operationId":"getPost","tags":["Posts"],"summary":"Get a post","description":"One post by id, with its status, publish time, link once published, and `failure` when the last publish failed.","parameters":[{"in":"path","name":"id","required":true,"description":"The post's id","schema":{"type":"string"},"example":"cmhb0zq1m0005lb08t4n2x8rk"}],"responses":{"200":{"description":"The post","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Post"},"examples":{"published":{"value":{"id":"cmhb0zq1m0005lb08t4n2x8rk","platform":"x","status":"published","text":"Shipping beats polishing. Every time.","thread":["Shipping beats polishing. Every time."],"firstComment":null,"mediaCount":1,"scheduledAt":"2026-11-03T08:00:00.000Z","publishedAt":"2026-11-03T08:00:04.000Z","url":"https://x.com/i/status/1853421987654321098","failure":null,"createdAt":"2026-11-02T17:40:11.000Z","updatedAt":"2026-11-03T08:00:04.000Z"}}}}}},"401":{"description":"No valid Postel API key","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"invalid_api_key":{"value":{"error":"This API key isn't valid. Create one in Postel under Settings → API & MCP and paste it in.","code":"invalid_api_key","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}},"404":{"description":"No such post in this workspace (or viral post in the library)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"not_found":{"value":{"error":"There's no post with this id in this workspace.","code":"not_found","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}},"429":{"description":"Too many requests (60 a minute per key; 3 posts being written at once per workspace)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"rate_limited":{"value":{"error":"Too many requests with this API key. Try again in 12 seconds.","code":"rate_limited","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}},"500":{"description":"Something broke on Postel's side; never an HTML page","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"server_error":{"value":{"error":"Something went wrong on Postel's side. Try again in a minute.","code":"server_error","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}}}}},"/v1/posts/{id}/schedule":{"post":{"operationId":"schedulePost","tags":["Posts"],"summary":"Schedule a draft","description":"Schedules a draft for `scheduledAt`. Needs a paid plan and a connected channel. When the workspace needs approval, the post waits for an owner or admin (status pending_approval, with a `message`). Only drafts can be scheduled: a scheduled post keeps its slot (409 `not_a_draft`), and two posts can't share the exact minute on one network (409 `time_taken`).","parameters":[{"in":"path","name":"id","required":true,"description":"The draft's id","schema":{"type":"string"},"example":"cmhb0x7rd0001lb08q2k5w3zt"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SchedulePostRequest"},"examples":{"schedule":{"summary":"Schedule for a time","value":{"scheduledAt":"2026-11-05T09:30:00Z"}},"withComment":{"summary":"LinkedIn, with a first comment","value":{"scheduledAt":"2026-11-05T10:30:00+01:00","firstComment":"Docs: https://www.postel.app/docs/api"}}}}}},"responses":{"200":{"description":"The scheduled post","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostResult"},"examples":{"scheduled":{"value":{"id":"cmhb0x7rd0001lb08q2k5w3zt","platform":"linkedin","status":"scheduled","text":"We shipped our public API today. Three things we learned building it:","thread":["We shipped our public API today. Three things we learned building it:"],"firstComment":"Docs: https://www.postel.app/docs/api","mediaCount":0,"scheduledAt":"2026-11-05T09:30:00.000Z","publishedAt":null,"url":null,"failure":null,"createdAt":"2026-11-03T09:12:44.000Z","updatedAt":"2026-11-03T09:12:44.000Z"}}}}}},"400":{"description":"The request isn't valid","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"invalid_request":{"value":{"error":"text: Send either text or thread","code":"invalid_request","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}},"text_too_long":{"value":{"error":"The post is 312 characters; X allows 280. Longer posts need X Premium; split it into a thread with a line of --- instead.","code":"text_too_long","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}},"401":{"description":"No valid Postel API key","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"invalid_api_key":{"value":{"error":"This API key isn't valid. Create one in Postel under Settings → API & MCP and paste it in.","code":"invalid_api_key","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}},"402":{"description":"Needs a paid plan, or AI credits ran out","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"plan_required":{"value":{"error":"Scheduling and publishing need a paid Postel plan. Pick one at https://www.postel.app/app/acme/settings/billing, then run this step again. Saving drafts works on every plan.","code":"plan_required","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}},"out_of_credits":{"value":{"error":"Out of AI credits. Upgrade your Postel plan for more credits.","code":"out_of_credits","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}},"404":{"description":"No such post in this workspace (or viral post in the library)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"not_found":{"value":{"error":"There's no post with this id in this workspace.","code":"not_found","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}},"409":{"description":"The post or channel isn't in a state that allows this","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"channel_not_connected":{"value":{"error":"Connect LinkedIn in Postel (Settings → Channels), then run this step again.","code":"channel_not_connected","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}},"not_a_draft":{"value":{"error":"Only drafts can be scheduled; this post is scheduled.","code":"not_a_draft","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}},"request_in_progress":{"value":{"error":"A request with this Idempotency-Key is still running. Wait for it to finish.","code":"request_in_progress","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}},"422":{"description":"The network refused the post, or an Idempotency-Key was reused for a different request","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"idempotency_key_reused":{"value":{"error":"This Idempotency-Key was already used for a different request. Use a new key.","code":"idempotency_key_reused","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}},"429":{"description":"Too many requests (60 a minute per key; 3 posts being written at once per workspace)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"rate_limited":{"value":{"error":"Too many requests with this API key. Try again in 12 seconds.","code":"rate_limited","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}},"500":{"description":"Something broke on Postel's side; never an HTML page","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"server_error":{"value":{"error":"Something went wrong on Postel's side. Try again in a minute.","code":"server_error","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}},"502":{"description":"The network, scheduler or writer failed; try again","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"schedule_failed":{"value":{"error":"Postel couldn't schedule this post just now. Try again in a minute.","code":"schedule_failed","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}},"generate_failed":{"value":{"error":"Postel couldn't write the post just now. Nothing was charged; try again in a minute.","code":"generate_failed","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}}}}},"/v1/posts/{id}/publish":{"post":{"operationId":"publishPost","tags":["Posts"],"summary":"Publish a draft now","description":"Needs a paid plan. Postel publishes the draft within a minute; the response comes back at once (202, status scheduled) and post.published or post.failed events say how it went. Publishing never runs inside the request, so a client that times out and retries can't post twice. Refused when the workspace needs approval (409 `approval_required`): schedule the post instead.","parameters":[{"in":"path","name":"id","required":true,"description":"The draft's id","schema":{"type":"string"},"example":"cmhb0x7rd0001lb08q2k5w3zt"}],"responses":{"202":{"description":"Queued to go out now","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostResult"},"examples":{"publishing":{"value":{"id":"cmhb0x7rd0001lb08q2k5w3zt","platform":"linkedin","status":"scheduled","text":"We shipped our public API today. Three things we learned building it:","thread":["We shipped our public API today. Three things we learned building it:"],"firstComment":"Docs: https://www.postel.app/docs/api","mediaCount":0,"scheduledAt":"2026-11-03T09:15:15.000Z","publishedAt":null,"url":null,"failure":null,"createdAt":"2026-11-03T09:12:44.000Z","updatedAt":"2026-11-03T09:12:44.000Z","message":"Postel is publishing it now. It goes out within a minute; the Post Published or Post Failed trigger tells you how it went."}}}}}},"401":{"description":"No valid Postel API key","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"invalid_api_key":{"value":{"error":"This API key isn't valid. Create one in Postel under Settings → API & MCP and paste it in.","code":"invalid_api_key","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}},"402":{"description":"Needs a paid plan, or AI credits ran out","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"plan_required":{"value":{"error":"Scheduling and publishing need a paid Postel plan. Pick one at https://www.postel.app/app/acme/settings/billing, then run this step again. Saving drafts works on every plan.","code":"plan_required","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}},"out_of_credits":{"value":{"error":"Out of AI credits. Upgrade your Postel plan for more credits.","code":"out_of_credits","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}},"404":{"description":"No such post in this workspace (or viral post in the library)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"not_found":{"value":{"error":"There's no post with this id in this workspace.","code":"not_found","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}},"409":{"description":"The post or channel isn't in a state that allows this","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"channel_not_connected":{"value":{"error":"Connect LinkedIn in Postel (Settings → Channels), then run this step again.","code":"channel_not_connected","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}},"not_a_draft":{"value":{"error":"Only drafts can be scheduled; this post is scheduled.","code":"not_a_draft","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}},"request_in_progress":{"value":{"error":"A request with this Idempotency-Key is still running. Wait for it to finish.","code":"request_in_progress","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}},"422":{"description":"The network refused the post, or an Idempotency-Key was reused for a different request","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"idempotency_key_reused":{"value":{"error":"This Idempotency-Key was already used for a different request. Use a new key.","code":"idempotency_key_reused","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}},"429":{"description":"Too many requests (60 a minute per key; 3 posts being written at once per workspace)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"rate_limited":{"value":{"error":"Too many requests with this API key. Try again in 12 seconds.","code":"rate_limited","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}},"500":{"description":"Something broke on Postel's side; never an HTML page","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"server_error":{"value":{"error":"Something went wrong on Postel's side. Try again in a minute.","code":"server_error","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}},"502":{"description":"The network, scheduler or writer failed; try again","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"schedule_failed":{"value":{"error":"Postel couldn't schedule this post just now. Try again in a minute.","code":"schedule_failed","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}},"generate_failed":{"value":{"error":"Postel couldn't write the post just now. Nothing was charged; try again in a minute.","code":"generate_failed","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}}}}},"/v1/events":{"get":{"operationId":"listEvents","tags":["Events"],"summary":"List events","description":"Polling triggers, newest first, one `type` at a time. Each event's `id` is unique per occurrence: dedupe on it. Poll every few minutes with `since` for only newer events; page back with `before` while pages come back full.\n\n- `post.published`: a post went out (replies Postel sent are left out).\n- `post.failed`: a publish failed. A post that fails twice fires twice.\n- `draft.ready`: a post Postel wrote for you is waiting in Review (plans with For You).\n- `viral.new`: a post taking off in your niche (Professional, Team and Enterprise; empty otherwise).","responses":{"200":{"description":"Events","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"type":"object","properties":{"events":{"type":"array","items":{"$ref":"#/components/schemas/Event"}}},"required":["events"]},"examples":{"published":{"summary":"post.published","value":{"events":[{"id":"post.published:cmhb0zq1m0005lb08t4n2x8rk","type":"post.published","occurredAt":"2026-11-03T08:00:04.000Z","post":{"id":"cmhb0zq1m0005lb08t4n2x8rk","platform":"x","status":"published","text":"Shipping beats polishing. Every time.","thread":["Shipping beats polishing. Every time."],"firstComment":null,"mediaCount":1,"scheduledAt":"2026-11-03T08:00:00.000Z","publishedAt":"2026-11-03T08:00:04.000Z","url":"https://x.com/i/status/1853421987654321098","failure":null,"createdAt":"2026-11-02T17:40:11.000Z","updatedAt":"2026-11-03T08:00:04.000Z"},"draft":null,"viralPost":null}]}},"draftReady":{"summary":"draft.ready","value":{"events":[{"id":"draft.ready:cmhb1a9xe0007lb08w2p4k6qd:2026-11-03T06:00:00.000Z","type":"draft.ready","occurredAt":"2026-11-03T06:00:00.000Z","post":null,"draft":{"id":"cmhb1a9xe0007lb08w2p4k6qd","platform":"linkedin","text":"Your first 100 users won't come from launch day.","writtenAt":"2026-11-03T06:00:00.000Z","plannedFor":"2026-11-04T08:30:00.000Z","reviewUrl":"https://www.postel.app/app/acme/review"},"viralPost":null}]}}}}}},"400":{"description":"The request isn't valid","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"invalid_request":{"value":{"error":"text: Send either text or thread","code":"invalid_request","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}},"text_too_long":{"value":{"error":"The post is 312 characters; X allows 280. Longer posts need X Premium; split it into a thread with a line of --- instead.","code":"text_too_long","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}},"401":{"description":"No valid Postel API key","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"invalid_api_key":{"value":{"error":"This API key isn't valid. Create one in Postel under Settings → API & MCP and paste it in.","code":"invalid_api_key","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}},"429":{"description":"Too many requests (60 a minute per key; 3 posts being written at once per workspace)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"rate_limited":{"value":{"error":"Too many requests with this API key. Try again in 12 seconds.","code":"rate_limited","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}},"500":{"description":"Something broke on Postel's side; never an HTML page","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"server_error":{"value":{"error":"Something went wrong on Postel's side. Try again in a minute.","code":"server_error","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}}},"parameters":[{"in":"query","name":"type","schema":{"type":"string","enum":["post.published","post.failed","draft.ready","viral.new"]},"required":true,"description":"post.published, post.failed, draft.ready (a post Postel wrote for you is waiting in Review), or viral.new (a post taking off in your niche)"},{"in":"query","name":"since","schema":{"examples":["2026-11-03T08:00:00Z"],"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"description":"Only events after this time"},{"in":"query","name":"before","schema":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"description":"Page back: events at or before this time (pass the oldest occurredAt you got; dedupe on id)"},{"in":"query","name":"limit","schema":{"type":"integer","minimum":1,"maximum":100},"description":"Events per page, 1 to 100. Default 25"}]}},"/v1/viral/posts":{"get":{"operationId":"listViralPosts","tags":["Viral"],"summary":"Find viral posts","description":"Posts and videos that took off, from individual creators (brands, politicians and aggregator pages are kept out). Most viewed first. Search X posts by topic, or YouTube, TikTok and Instagram videos. Read-only and free on every plan; 30 searches a minute per workspace.","responses":{"200":{"description":"Viral posts","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"type":"object","properties":{"posts":{"type":"array","items":{"$ref":"#/components/schemas/ViralPost"}}},"required":["posts"]},"examples":{"posts":{"value":{"posts":[{"id":"1853398412345678901","network":"x","url":"https://x.com/jane/status/1853398412345678901","title":null,"text":"I deleted our onboarding and signups went up 40%.\n\nHere's what we replaced it with:","author":{"handle":"jane","name":"Jane Park","followers":4200},"postedAt":"2026-11-02T14:05:00.000Z","views":389000,"likes":5100,"comments":312,"reposts":640,"outlierScore":92.6,"outlierBasis":"followers","format":"the-numbers","isShort":null}]}}}}}},"400":{"description":"The request isn't valid","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"invalid_request":{"value":{"error":"text: Send either text or thread","code":"invalid_request","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}},"text_too_long":{"value":{"error":"The post is 312 characters; X allows 280. Longer posts need X Premium; split it into a thread with a line of --- instead.","code":"text_too_long","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}},"401":{"description":"No valid Postel API key","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"invalid_api_key":{"value":{"error":"This API key isn't valid. Create one in Postel under Settings → API & MCP and paste it in.","code":"invalid_api_key","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}},"429":{"description":"Too many requests (60 a minute per key; 3 posts being written at once per workspace)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"rate_limited":{"value":{"error":"Too many requests with this API key. Try again in 12 seconds.","code":"rate_limited","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}},"500":{"description":"Something broke on Postel's side; never an HTML page","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"server_error":{"value":{"error":"Something went wrong on Postel's side. Try again in a minute.","code":"server_error","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}}},"parameters":[{"in":"query","name":"query","schema":{"examples":["cold email"],"type":"string","maxLength":200},"description":"Topic or keywords; leave out to browse what's doing best"},{"in":"query","name":"network","schema":{"default":"x","type":"string","enum":["x","youtube","tiktok","instagram"]},"description":"x (posts), or youtube, tiktok, instagram (videos)"},{"in":"query","name":"format","schema":{"type":"string","maxLength":80},"description":"X: only posts in this format (an id from GET /v1/viral/formats)"},{"in":"query","name":"length","schema":{"type":"string","enum":["short","long"]},"description":"Videos: short-form or long-form"},{"in":"query","name":"days","schema":{"default":7,"type":"integer","minimum":1,"maximum":60},"description":"How far back to look"},{"in":"query","name":"minOutlierScore","schema":{"type":"number","minimum":0,"maximum":100000},"description":"Only posts that outran normal by at least this much (X: views ÷ followers; videos: × the creator's usual)"},{"in":"query","name":"minViews","schema":{"type":"integer","minimum":0,"maximum":9007199254740991},"description":"Only posts with at least this many views"},{"in":"query","name":"limit","schema":{"default":20,"type":"integer","minimum":1,"maximum":50},"description":"Posts to return, 1 to 50"}]}},"/v1/viral/posts/{id}":{"get":{"operationId":"getViralPost","tags":["Viral"],"summary":"Get a viral post","description":"One X post or video from the viral library, by its id.","parameters":[{"in":"path","name":"id","required":true,"description":"The viral post's `id` from GET /v1/viral/posts","schema":{"type":"string"},"example":"1853398412345678901"}],"responses":{"200":{"description":"The viral post","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ViralPost"},"examples":{"post":{"value":{"id":"1853398412345678901","network":"x","url":"https://x.com/jane/status/1853398412345678901","title":null,"text":"I deleted our onboarding and signups went up 40%.\n\nHere's what we replaced it with:","author":{"handle":"jane","name":"Jane Park","followers":4200},"postedAt":"2026-11-02T14:05:00.000Z","views":389000,"likes":5100,"comments":312,"reposts":640,"outlierScore":92.6,"outlierBasis":"followers","format":"the-numbers","isShort":null}}}}}},"401":{"description":"No valid Postel API key","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"invalid_api_key":{"value":{"error":"This API key isn't valid. Create one in Postel under Settings → API & MCP and paste it in.","code":"invalid_api_key","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}},"404":{"description":"No such post in this workspace (or viral post in the library)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"not_found":{"value":{"error":"There's no post with this id in this workspace.","code":"not_found","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}},"429":{"description":"Too many requests (60 a minute per key; 3 posts being written at once per workspace)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"rate_limited":{"value":{"error":"Too many requests with this API key. Try again in 12 seconds.","code":"rate_limited","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}},"500":{"description":"Something broke on Postel's side; never an HTML page","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"server_error":{"value":{"error":"Something went wrong on Postel's side. Try again in a minute.","code":"server_error","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}}}}},"/v1/viral/formats":{"get":{"operationId":"listViralFormats","tags":["Viral"],"summary":"List post formats","description":"The shapes viral posts keep using, each with the question Postel asks before writing one. Write one with POST /v1/viral/takes and your answer to its question.","responses":{"200":{"description":"Formats","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"type":"object","properties":{"formats":{"type":"array","items":{"$ref":"#/components/schemas/ViralFormat"}}},"required":["formats"]},"examples":{"formats":{"value":{"formats":[{"id":"the-numbers","name":"The numbers","why":"A real figure stops the scroll, and the decisions behind it are what readers save.","question":"What's a number from your work you'd stand behind?","placeholder":"40 demo calls in October, all from replying to posts, about 20 minutes a day"}]}}}}}},"401":{"description":"No valid Postel API key","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"invalid_api_key":{"value":{"error":"This API key isn't valid. Create one in Postel under Settings → API & MCP and paste it in.","code":"invalid_api_key","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}},"429":{"description":"Too many requests (60 a minute per key; 3 posts being written at once per workspace)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"rate_limited":{"value":{"error":"Too many requests with this API key. Try again in 12 seconds.","code":"rate_limited","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}},"500":{"description":"Something broke on Postel's side; never an HTML page","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"server_error":{"value":{"error":"Something went wrong on Postel's side. Try again in a minute.","code":"server_error","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}}}}},"/v1/viral/takes":{"post":{"operationId":"createViralTake","tags":["Writing"],"summary":"Write my take","description":"Your own post in your voice from a viral X post or a format, saved as a draft per network (the Viral posts page's Use as template). Works on every plan. One AI credit per network; a network whose draft fails costs nothing. Takes 10 to 40 seconds, so give the request a 60-second timeout. Videos aren't supported yet (422 `not_supported`). Shares the 3-at-once writing slots with POST /v1/posts/generate. Send an `Idempotency-Key` only when it is unique per run.","parameters":[{"in":"header","name":"Idempotency-Key","required":false,"description":"Makes retries safe. The first request with a key runs; the same key from the same workspace within 24 hours gets that first response back (with `Idempotent-Replayed: true`) instead of creating again. Use a value unique to the run, up to 255 characters. Only successes are remembered. A key still running answers 409 `request_in_progress`; a key reused with a different body answers 422 `idempotency_key_reused`.","schema":{"type":"string","maxLength":255},"example":"zap-7f3a9c-run-2026-11-03T09:00"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateViralTakeRequest"},"examples":{"fromPost":{"summary":"From a viral X post","value":{"viralPostId":"1853398412345678901","answer":"We cut onboarding to one screen in March","platforms":["linkedin","x"]}},"fromFormat":{"summary":"From a format","value":{"format":"the-numbers","answer":"40 demo calls in October, all from replying to posts, about 20 minutes a day","platforms":["linkedin"]}}}}}},"responses":{"201":{"description":"The drafts. `post` is the first of `posts`","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Idempotent-Replayed":{"$ref":"#/components/headers/IdempotentReplayed"}},"content":{"application/json":{"schema":{"type":"object","properties":{"post":{"$ref":"#/components/schemas/Post"},"posts":{"type":"array","items":{"$ref":"#/components/schemas/Post"}},"failed":{"type":"array","items":{"type":"string"},"description":"Networks whose draft couldn't be written"}},"required":["post","posts","failed"]},"examples":{"drafts":{"value":{"post":{"id":"cmhb0x7rd0001lb08q2k5w3zt","platform":"linkedin","status":"draft","text":"We shipped our public API today. Three things we learned building it:","thread":["We shipped our public API today. Three things we learned building it:"],"firstComment":"Docs: https://www.postel.app/docs/api","mediaCount":0,"scheduledAt":null,"publishedAt":null,"url":null,"failure":null,"createdAt":"2026-11-03T09:12:44.000Z","updatedAt":"2026-11-03T09:12:44.000Z"},"posts":[{"id":"cmhb0x7rd0001lb08q2k5w3zt","platform":"linkedin","status":"draft","text":"We shipped our public API today. Three things we learned building it:","thread":["We shipped our public API today. Three things we learned building it:"],"firstComment":"Docs: https://www.postel.app/docs/api","mediaCount":0,"scheduledAt":null,"publishedAt":null,"url":null,"failure":null,"createdAt":"2026-11-03T09:12:44.000Z","updatedAt":"2026-11-03T09:12:44.000Z"}],"failed":[]}}}}}},"400":{"description":"The request isn't valid","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"invalid_request":{"value":{"error":"text: Send either text or thread","code":"invalid_request","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}},"text_too_long":{"value":{"error":"The post is 312 characters; X allows 280. Longer posts need X Premium; split it into a thread with a line of --- instead.","code":"text_too_long","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}},"401":{"description":"No valid Postel API key","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"invalid_api_key":{"value":{"error":"This API key isn't valid. Create one in Postel under Settings → API & MCP and paste it in.","code":"invalid_api_key","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}},"402":{"description":"Needs a paid plan, or AI credits ran out","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"plan_required":{"value":{"error":"Scheduling and publishing need a paid Postel plan. Pick one at https://www.postel.app/app/acme/settings/billing, then run this step again. Saving drafts works on every plan.","code":"plan_required","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}},"out_of_credits":{"value":{"error":"Out of AI credits. Upgrade your Postel plan for more credits.","code":"out_of_credits","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}},"404":{"description":"No such post in this workspace (or viral post in the library)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"not_found":{"value":{"error":"There's no post with this id in this workspace.","code":"not_found","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}},"409":{"description":"The post or channel isn't in a state that allows this","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"channel_not_connected":{"value":{"error":"Connect LinkedIn in Postel (Settings → Channels), then run this step again.","code":"channel_not_connected","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}},"not_a_draft":{"value":{"error":"Only drafts can be scheduled; this post is scheduled.","code":"not_a_draft","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}},"request_in_progress":{"value":{"error":"A request with this Idempotency-Key is still running. Wait for it to finish.","code":"request_in_progress","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}},"422":{"description":"The network refused the post, or an Idempotency-Key was reused for a different request","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"idempotency_key_reused":{"value":{"error":"This Idempotency-Key was already used for a different request. Use a new key.","code":"idempotency_key_reused","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}},"429":{"description":"Too many requests (60 a minute per key; 3 posts being written at once per workspace)","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"},"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"rate_limited":{"value":{"error":"Too many requests with this API key. Try again in 12 seconds.","code":"rate_limited","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}},"500":{"description":"Something broke on Postel's side; never an HTML page","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"server_error":{"value":{"error":"Something went wrong on Postel's side. Try again in a minute.","code":"server_error","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}},"502":{"description":"The network, scheduler or writer failed; try again","headers":{"X-Request-Id":{"$ref":"#/components/headers/RequestId"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"schedule_failed":{"value":{"error":"Postel couldn't schedule this post just now. Try again in a minute.","code":"schedule_failed","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}},"generate_failed":{"value":{"error":"Postel couldn't write the post just now. Nothing was charged; try again in a minute.","code":"generate_failed","requestId":"req_8f14e45fceea167a5a36dedd4bea2543"}}}}}}}}}}}