{"openapi":"3.1.0","info":{"title":"PostWing Agent API","version":"1.1.0","description":"Schedule, preview, edit, and cancel social posts; connect/disconnect accounts; manage workspaces. All data is scoped to the authenticated user and active workspace. Schedule/edit writes require a confirmation_token from the matching preview endpoint (5 minute TTL). Use targets[] with platform_options for per-account captions and platform settings (X quote posts, Pinterest boards, YouTube titles, Instagram Reels, TikTok privacy, etc.). Media: prefer POST /media/upload-url, PUT the file to upload_url, then pass media_url into preview. POST /media URL import is optional. Video media_items support thumbnail_url or thumbnail_timestamp_ms (default 1000). Caption may be \"\" or \" \" for no caption. Human docs: https://postwing.io/docs","contact":{"url":"https://postwing.io/docs"}},"servers":[{"url":"https://postwing.io/api/v1","description":"Production"}],"security":[{"bearerAuth":[]},{"oauth2":["accounts:read","accounts:write","posts:read","posts:write","workspaces:read","workspaces:write"]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"PostWing API key (`pw_live_…`) created in Dashboard → AI & API."},"oauth2":{"type":"oauth2","flows":{"authorizationCode":{"authorizationUrl":"https://postwing.io/oauth/authorize","tokenUrl":"https://postwing.io/oauth/token","refreshUrl":"https://postwing.io/oauth/token","scopes":{"accounts:read":"Access scope accounts:read","accounts:write":"Access scope accounts:write","posts:read":"Access scope posts:read","posts:write":"Access scope posts:write","workspaces:read":"Access scope workspaces:read","workspaces:write":"Access scope workspaces:write"}}}}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"string"}}},"MediaItem":{"type":"object","required":["url","type"],"description":"Image or video attached to a post. For videos, set thumbnail_url (custom cover) or thumbnail_timestamp_ms (frame cover). Same as the dashboard.","properties":{"url":{"type":"string","format":"uri","description":"Public HTTPS URL of the media file."},"type":{"type":"string","enum":["image","video"],"description":"Media kind."},"thumbnail_url":{"type":"string","format":"uri","description":"Video only. Custom cover image (JPG/PNG/WEBP, max 10MB). Wins over thumbnail_timestamp_ms."},"thumbnail_timestamp_ms":{"type":"integer","minimum":0,"description":"Video only. Cover frame time in milliseconds when thumbnail_url is omitted. Default 1000.","examples":[1000,3500]}}},"PlatformOptions":{"type":"object","description":"Platform-specific settings for this target account. Keys depend on the account platform. X quote posts: set quote_tweet_url. Pinterest: set board_ids from GET /accounts/pinterest-boards. See https://postwing.io/docs#platform-options","additionalProperties":true,"properties":{"quote_tweet_url":{"type":"string","description":"X only. Tweet URL or ID to publish as a native quote post.","examples":["https://x.com/user/status/1234567890"]},"reply_settings":{"type":"string","nullable":true,"enum":[null,"following","mentionedUsers","subscribers","verified"],"description":"X only. Who can reply (null = everyone)."},"placement":{"type":"string","enum":["timeline","reels"],"description":"Facebook / Instagram. Where the post appears."},"share_to_feed":{"type":"boolean","description":"Instagram Reels: also share to the main feed (default true)."},"trial_reel_type":{"type":"string","nullable":true,"enum":[null,"manual","performance"],"description":"Instagram Reels trial mode."},"title":{"type":"string","description":"YouTube (required), Pinterest pin title, or TikTok image title."},"made_for_kids":{"type":"boolean","description":"YouTube only."},"privacy_status":{"type":"string","enum":["public","private"],"description":"TikTok only."},"allow_comment":{"type":"boolean","description":"TikTok only."},"allow_duet":{"type":"boolean","description":"TikTok only."},"allow_stitch":{"type":"boolean","description":"TikTok only."},"is_ai_generated":{"type":"boolean","description":"TikTok only."},"is_draft":{"type":"boolean","description":"TikTok only. Save as app draft."},"board_ids":{"type":"array","items":{"type":"string"},"description":"Pinterest only. Required board IDs."},"link":{"type":"string","format":"uri","description":"Pinterest destination URL."},"caption":{"type":"string","description":"Optional caption stored inside platform_options (prefer targets[].caption, including \"\" or \" \")."}}},"ScheduleTarget":{"type":"object","required":["account_id"],"description":"Per-account schedule target. Use instead of account_ids/platforms when you need per-account captions or platform_options (e.g. X quote posts, Pinterest boards, Instagram Reel settings).","properties":{"account_id":{"type":"string","format":"uuid","description":"PostWing social account UUID"},"caption":{"type":"string","description":"Optional per-account caption override. Use \"\" or \" \" for an empty caption on this account. Omit to use the global caption."},"platform_options":{"$ref":"#/components/schemas/PlatformOptions"}}},"PreviewScheduleBody":{"type":"object","required":["caption","scheduled_at"],"properties":{"caption":{"type":"string","description":"Global caption for all targets. Use \"\" or \" \" for no caption. Overridden per account by targets[].caption when set."},"scheduled_at":{"type":"string","format":"date-time"},"account_ids":{"type":"array","items":{"type":"string","format":"uuid"}},"platforms":{"type":"array","items":{"type":"string","enum":["instagram","tiktok","youtube","linkedin","x","facebook","pinterest","threads"]}},"targets":{"type":"array","items":{"$ref":"#/components/schemas/ScheduleTarget"},"description":"Preferred for per-account captions or platform_options (quote posts, boards, Reels, etc.)"},"post_type":{"type":"string","enum":["text","image","video"]},"media_items":{"type":"array","items":{"$ref":"#/components/schemas/MediaItem"}},"media_urls":{"type":"array","items":{"type":"string","format":"uri"}}},"examples":[{"caption":"Adding my take","scheduled_at":"2026-09-21T15:00:00Z","targets":[{"account_id":"00000000-0000-0000-0000-000000000001","platform_options":{"quote_tweet_url":"https://x.com/someone/status/1234567890"}}]},{"caption":"","scheduled_at":"2026-09-21T18:00:00Z","targets":[{"account_id":"00000000-0000-0000-0000-000000000002","platform_options":{"share_to_feed":true}}],"media_items":[{"url":"https://cdn.example.com/reel.mp4","type":"video","thumbnail_url":"https://cdn.example.com/reel-cover.jpg"}]}]},"PreviewEditBody":{"type":"object","required":["post_ids"],"properties":{"post_ids":{"type":"array","items":{"type":"string","format":"uuid"},"minItems":1,"maxItems":20},"caption":{"type":"string","description":"New global caption. Use \"\" or \" \" for no caption. Omit to keep the current one."},"scheduled_at":{"type":"string","format":"date-time"},"shift_minutes":{"type":"integer"},"account_ids":{"type":"array","items":{"type":"string","format":"uuid"}},"platforms":{"type":"array","items":{"type":"string"}},"targets":{"type":"array","items":{"$ref":"#/components/schemas/ScheduleTarget"},"description":"Replace targets (captions / platform_options)"},"target_options":{"type":"array","items":{"$ref":"#/components/schemas/ScheduleTarget"},"description":"Update captions/platform_options for accounts already on the post without replacing the whole target list"},"media_items":{"type":"array","items":{"$ref":"#/components/schemas/MediaItem"}},"media_urls":{"type":"array","items":{"type":"string","format":"uri"}},"remove_media":{"type":"boolean"}}}}},"paths":{"/accounts":{"get":{"operationId":"listAccounts","summary":"List connected social accounts","security":[{"bearerAuth":[]},{"oauth2":["accounts:read"]}],"responses":{"200":{"description":"Accounts for the authenticated user"},"401":{"description":"Unauthorized"},"403":{"description":"Missing scope or subscription"}}},"post":{"operationId":"connectAccount","summary":"Start OAuth connect for a platform (returns browser URL)","security":[{"bearerAuth":[]},{"oauth2":["accounts:write"]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["platform"],"properties":{"platform":{"type":"string","description":"facebook, instagram, linkedin, pinterest, threads, tiktok, x, youtube"}}}}}},"responses":{"200":{"description":"Connect URL"},"400":{"description":"Invalid platform"},"403":{"description":"Limit or subscription"}}},"delete":{"operationId":"disconnectAccount","summary":"Disconnect an account (confirm: true required)","security":[{"bearerAuth":[]},{"oauth2":["accounts:write"]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["account_id","confirm"],"properties":{"account_id":{"type":"string","format":"uuid"},"confirm":{"type":"boolean","enum":[true]}}}}}},"responses":{"200":{"description":"Disconnected"},"400":{"description":"Missing confirm"},"404":{"description":"Not found"}}}},"/accounts/sync":{"post":{"operationId":"syncAccounts","summary":"Sync accounts after the user completes OAuth","security":[{"bearerAuth":[]},{"oauth2":["accounts:write"]}],"responses":{"200":{"description":"Synced accounts"}}}},"/accounts/pinterest-boards":{"get":{"operationId":"listPinterestBoards","summary":"List Pinterest boards for an account","security":[{"bearerAuth":[]},{"oauth2":["accounts:read"]}],"parameters":[{"name":"account_id","in":"query","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Boards"},"404":{"description":"Pinterest account not found"}}}},"/workspaces":{"get":{"operationId":"listWorkspaces","summary":"List workspaces","security":[{"bearerAuth":[]},{"oauth2":["workspaces:read"]}],"responses":{"200":{"description":"Workspaces"}}},"post":{"operationId":"createWorkspace","summary":"Create a workspace (Scale plan)","security":[{"bearerAuth":[]},{"oauth2":["workspaces:write"]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["name"],"properties":{"name":{"type":"string"}}}}}},"responses":{"201":{"description":"Created"},"403":{"description":"Plan does not support workspaces"}}}},"/workspaces/active":{"post":{"operationId":"switchWorkspace","summary":"Set the active workspace","security":[{"bearerAuth":[]},{"oauth2":["workspaces:write"]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["workspace_id"],"properties":{"workspace_id":{"type":"string","format":"uuid"}}}}}},"responses":{"200":{"description":"Active workspace updated"}}}},"/posts":{"get":{"operationId":"listPosts","summary":"List scheduled posts (paginated)","security":[{"bearerAuth":[]},{"oauth2":["posts:read"]}],"parameters":[{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":200}},{"name":"offset","in":"query","schema":{"type":"integer","minimum":0}},{"name":"fields","in":"query","schema":{"type":"string","enum":["full","summary"]}},{"name":"status","in":"query","schema":{"type":"string","enum":["scheduled","publishing","published","partially_failed","failed","paused"]}},{"name":"from","in":"query","schema":{"type":"string","format":"date-time"}},{"name":"until","in":"query","schema":{"type":"string","format":"date-time"}},{"name":"include_past","in":"query","schema":{"type":"boolean"}}],"responses":{"200":{"description":"Paginated posts for the authenticated user"},"401":{"description":"Unauthorized"},"403":{"description":"Missing scope or subscription"}}}},"/schedule/summary":{"get":{"operationId":"getScheduleSummary","summary":"Schedule counts and patterns","security":[{"bearerAuth":[]},{"oauth2":["posts:read"]}],"responses":{"200":{"description":"Summary for the authenticated user"},"401":{"description":"Unauthorized"},"403":{"description":"Missing scope or subscription"}}}},"/media/upload-url":{"post":{"operationId":"createMediaUploadUrl","summary":"Get a direct upload URL for an image or video","description":"Preferred media path. Returns upload_url and media_url. PUT the file bytes to upload_url (same Content-Type), then pass media_url into POST /posts/preview as media_items[].url.","security":[{"bearerAuth":[]},{"oauth2":["posts:write"]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["filename","content_type","size"],"properties":{"filename":{"type":"string","description":"Original filename"},"content_type":{"type":"string","description":"MIME type (contentType alias accepted). e.g. image/jpeg, video/mp4"},"size":{"type":"number","description":"File size in bytes","exclusiveMinimum":0}}},"examples":{"image":{"summary":"Prepare an image upload","value":{"filename":"launch.jpg","content_type":"image/jpeg","size":245760}}}}}},"responses":{"200":{"description":"upload_url to PUT the file, media_url to use in preview","content":{"application/json":{"schema":{"type":"object","properties":{"upload_url":{"type":"string","format":"uri"},"media_url":{"type":"string","format":"uri"},"type":{"type":"string","enum":["image","video"]},"next_step":{"type":"string"}}}}}},"400":{"description":"Invalid input"},"401":{"description":"Unauthorized"},"403":{"description":"Missing scope or subscription"},"429":{"description":"Rate limit"},"503":{"description":"Upstream storage unavailable"}}}},"/media":{"post":{"operationId":"importMedia","summary":"Import media from public HTTPS URLs (optional)","description":"Optional. Downloads public HTTPS URLs through PostWing and returns media_items. Prefer POST /media/upload-url for local files, or pass durable public URLs directly into POST /posts/preview.","security":[{"bearerAuth":[]},{"oauth2":["posts:write"]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["urls"],"properties":{"urls":{"type":"array","items":{"type":"string","format":"uri"},"minItems":1,"maxItems":10}}}}}},"responses":{"200":{"description":"Imported media_items"},"400":{"description":"Invalid input"},"401":{"description":"Unauthorized"},"403":{"description":"Missing scope or subscription"}}}},"/posts/preview":{"post":{"operationId":"previewSchedulePost","summary":"Preview a post and get a confirmation_token (5 min)","description":"Supports account_ids, platforms, or targets with platform_options. Videos: set media_items[].thumbnail_url or thumbnail_timestamp_ms. Empty captions: caption \"\" and/or targets[].caption \"\". For an X quote post, pass targets[{account_id, platform_options:{quote_tweet_url}}].","security":[{"bearerAuth":[]},{"oauth2":["posts:write"]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PreviewScheduleBody"},"examples":{"quoteTweet":{"summary":"X native quote post","value":{"caption":"Adding my take","scheduled_at":"2026-09-21T15:00:00Z","targets":[{"account_id":"00000000-0000-0000-0000-000000000001","platform_options":{"quote_tweet_url":"https://x.com/someone/status/1234567890"}}]}},"instagramReelCover":{"summary":"Instagram Reel with custom cover and empty caption","value":{"caption":"","scheduled_at":"2026-09-21T18:00:00Z","targets":[{"account_id":"00000000-0000-0000-0000-000000000002","platform_options":{"share_to_feed":true}}],"media_items":[{"url":"https://cdn.example.com/reel.mp4","type":"video","thumbnail_url":"https://cdn.example.com/reel-cover.jpg"}]}}}}}},"responses":{"200":{"description":"Preview + confirmation_token"},"400":{"description":"Validation error"},"401":{"description":"Unauthorized"},"403":{"description":"Missing scope or subscription"}}}},"/posts/confirm":{"post":{"operationId":"confirmSchedulePost","summary":"Schedule a post using confirmation_token from preview","security":[{"bearerAuth":[]},{"oauth2":["posts:write"]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["confirmation_token"],"properties":{"confirmation_token":{"type":"string"}}}}}},"responses":{"200":{"description":"Post scheduled"},"400":{"description":"Invalid or expired token"},"401":{"description":"Unauthorized"},"403":{"description":"Missing scope or subscription"}}}},"/posts/edit/preview":{"post":{"operationId":"previewEditPosts","summary":"Preview edits and get a confirmation_token (5 min)","description":"Only send fields being changed. Use targets or target_options to update platform_options (e.g. quote_tweet_url).","security":[{"bearerAuth":[]},{"oauth2":["posts:write"]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PreviewEditBody"}}}},"responses":{"200":{"description":"Edit preview + confirmation_token"},"400":{"description":"Validation error"},"401":{"description":"Unauthorized"},"403":{"description":"Missing scope or subscription"}}}},"/posts/edit/confirm":{"post":{"operationId":"confirmEditPosts","summary":"Apply edits using confirmation_token from edit preview","security":[{"bearerAuth":[]},{"oauth2":["posts:write"]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["confirmation_token"],"properties":{"confirmation_token":{"type":"string"}}}}}},"responses":{"200":{"description":"Posts updated"},"400":{"description":"Invalid or expired token"},"401":{"description":"Unauthorized"},"403":{"description":"Missing scope or subscription"}}}},"/posts/{id}":{"get":{"operationId":"getPost","summary":"Get one post with media and platform_options","security":[{"bearerAuth":[]},{"oauth2":["posts:read"]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Post detail"},"404":{"description":"Not found for this user"}}},"delete":{"operationId":"cancelPost","summary":"Cancel a scheduled or paused post (requires confirm: true)","security":[{"bearerAuth":[]},{"oauth2":["posts:write"]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["confirm"],"properties":{"confirm":{"type":"boolean","enum":[true]}}}}}},"responses":{"200":{"description":"Post cancelled"},"400":{"description":"Missing confirm or invalid state"},"401":{"description":"Unauthorized"},"403":{"description":"Missing scope or subscription"},"404":{"description":"Not found for this user"}}}}},"x-postwing-oauth-resource":{"resource":"https://postwing.io/api/v1","authorization_servers":["https://postwing.io"],"scopes_supported":["accounts:read","accounts:write","posts:read","posts:write","workspaces:read","workspaces:write"],"bearer_methods_supported":["header"],"resource_name":"PostWing API","resource_documentation":"https://postwing.io/docs"},"x-postwing-platform-options":[{"platform":"x","label":"X (Twitter)","fields":[{"key":"quote_tweet_url","type":"string","description":"Tweet URL or status ID to quote. Creates a native X quote post (not a plain link in the caption). Example: https://x.com/user/status/123"},{"key":"reply_settings","type":"string | null","description":"Who can reply: null (everyone), following, mentionedUsers, subscribers, verified"}]},{"platform":"instagram","label":"Instagram","fields":[{"key":"placement","type":"string","description":"Set automatically by PostWing: reels for video, timeline for image/text. You usually do not need to send this."},{"key":"share_to_feed","type":"boolean","description":"Video/Reels only. If true, also share the Reel to the main feed (default true). Same as “Share Reels to feed” in the dashboard."},{"key":"trial_reel_type","type":"string | null","description":"Video only. null (off), manual, or performance. Trial reels require 1000+ followers; max 5 per day."}]},{"platform":"facebook","label":"Facebook","fields":[{"key":"placement","type":"string","description":"timeline or reels. Video defaults to reels; images/text use timeline."}]},{"platform":"youtube","label":"YouTube","fields":[{"key":"title","type":"string","description":"Required for YouTube. Shorts/long-form title."},{"key":"made_for_kids","type":"boolean","description":"Whether the video is made for kids (default false)"}]},{"platform":"tiktok","label":"TikTok","fields":[{"key":"title","type":"string","description":"Image posts only (not used for video)"},{"key":"privacy_status","type":"string","description":"public or private (default public)"},{"key":"allow_comment","type":"boolean","description":"Allow comments (default true)"},{"key":"allow_duet","type":"boolean","description":"Allow duets (default true)"},{"key":"allow_stitch","type":"boolean","description":"Allow stitch (default true)"},{"key":"is_ai_generated","type":"boolean","description":"Mark as AI-generated content (default false)"},{"key":"is_draft","type":"boolean","description":"Save as draft in the TikTok app (default false)"}]},{"platform":"pinterest","label":"Pinterest","fields":[{"key":"board_ids","type":"string[]","description":"Required. Board IDs from GET /accounts/pinterest-boards or list_pinterest_boards"},{"key":"title","type":"string","description":"Pin title"},{"key":"link","type":"string","description":"Destination URL for the pin"}]},{"platform":"linkedin","label":"LinkedIn","fields":[{"key":"(none)","type":"n/a","description":"Caption only; no extra platform options"}]},{"platform":"threads","label":"Threads","fields":[{"key":"(none)","type":"n/a","description":"Caption only; no extra platform options"}]}]}