Every API here is available over the APIs.io API and to AI agents over MCP.
{"openapi":"3.1.0","info":{"title":"PostOnce Public API","version":"1.0.0-alpha","description":"Public API for PostOnce paid users"},"servers":[{"url":"https://postonce.to/api/public"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"API Key","description":"Scoped API key issued in PostOnce Settings. OAuth authorization grants are not currently supported.","x-scopes":{"accounts:read":"List and read connected accounts.","accounts:write":"Connect and disconnect social accounts.","media:read":"Read uploaded media.","media:write":"Upload media.","posts:read":"Read posts and unpublished drafts.","posts:write":"Create, update, publish, cancel, retry, and delete posts and drafts.","workflows:read":"List and read crossposting workflows.","workflows:write":"Create, update, and delete crossposting workflows."}}},"parameters":{"accountId":{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Connected account ID."},"mediaId":{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Encoded `media_id` returned by the upload endpoint."},"draftId":{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Draft ID."},"postId":{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"},"description":"Aggregate post ID."},"targetPostId":{"name":"targetId","in":"path","required":true,"schema":{"type":"string"},"description":"Concrete destination row ID from `data.targets[].target_post_id`."},"workflowId":{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Workflow ID."},"platform":{"name":"platform","in":"path","required":true,"schema":{"type":"string","enum":["bluesky","facebook","farcaster","nostr","telegram","instagram","linkedin","pinterest","reddit","snapchat","threads","tiktok","twitter","vimeo","youtube"]},"description":"Social platform to connect."},"listLimit":{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100},"description":"Maximum number of items to return."},"listAfter":{"name":"after","in":"query","required":false,"schema":{"type":"string"},"description":"Cursor returned by a previous list response."}},"schemas":{"ErrorResponse":{"type":"object","properties":{"error":{"type":"object","required":["code","message"],"properties":{"hint":{"type":"string","description":"Suggested action to resolve the error."},"code":{"type":"string","example":"workflow_limit_reached"},"message":{"type":"string","example":"You have reached the maximum number of workflows for your plan."}}}}},"AuthErrorResponse":{"type":"object","properties":{"error":{"type":"object","properties":{"hint":{"type":"string","description":"Suggested action to resolve the error."},"code":{"type":"string","example":"invalid_api_key"},"message":{"type":"string","example":"Invalid API key."}}}}},"Account":{"type":"object","properties":{"id":{"type":"string"},"platform":{"type":"string","enum":["bluesky","facebook","farcaster","nostr","telegram","instagram","linkedin","pinterest","reddit","snapchat","threads","tiktok","twitter","vimeo","youtube"],"example":"twitter"},"username":{"type":"string","example":"nigelyong"},"avatar_url":{"type":"string","nullable":true},"status":{"type":"string","enum":["active","auth_required","disconnected"],"example":"active"},"capabilities":{"type":"object","nullable":true,"description":"Discoverable account capabilities used by both the public API and generated MCP tools.","properties":{"connection_mode":{"type":"string"},"destination":{"type":"boolean"},"source":{"type":"boolean"},"backfill":{"type":"boolean"},"text":{"type":"boolean"},"images":{"type":"boolean"},"video":{"type":"boolean"},"replies":{"type":"boolean"},"threads":{"type":"boolean"}}},"media_requirements":{"type":"object","nullable":true,"description":"Media input constraints from PostOnce's publishing validator. Check required_media before a text-only post. Platform-level guidance is not proof of current account authorization or delivery success. Format conversions remain subject to processing limits.","properties":{"supported_formats":{"type":"array","items":{"type":"string"}},"convertible_formats":{"type":"object","additionalProperties":{"type":"string"}},"max_size_bytes":{"type":"object","properties":{"image":{"type":"integer"},"video":{"type":"integer"},"gif":{"type":"integer"}}},"file_constraints":{"type":"object","description":"maxFiles, maxVideoFiles, maxImageFiles, optional maxGifFiles, allowMixedMedia and optional gifBehavior.","additionalProperties":true},"required_media":{"type":"object","nullable":true,"description":"When present: minFiles and acceptedTypes (image/video) define mandatory media, with errorCode and errorMessage.","additionalProperties":true},"max_hashtags":{"type":"integer","minimum":0,"nullable":true},"min_video_duration_seconds":{"type":"number","nullable":true},"max_video_duration_seconds":{"type":"number","nullable":true},"aspect_ratio":{"type":"object","nullable":true,"additionalProperties":true},"thumbnail":{"type":"object","nullable":true,"additionalProperties":true}}},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"ConnectAccountResponse":{"type":"object","properties":{"data":{"type":"object","properties":{"hosted_url":{"type":"string","nullable":true,"example":"https://postonce.to/dashboard/accounts?connect=twitter&source=public_api"},"authorization_url":{"type":"string","nullable":true,"description":"Five-minute browser connection URL bound to the API-key profile with signed state. OAuth platforms return a direct provider URL; Bluesky returns a PostOnce-hosted app-password form; Farcaster returns a PostOnce-hosted managed-signer approval flow; Nostr returns a PostOnce-hosted NIP-46 remote-signer flow; Telegram returns a PostOnce-hosted flow that adds @PostOnceBot as a channel admin.","example":"https://www.tiktok.com/v2/auth/authorize/?client_key=..."},"mode":{"type":"string","enum":["credential_form_url","managed_signer","remote_signer","oauth_authorization_url"],"example":"oauth_authorization_url"},"platform":{"type":"string","example":"twitter"}}}}},"DeleteResponse":{"type":"object","properties":{"data":{"type":"object","properties":{"deleted":{"type":"boolean","example":true},"id":{"type":"string"},"provider_revocation_required":{"type":"boolean","nullable":true,"description":"True when local PostOnce access is removed but the provider requires the user to revoke the remote signer separately."},"provider_revocation_url":{"type":"string","nullable":true},"message":{"type":"string","nullable":true}}}}},"Media":{"type":"object","properties":{"id":{"type":"string"},"path":{"type":"string"},"public_url":{"type":"string"}}},"MediaInput":{"type":"object","required":["url"],"properties":{"url":{"type":"string","description":"Required absolute HTTP or HTTPS media URL. Surrounding whitespace is trimmed. Invalid URLs reject the entire request with invalid_media; URL contents are not fetched during validation.","example":"https://cdn.example.com/launch-image.png"},"type":{"type":"string","enum":["image","video"],"example":"image"},"width":{"type":"integer"},"height":{"type":"integer"},"size":{"type":"integer"},"duration":{"type":"number","description":"Video duration in seconds when known."},"thumbnail_url":{"type":"string","description":"Cover image URL for video media (JPEG or PNG, 8 MB or less). It is applied as the video cover on Instagram Reels and Stories, Facebook Reels, and TikTok (inserted as the video's first frame), and is also used in scheduled and published post previews."}}},"DraftMediaInput":{"type":"object","additionalProperties":false,"required":["type","url"],"properties":{"url":{"type":"string"},"type":{"type":"string","enum":["image","video"]},"file_name":{"type":"string"},"mime_type":{"type":"string"},"width":{"type":"integer","minimum":0},"height":{"type":"integer","minimum":0},"size":{"type":"integer","minimum":0},"duration":{"type":"number","minimum":0},"thumbnail_url":{"type":"string","nullable":true}}},"Draft":{"type":"object","properties":{"id":{"type":"string"},"content":{"type":"string"},"candidate_angle":{"type":"string"},"media":{"type":"array","items":{"$ref":"#/components/schemas/DraftMediaInput"}},"selected_account_ids":{"type":"array","uniqueItems":true,"items":{"type":"string","format":"uuid"}},"suggested_platforms":{"type":"array","items":{"type":"string"}},"source_type":{"type":"string"},"status":{"type":"string","enum":["inbox","ready","scheduled","archived"]},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"PostTargetInput":{"type":"object","required":["account_id"],"properties":{"account_id":{"type":"string"},"content_override":{"type":"string","nullable":true},"platform_options":{"type":"object","additionalProperties":true,"nullable":true,"properties":{"ai_generated":{"type":"boolean","description":"Requests the target platform's native AI-generated or synthetic-media disclosure. Supported for TikTok direct videos, YouTube videos, and Instagram media. Facebook Page publishing does not currently expose an equivalent API field."},"channel_id":{"type":"string","description":"Farcaster channel ID for the cast."},"video_cover_timestamp_ms":{"type":"integer","minimum":0,"description":"TikTok direct video posts only. Selects the frame at this position, in milliseconds, as the video cover. When set, it takes precedence over media[].thumbnail_url for that TikTok target. Not supported for photo posts or draft uploads."},"title":{"type":"string","description":"Title for platforms that support one. For Vimeo, 1-128 characters; defaults to the first line of the content."},"privacy_view":{"type":"string","enum":["anybody","unlisted","nobody","disable"],"default":"anybody","description":"Vimeo only. Who can view the uploaded video."},"reply_to":{"type":"object","nullable":true,"required":["hash","author_fid"],"properties":{"hash":{"type":"string"},"author_fid":{"type":"integer"}},"description":"Farcaster parent cast. Both the full cast hash and parent author FID are required."},"disable_notification":{"type":"boolean","description":"Telegram only. Sends the channel post silently; subscribers get no notification sound."},"protect_content":{"type":"boolean","description":"Telegram only. Prevents forwarding and saving of the channel post."},"disable_link_preview":{"type":"boolean","description":"Telegram only. Disables the link preview on text posts."}},"description":"Platform-specific settings are mapped explicitly; unknown properties are stored but are not forwarded automatically. For Farcaster, pass { \"channel_id\": \"founders\" } or { \"reply_to\": { \"hash\": \"0x...\", \"author_fid\": 123 } }. To request native AI-content disclosure on TikTok, YouTube, or Instagram, pass { \"ai_generated\": true }. Facebook Page publishing does not currently expose an equivalent API field. For Reddit, pass { \"subreddit\": \"SideProject\", \"title\": \"Launch title\", \"postType\": \"self\" }. For Snapchat, pass { \"placements\": [\"spotlight\", \"public_story\"], \"locale\": \"en_US\" }; placements default to Spotlight. For a TikTok draft upload, pass { \"publish_mode\": \"draft\" }; the creator must finish and publish it from their TikTok inbox. To choose a TikTok cover frame instead of a cover image, pass { \"video_cover_timestamp_ms\": 1500 }. For Vimeo, pass { \"title\": \"Launch video\", \"privacy_view\": \"unlisted\" }; privacy_view defaults to anybody. Telegram accepts only { \"disable_notification\", \"protect_content\", \"disable_link_preview\" } booleans and rejects other keys."}}},"PostTarget":{"type":"object","properties":{"target_post_id":{"type":"string"},"account_id":{"type":"string","nullable":true},"platform":{"type":"string","nullable":true},"target_variant":{"type":"string","enum":["spotlight","public_story"],"nullable":true,"description":"Concrete placement for platforms that fan one account target into multiple publications."},"username":{"type":"string","nullable":true},"status":{"type":"string","enum":["pending","scheduled","processing","submitted","published","failed","skipped","cancelled"],"example":"pending"},"error":{"type":"string","nullable":true},"platform_post_id":{"type":"string","nullable":true},"platform_post_url":{"type":"string","nullable":true},"scheduled_time":{"type":"string","format":"date-time","nullable":true}}},"Post":{"type":"object","properties":{"id":{"type":"string"},"content":{"type":"string"},"external_id":{"type":"string","nullable":true},"media":{"type":"array","items":{"$ref":"#/components/schemas/MediaInput"}},"origin":{"type":"string","enum":["ui","api","workflow"],"nullable":true},"publish_at":{"type":"string","format":"date-time","nullable":true},"status":{"type":"string","enum":["queued","scheduled","processing","partial","published","failed","cancelled"],"example":"scheduled"},"created_at":{"type":"string","format":"date-time","nullable":true},"targets":{"type":"array","items":{"$ref":"#/components/schemas/PostTarget"}}}},"WorkflowSettings":{"type":"object","properties":{"content_filters":{"type":"object","properties":{"types":{"type":"object","properties":{"text":{"type":"boolean"},"image":{"type":"boolean"},"quote":{"type":"boolean"},"video":{"type":"boolean"}}},"hashtags":{"type":"object","properties":{"include":{"type":"array","items":{"type":"string"}},"exclude":{"type":"array","items":{"type":"string"}}}}}},"platform_settings":{"type":"object","properties":{"tiktok":{"type":"object","properties":{"keep_source_thumbnail":{"type":"boolean","default":true,"description":"When false, TikTok workflow posts do not prepend the source thumbnail as the first video frame."}}}}},"destination_publish_settings":{"type":"object","additionalProperties":{"type":"object","properties":{"minimum_interval_minutes":{"type":"integer","minimum":1},"caption_position":{"type":"string","enum":["before","after"],"description":"Placement of custom caption text. Defaults to after."},"remove_comment_dm_prompts":{"type":"boolean","default":false,"description":"Remove comment/DM prompts from the original caption for this destination before applying custom before/after text. Supported on the same platforms as caption_ending."},"adapt_caption":{"type":"boolean","default":false,"description":"Rewrite the caption for this destination with AI at publish time, after comment/DM prompt removal and before custom text. Links are always kept. If the rewrite fails, the original caption is published. Supported on the same platforms as caption_ending."},"adapt_caption_instructions":{"type":"string","maxLength":1000,"description":"Optional style instructions for the AI rewrite. When set, they replace the built-in guidance for the destination platform."},"caption_ending":{"type":"string","maxLength":2000,"description":"Optional custom caption text for this destination. Placement follows caption_position (after by default). Empty clears it. Supported for Instagram, Threads, X, Facebook, LinkedIn, Bluesky, Pinterest, YouTube and TikTok. Platform caption limits also apply."},"snapchat":{"type":"object","required":["publish_targets"],"properties":{"publish_targets":{"type":"array","minItems":1,"uniqueItems":true,"items":{"type":"string","enum":["spotlight","public_story"]}},"locale":{"type":"string","pattern":"^[a-z]{2}_[A-Z]{2}$","default":"en_US"}}},"threads":{"type":"object","properties":{"caption_max_length":{"type":"integer","minimum":1,"maximum":500,"default":500,"description":"Maximum outgoing Threads caption length for this destination. Custom limits prefer whole words; 500 preserves default behavior."}}},"instagram":{"type":"object","anyOf":[{"required":["publish_as_trial_reel"]},{"required":["remove_mentions"]}],"properties":{"remove_mentions":{"type":"boolean","default":false,"description":"Remove @ from mentions in this Instagram destination's outgoing captions. Preserves usernames and email addresses."},"publish_as_trial_reel":{"type":"boolean","default":false,"description":"When true, eligible single-video Instagram Reels publish as Trial Reels for non-followers with manual graduation. Images and carousels publish normally."}}},"reddit":{"type":"object","required":["subreddit"],"properties":{"subreddit":{"type":"string","example":"SideProject"},"post_type":{"type":"string","enum":["auto","self","link"],"default":"auto"},"flair_id":{"type":"string"},"flair_text":{"type":"string"},"nsfw":{"type":"boolean","default":false},"spoiler":{"type":"boolean","default":false},"send_replies":{"type":"boolean","default":true}}}}}}}},"Workflow":{"type":"object","properties":{"id":{"type":"string"},"profile_id":{"type":"string"},"source_account_id":{"type":"string"},"target_account_ids":{"type":"array","items":{"type":"string"}},"settings":{"$ref":"#/components/schemas/WorkflowSettings"},"is_active":{"type":"boolean"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"name":{"type":"string"},"description":{"type":"string","nullable":true},"future_content_enabled":{"type":"boolean"},"last_checked":{"type":"string","format":"date-time","nullable":true},"last_enabled_at":{"type":"string","format":"date-time","nullable":true}}}}},"paths":{"/v1/accounts":{"get":{"summary":"List accounts","description":"Returns all social accounts owned by the API key profile, including disconnected accounts.","responses":{"200":{"description":"Accounts returned successfully.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Account"}}}}}}},"401":{"description":"Missing, invalid, revoked, or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthErrorResponse"}}}}}}},"/v1/accounts/{id}":{"delete":{"summary":"Disconnect account","description":"Removes PostOnce access to an owned social account. The account is disconnected with its credentials cleared (the response returns deleted=false and disconnected=true); its posts and history are kept, and reconnecting the same account restores posting. Farcaster, Nostr, and Telegram records are also disabled. Farcaster signer revocation requires a separate provider step; Nostr remote-signer logout is best effort; @PostOnceBot leaves a Telegram channel on a best-effort basis when no other PostOnce account uses it. This does not delete published social content.","parameters":[{"$ref":"#/components/parameters/accountId"}],"responses":{"200":{"description":"Account disconnected.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteResponse"}}}},"401":{"description":"Missing, invalid, revoked, or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthErrorResponse"}}}},"404":{"description":"Account not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/accounts/connect/{platform}":{"post":{"summary":"Connect account","description":"Computes a five-minute browser authorization URL bound to the authenticated profile without connecting or changing an account. Open the returned URL in a separate browser step to authorize the provider; then list accounts to verify connection. Depending on platform, the browser uses provider OAuth or a PostOnce-hosted credential or signer flow. Never enter provider credentials in chat.","parameters":[{"$ref":"#/components/parameters/platform"}],"responses":{"201":{"description":"Account connection URL created successfully.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectAccountResponse"}}}},"401":{"description":"Missing, invalid, revoked, or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthErrorResponse"}}}},"403":{"description":"Missing required scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Unsupported platform.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/media/uploads":{"post":{"summary":"Create upload URL","description":"Creates a signed upload URL and media_id. Upload raw file bytes with PUT to data.upload.signedUrl, then call get_media with that media_id and pass the returned data.public_url as media[].url when creating a post or draft. URL creation alone does not upload bytes. Treat the signed URL as a temporary credential; do not open it in a browser.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["filename","content_type"],"properties":{"filename":{"type":"string","description":"Original file name including extension.","example":"launch-image.png"},"content_type":{"type":"string","description":"MIME type of the file being uploaded.","enum":["image/jpeg","image/png","image/webp","video/mp4","video/quicktime","video/webm"],"example":"image/png"}}},"examples":{"image":{"value":{"filename":"launch-image.png","content_type":"image/png"}},"video":{"value":{"filename":"teaser.mp4","content_type":"video/mp4"}}}}}},"responses":{"201":{"description":"Signed upload URL created successfully.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"content_type":{"type":"string","enum":["image/jpeg","image/png","image/webp","video/mp4","video/quicktime","video/webm"],"example":"image/png"},"media_id":{"type":"string","example":"cHVibGljLWFwaS91c2VyLTEyMy8xNzQzNDM0NTY3LWxhdW5jaC1pbWFnZS5wbmc"},"path":{"type":"string","example":"public-api/user-123/1743434567-launch-image.png"},"upload":{"type":"object","properties":{"path":{"type":"string","example":"public-api/user-123/1743434567-launch-image.png"},"token":{"type":"string","example":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."},"signedUrl":{"type":"string","example":"https://swgrcbzlaeshsmygubik.supabase.co/storage/v1/object/upload/sign/social-media/public-api/user-123/1743434567-launch-image.png?token=..."}}}}}}},"examples":{"imageUpload":{"value":{"data":{"content_type":"image/png","media_id":"cHVibGljLWFwaS91c2VyLTEyMy8xNzQzNDM0NTY3LWxhdW5jaC1pbWFnZS5wbmc","path":"public-api/user-123/1743434567-launch-image.png","upload":{"path":"public-api/user-123/1743434567-launch-image.png","token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...","signedUrl":"https://swgrcbzlaeshsmygubik.supabase.co/storage/v1/object/upload/sign/social-media/public-api/user-123/1743434567-launch-image.png?token=..."}}}}}}}},"400":{"description":"Missing or invalid payload fields.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"examples":{"invalidFilename":{"value":{"error":{"code":"invalid_filename","message":"filename is required."}}}}}}},"401":{"description":"Missing, invalid, revoked, or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthErrorResponse"}}}}}}},"/v1/media/{id}":{"get":{"summary":"Get media","description":"Resolves a previously returned `media_id` into its storage path and public URL.","parameters":[{"$ref":"#/components/parameters/mediaId"}],"responses":{"200":{"description":"Media resolved successfully.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Media"}}}}}},"400":{"description":"Invalid media ID.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing, invalid, revoked, or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthErrorResponse"}}}}}}},"/v1/drafts":{"get":{"summary":"List drafts","description":"Lists unscheduled drafts owned by the authenticated API-key profile.","parameters":[{"$ref":"#/components/parameters/listLimit"},{"$ref":"#/components/parameters/listAfter"},{"name":"after_id","in":"query","required":false,"schema":{"type":"string","format":"uuid"},"description":"Stable ID tie-breaker returned as meta.next_after_id. Send it with after to avoid skipping drafts that share the same updated_at timestamp."},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["inbox","ready","archived"]}}],"responses":{"200":{"description":"Drafts returned successfully.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Draft"}},"meta":{"type":"object","required":["has_more","next_after","next_after_id"],"properties":{"has_more":{"type":"boolean"},"next_after":{"type":"string","format":"date-time","nullable":true},"next_after_id":{"type":"string","format":"uuid","nullable":true}}}}}}}},"401":{"description":"Missing, invalid, revoked, or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthErrorResponse"}}}}}},"post":{"summary":"Create draft","description":"Saves content and optional media or destinations without publishing or scheduling it. Reuse an Idempotency-Key when retrying.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","minLength":1,"maxLength":200},"description":"Stable retry key. Reusing it with the same body returns the original draft; reusing it with a different body returns 409."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"properties":{"content":{"type":"string"},"candidate_angle":{"type":"string"},"media":{"type":"array","items":{"$ref":"#/components/schemas/DraftMediaInput"}},"selected_account_ids":{"type":"array","uniqueItems":true,"items":{"type":"string","format":"uuid"}},"suggested_platforms":{"type":"array","items":{"type":"string"}}}}}}},"responses":{"201":{"description":"Draft created successfully.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Draft"}}}}}},"400":{"description":"Invalid draft or destination account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing, invalid, revoked, or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthErrorResponse"}}}}}}},"/v1/drafts/{id}":{"get":{"summary":"Get draft","description":"Returns one draft owned by the authenticated API-key profile.","parameters":[{"$ref":"#/components/parameters/draftId"}],"responses":{"200":{"description":"Draft returned successfully.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Draft"}}}}}},"404":{"description":"Draft not found for this profile.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"patch":{"summary":"Update draft","description":"Updates, archives, restores, or marks a draft ready using updated_at as a compare-and-swap version.","parameters":[{"$ref":"#/components/parameters/draftId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":false,"required":["expected_updated_at"],"properties":{"expected_updated_at":{"type":"string","format":"date-time"},"content":{"type":"string"},"candidate_angle":{"type":"string"},"media":{"type":"array","items":{"$ref":"#/components/schemas/DraftMediaInput"}},"selected_account_ids":{"type":"array","uniqueItems":true,"items":{"type":"string","format":"uuid"}},"suggested_platforms":{"type":"array","items":{"type":"string"}},"status":{"type":"string","enum":["inbox","ready","archived"]}}}}}},"responses":{"200":{"description":"Draft updated successfully.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Draft"}}}}}},"409":{"description":"The draft changed or the requested lifecycle transition is invalid.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/v1/posts":{"get":{"summary":"List posts","description":"Lists the authenticated profile's posts, newest first: posts created through the API or dashboard, and posts published by workflows (origin `workflow`). Workflow posts are read-only.","parameters":[{"$ref":"#/components/parameters/listLimit"},{"$ref":"#/components/parameters/listAfter"}],"responses":{"200":{"description":"Posts returned successfully.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Post"}},"meta":{"type":"object","additionalProperties":true}}}}}},"400":{"description":"The after cursor is not a post ID or does not match a post.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing, invalid, revoked, or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthErrorResponse"}}}}}},"post":{"summary":"Create post","description":"Creates one logical post that can target multiple connected accounts. Include `publish_at` to schedule it instead of sending immediately.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["targets"],"properties":{"content":{"type":"string","example":"Shipping the public API today."},"external_id":{"type":"string","nullable":true,"example":"launch-001"},"media":{"type":"array","items":{"$ref":"#/components/schemas/MediaInput"}},"publish_at":{"type":"string","format":"date-time","nullable":true,"example":"2026-04-01T14:00:00.000Z"},"targets":{"type":"array","items":{"$ref":"#/components/schemas/PostTargetInput"}}}},"examples":{"immediate":{"value":{"content":"Shipping the public API today.","external_id":"launch-001","targets":[{"account_id":"account-1"},{"account_id":"account-2","content_override":"Shorter version for X"}]}},"mediaSingle":{"value":{"content":"Launching with a hero image.","media":[{"url":"https://cdn.example.com/launch-image.png","type":"image","width":1200,"height":630}],"targets":[{"account_id":"account-1"}]}},"mediaMultiple":{"value":{"content":"Carousel post with multiple images.","media":[{"url":"https://cdn.example.com/carousel-1.png","type":"image"},{"url":"https://cdn.example.com/carousel-2.png","type":"image"},{"url":"https://cdn.example.com/carousel-3.png","type":"image"}],"targets":[{"account_id":"account-1"}]}},"perTargetCustomization":{"value":{"content":"Shipping the public API today.","targets":[{"account_id":"twitter-account-id","content_override":"Shipping today. API is live."},{"account_id":"linkedin-account-id","platform_options":{"title":"Public API launch"}}]}},"scheduled":{"value":{"content":"Scheduled launch note","publish_at":"2026-04-01T14:00:00.000Z","targets":[{"account_id":"account-1"}]}}}}}},"responses":{"201":{"description":"Post created successfully.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Post"}}}}}},"400":{"description":"One or more target accounts are invalid or inactive.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing, invalid, revoked, or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthErrorResponse"}}}}}}},"/v1/posts/{id}":{"get":{"summary":"Get post","description":"Returns one aggregate post with per-target delivery state.","parameters":[{"$ref":"#/components/parameters/postId"}],"responses":{"200":{"description":"Post returned successfully.","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Post"}}}}}},"400":{"description":"Post ID must be a valid UUID.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing, invalid, revoked, or expired API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthErrorResponse"}}}}}},"patch":{"summary":"Update post","description":"Updates a scheduled post before it runs.","parameters":[{"$ref":"#/components/parameters/postId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"content":{"type":"string"},"external_id":{"type":"string","nullable":true},"media":{"type":"array","items":{"$ref":"#/components/schemas/MediaInput"}},"publish_at":{"type":"string","format":"date-time","nullable":true},"targets":{"type":"array","items":{"$ref":"#/components/schemas/PostTargetInput"}}}},"examples":{"reschedule":{"value":{"publish_at":"2026-04-02T15:00:00.000Z"}},"changeContent":{"value":{"content"
# --- truncated at 32 KB (40 KB total) ---
# Full source: https://raw.githubusercontent.com/api-evangelist/postonce/refs/heads/main/openapi/_original/postonce-api-openapi.json