{"openapi":"3.1.0","info":{"title":"Flaghand API","version":"1.0.0","description":"Everything Flaghand does, for people and their agents. Agents draft, upload media, check and request approval with scoped API tokens; only a workspace owner approves, schedules or posts, with a passkey, and the server enforces it. Docs: https://app.flaghand.com/docs. Errors are { error: { code, message } } with stable codes (https://app.flaghand.com/docs/errors)."},"servers":[{"url":"https://app.flaghand.com"}],"tags":[{"name":"Meta"},{"name":"Sign-in"},{"name":"Workspaces"},{"name":"Voice"},{"name":"Tokens"},{"name":"Connections"},{"name":"Drafts"},{"name":"Link cards"},{"name":"Media"},{"name":"Approvals"},{"name":"Posts"},{"name":"Jobs"},{"name":"Events"},{"name":"Tools"},{"name":"Webhooks"},{"name":"Notifications"},{"name":"Calendar feed"},{"name":"Feeds"},{"name":"Sign-up"},{"name":"Mentions"},{"name":"Best times"},{"name":"Slideshows"},{"name":"Billing"}],"paths":{"/api/v1/openapi.json":{"get":{"operationId":"getOpenApi","summary":"This API's OpenAPI 3.1 spec","tags":["Meta"],"description":"Who can call it: No sign-in needed.","security":[],"x-auth":"public","responses":{"200":{"description":"This API's OpenAPI 3.1 spec","content":{"application/json":{"schema":{"type":"object"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/me":{"get":{"operationId":"getMe","summary":"Who is calling, and the workspaces it can use","tags":["Meta"],"description":"Who can call it: An API token, or a signed-in person.","security":[{"token":[]},{"session":[]}],"x-auth":"any","responses":{"200":{"description":"Who is calling, and the workspaces it can use","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Me"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/auth/enroll/options":{"post":{"operationId":"startEnrollment","summary":"Start adding a passkey from a link, or joining a workspace with an existing passkey","tags":["Sign-in"],"description":"Who can call it: No sign-in needed.","security":[],"x-auth":"public","responses":{"200":{"description":"Start adding a passkey from a link, or joining a workspace with an existing passkey","content":{"application/json":{"schema":{"type":"object","properties":{"challenge_id":{"type":"string"},"options":{"type":"object","description":"The browser's PublicKeyCredential, as JSON (what @simplewebauthn/browser returns)."},"email":{"type":"string"},"plan":{"type":["string","null"]},"action":{"type":"string","enum":["join"],"description":"join: sign with an existing passkey, never register a new one."}},"required":["challenge_id","options"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string","description":"The token from the enrollment or invitation link's #token= fragment, or a sign-up link's #signup= fragment."}},"required":["token"],"additionalProperties":false}}}}}},"/api/v1/auth/enroll":{"post":{"operationId":"finishEnrollment","summary":"Finish adding a passkey or joining a workspace; signs you in. A sign-up link creates the account and workspace","tags":["Sign-in"],"description":"Who can call it: No sign-in needed.","security":[],"x-auth":"public","responses":{"200":{"description":"Finish adding a passkey or joining a workspace; signs you in. A sign-up link creates the account and workspace","content":{"application/json":{"schema":{"type":"object","properties":{"user":{"$ref":"#/components/schemas/User"},"workspaces":{"type":"array","items":{"$ref":"#/components/schemas/Workspace"}}},"required":["user"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string"},"challenge_id":{"type":"string"},"response":{"type":"object","description":"The browser's PublicKeyCredential, as JSON (what @simplewebauthn/browser returns)."},"time_zone":{"type":"string","description":"At sign-up, the owner's browser supplies its IANA time zone here, overriding any zone from the agent's sign-up. Ignored when adding a passkey or joining a workspace."},"name":{"type":"string","maxLength":80,"description":"A name for this passkey, like the device."}},"required":["token","challenge_id","response"],"additionalProperties":false}}}}}},"/api/v1/auth/sign-in/options":{"post":{"operationId":"startSignIn","summary":"Start signing in with a passkey","tags":["Sign-in"],"description":"Who can call it: No sign-in needed.","security":[],"x-auth":"public","responses":{"200":{"description":"Start signing in with a passkey","content":{"application/json":{"schema":{"type":"object","properties":{"challenge_id":{"type":"string"},"options":{"type":"object","description":"The browser's PublicKeyCredential, as JSON (what @simplewebauthn/browser returns)."}},"required":["challenge_id","options"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/auth/sign-in":{"post":{"operationId":"finishSignIn","summary":"Finish signing in with a passkey; sets the session cookie","tags":["Sign-in"],"description":"Who can call it: No sign-in needed.","security":[],"x-auth":"public","responses":{"200":{"description":"Finish signing in with a passkey; sets the session cookie","content":{"application/json":{"schema":{"type":"object","properties":{"user":{"$ref":"#/components/schemas/User"},"workspaces":{"type":"array","items":{"$ref":"#/components/schemas/Workspace"}}},"required":["user"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"challenge_id":{"type":"string"},"response":{"type":"object","description":"The browser's PublicKeyCredential, as JSON (what @simplewebauthn/browser returns)."}},"required":["challenge_id","response"],"additionalProperties":false}}}}}},"/api/v1/auth/sign-out":{"post":{"operationId":"signOut","summary":"Sign out of this session","tags":["Sign-in"],"description":"Who can call it: A person signed in with a passkey (session cookie). API tokens are refused.","security":[{"session":[]}],"x-auth":"session","responses":{"200":{"description":"Sign out of this session","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/auth/passkeys":{"get":{"operationId":"listPasskeys","summary":"Your passkeys","tags":["Sign-in"],"description":"Who can call it: A person signed in with a passkey (session cookie). API tokens are refused.","security":[{"session":[]}],"x-auth":"session","responses":{"200":{"description":"Your passkeys","content":{"application/json":{"schema":{"type":"object","properties":{"passkeys":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":["string","null"]},"device_type":{"type":["string","null"]},"backed_up":{"type":"boolean"},"created_at":{"type":"string","format":"date-time"},"last_used_at":{"type":["string","null"],"format":"date-time"}},"required":["id"],"additionalProperties":false}}},"required":["passkeys"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/auth/enrollments/options":{"post":{"operationId":"startEnrollmentLink","summary":"The passkey challenge for a link that adds a passkey on another device","tags":["Sign-in"],"description":"Who can call it: A person signed in with a passkey (session cookie). API tokens are refused.","security":[{"session":[]}],"x-auth":"session","responses":{"200":{"description":"The passkey challenge for a link that adds a passkey on another device","content":{"application/json":{"schema":{"type":"object","properties":{"challenge_id":{"type":"string"},"options":{"type":"object","description":"The browser's PublicKeyCredential, as JSON (what @simplewebauthn/browser returns)."}},"required":["challenge_id","options"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/auth/enrollments":{"post":{"operationId":"createEnrollmentLink","summary":"A one-time link to add a passkey on another device (with a passkey assertion)","tags":["Sign-in"],"description":"Who can call it: A person signed in with a passkey (session cookie). API tokens are refused.","security":[{"session":[]}],"x-auth":"session","responses":{"200":{"description":"A one-time link to add a passkey on another device (with a passkey assertion)","content":{"application/json":{"schema":{"type":"object","properties":{"enrollment_url":{"type":"string"},"expires_at":{"type":"string","format":"date-time"}},"required":["enrollment_url","expires_at"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"challenge_id":{"type":"string"},"response":{"type":"object","description":"Your passkey's assertion over the challenge from the matching …/options call."}},"required":["challenge_id","response"],"additionalProperties":false}}}}}},"/api/v1/auth/passkeys/{id}":{"delete":{"operationId":"deletePasskey","summary":"Remove one of your passkeys (not your last one); its sessions end","tags":["Sign-in"],"description":"Who can call it: A person signed in with a passkey (session cookie). API tokens are refused.","security":[{"session":[]}],"x-auth":"session","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Remove one of your passkeys (not your last one); its sessions end","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/workspaces":{"get":{"operationId":"listWorkspaces","summary":"Workspaces you can use","tags":["Workspaces"],"description":"Who can call it: An API token, or a signed-in person.","security":[{"token":[]},{"session":[]}],"x-auth":"any","responses":{"200":{"description":"Workspaces you can use","content":{"application/json":{"schema":{"type":"object","properties":{"workspaces":{"type":"array","items":{"$ref":"#/components/schemas/Workspace"}}},"required":["workspaces"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/workspaces/{workspace}":{"get":{"operationId":"getWorkspace","summary":"One workspace","tags":["Workspaces"],"description":"Who can call it: An API token with the \"read\" scope, or a member of this workspace signed in with a passkey.","security":[{"token":["read"]},{"session":[]}],"x-auth":"scope","x-scope":"read","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}}],"responses":{"200":{"description":"One workspace","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Workspace"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"operationId":"renameWorkspace","summary":"Update a workspace's time zone (drafts scope), or its name (owner session only)","tags":["Workspaces"],"description":"Who can call it: An API token with the \"drafts\" scope, or an owner of this workspace signed in with a passkey. Refused (402 workspace_inactive) once the workspace's plan has lapsed.","security":[{"token":["drafts"]},{"session":[]}],"x-auth":"scope","x-scope":"drafts","x-plan":"not_lapsed","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}}],"responses":{"200":{"description":"Update a workspace's time zone (drafts scope), or its name (owner session only)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Workspace"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80},"time_zone":{"type":"string","description":"An IANA time zone, for example Europe/London or Asia/Tokyo. Best-times research, waking hours and heatmap cells use this local clock. Defaults to America/Los_Angeles when omitted at sign-up."}},"required":[],"additionalProperties":false}}}}}},"/api/v1/workspaces/{workspace}/notifications":{"get":{"operationId":"getNotifications","summary":"The workspace's post-needs-you email switch","tags":["Workspaces"],"description":"Who can call it: An API token with the \"read\" scope, or a member of this workspace signed in with a passkey.","security":[{"token":["read"]},{"session":[]}],"x-auth":"scope","x-scope":"read","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}}],"responses":{"200":{"description":"The workspace's post-needs-you email switch","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Notifications"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"operationId":"updateNotifications","summary":"Email me when a post needs me: on or off for the workspace's owners","tags":["Workspaces"],"description":"Who can call it: An API token with the \"drafts\" scope, or an owner of this workspace signed in with a passkey.","security":[{"token":["drafts"]},{"session":[]}],"x-auth":"scope","x-scope":"drafts","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}}],"responses":{"200":{"description":"Email me when a post needs me: on or off for the workspace's owners","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Notifications"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Notifications"}}}}}},"/api/v1/workspaces/{workspace}/voice":{"get":{"operationId":"getVoice","summary":"The workspace's voice note: read it before drafting","tags":["Voice"],"description":"Who can call it: An API token with the \"read\" scope, or a member of this workspace signed in with a passkey.","security":[{"token":["read"]},{"session":[]}],"x-auth":"scope","x-scope":"read","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}}],"responses":{"200":{"description":"The workspace's voice note: read it before drafting","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Voice"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"put":{"operationId":"setVoice","summary":"Replace the workspace's voice note (never posted)","tags":["Voice"],"description":"Who can call it: An API token with the \"drafts\" scope, or an owner of this workspace signed in with a passkey.","security":[{"token":["drafts"]},{"session":[]}],"x-auth":"scope","x-scope":"drafts","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}}],"responses":{"200":{"description":"Replace the workspace's voice note (never posted)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Voice"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"text":{"type":"string","description":"The whole note, in Markdown. An empty text clears it."}},"required":["text"],"additionalProperties":false}}}}}},"/api/v1/workspaces/{workspace}/members":{"get":{"operationId":"listMembers","summary":"The people in a workspace","tags":["Workspaces"],"description":"Who can call it: An API token with the \"read\" scope, or a member of this workspace signed in with a passkey.","security":[{"token":["read"]},{"session":[]}],"x-auth":"scope","x-scope":"read","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}}],"responses":{"200":{"description":"The people in a workspace","content":{"application/json":{"schema":{"type":"object","properties":{"members":{"type":"array","items":{"$ref":"#/components/schemas/Member"}}},"required":["members"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/workspaces/{workspace}/invites/options":{"post":{"operationId":"startInvite","summary":"The passkey challenge for inviting a person","tags":["Workspaces"],"description":"Who can call it: An owner of this workspace, signed in with a passkey (session cookie). API tokens are always refused (owner_session_required), whatever their scopes. Refused (402 workspace_inactive) once the workspace's plan has lapsed.","security":[{"session":[]}],"x-auth":"owner","x-plan":"not_lapsed","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}}],"responses":{"200":{"description":"The passkey challenge for inviting a person","content":{"application/json":{"schema":{"type":"object","properties":{"challenge_id":{"type":"string"},"options":{"type":"object","description":"The browser's PublicKeyCredential, as JSON (what @simplewebauthn/browser returns)."}},"required":["challenge_id","options"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/workspaces/{workspace}/invites":{"post":{"operationId":"inviteMember","summary":"Invite a person with a passkey assertion; existing accounts join with their own passkey, never enroll another","tags":["Workspaces"],"description":"Who can call it: An owner of this workspace, signed in with a passkey (session cookie). API tokens are always refused (owner_session_required), whatever their scopes. Refused (402 workspace_inactive) once the workspace's plan has lapsed.","security":[{"session":[]}],"x-auth":"owner","x-plan":"not_lapsed","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}}],"responses":{"200":{"description":"Invite a person with a passkey assertion; existing accounts join with their own passkey, never enroll another","content":{"application/json":{"schema":{"type":"object","properties":{"user_id":{"type":"string"},"enrollment_url":{"type":"string"},"expires_at":{"type":"string","format":"date-time"}},"required":["user_id","enrollment_url","expires_at"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","pattern":"^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$","description":"Kept for account recovery only."},"name":{"type":"string","maxLength":80},"role":{"type":"string","enum":["owner","editor"]},"challenge_id":{"type":"string"},"response":{"type":"object","description":"Your passkey's assertion over the challenge from the matching …/options call."}},"required":["email","role","challenge_id","response"],"additionalProperties":false}}}}}},"/api/v1/workspaces/{workspace}/tokens":{"get":{"operationId":"listTokens","summary":"The workspace's API tokens (never their values)","tags":["Tokens"],"description":"Who can call it: An owner of this workspace, signed in with a passkey (session cookie). API tokens are always refused (owner_session_required), whatever their scopes.","security":[{"session":[]}],"x-auth":"owner","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}}],"responses":{"200":{"description":"The workspace's API tokens (never their values)","content":{"application/json":{"schema":{"type":"object","properties":{"tokens":{"type":"array","items":{"$ref":"#/components/schemas/Token"}}},"required":["tokens"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"createToken","summary":"Create an API token for an agent; its value is shown once","tags":["Tokens"],"description":"Who can call it: An owner of this workspace, signed in with a passkey (session cookie). API tokens are always refused (owner_session_required), whatever their scopes. Refused (402 workspace_inactive) once the workspace's plan has lapsed.","security":[{"session":[]}],"x-auth":"owner","x-plan":"not_lapsed","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}}],"responses":{"201":{"description":"Create an API token for an agent; its value is shown once","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewToken"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80},"scopes":{"type":"array","items":{"type":"string","enum":["read","drafts","media","request-approval","cancel","connections","billing","webhooks","calendar"]},"minItems":1,"description":"No scope can approve, schedule, post or connect accounts."}},"required":["name","scopes"],"additionalProperties":false}}}}}},"/api/v1/workspaces/{workspace}/tokens/{id}":{"patch":{"operationId":"renameToken","summary":"Rename an API token (approval requests show the name of the token that asked)","tags":["Tokens"],"description":"Who can call it: An owner of this workspace, signed in with a passkey (session cookie). API tokens are always refused (owner_session_required), whatever their scopes.","security":[{"session":[]}],"x-auth":"owner","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^tkn_[a-z0-9]{16}$"}}],"responses":{"200":{"description":"Rename an API token (approval requests show the name of the token that asked)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Token"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80}},"required":["name"],"additionalProperties":false}}}}},"delete":{"operationId":"revokeToken","summary":"Revoke an API token","tags":["Tokens"],"description":"Who can call it: An owner of this workspace, signed in with a passkey (session cookie). API tokens are always refused (owner_session_required), whatever their scopes.","security":[{"session":[]}],"x-auth":"owner","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^tkn_[a-z0-9]{16}$"}}],"responses":{"200":{"description":"Revoke an API token","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Token"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/workspaces/{workspace}/connections":{"get":{"operationId":"listConnections","summary":"The X, LinkedIn and TikTok accounts the workspace posts as","tags":["Connections"],"x-read-only":false,"description":"May backfill this workspace's migrated connection identities from saved credentials; this GET can update account metadata. Who can call it: An API token with the \"read\" scope, or a member of this workspace signed in with a passkey.","security":[{"token":["read"]},{"session":[]}],"x-auth":"scope","x-scope":"read","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}}],"responses":{"200":{"description":"The X, LinkedIn and TikTok accounts the workspace posts as","content":{"application/json":{"schema":{"type":"object","properties":{"connections":{"type":"array","items":{"$ref":"#/components/schemas/Connection"}}},"required":["connections"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/workspaces/{workspace}/connections/{id}/slots":{"get":{"operationId":"getConnectionSlots","summary":"Read an account's weekly posting slots (UTC and empty until set)","tags":["Connections"],"description":"Who can call it: An API token with the \"read\" scope, or a member of this workspace signed in with a passkey.","security":[{"token":["read"]},{"session":[]}],"x-auth":"scope","x-scope":"read","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Read an account's weekly posting slots (UTC and empty until set)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectionPostingSlots"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"put":{"operationId":"setConnectionSlots","summary":"Replace the weekly proposed times; never moves a draft, approval or job","tags":["Connections"],"description":"Who can call it: An API token with the \"drafts\" scope, or a member of this workspace signed in with a passkey. Refused (402 workspace_inactive) once the workspace's plan has lapsed.","security":[{"token":["drafts"]},{"session":[]}],"x-auth":"scope","x-scope":"drafts","x-plan":"not_lapsed","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Replace the weekly proposed times; never moves a draft, approval or job","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectionPostingSlots"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"zone":{"type":"string","description":"IANA time zone, like America/New_York or UTC. Independent of the workspace display zone."},"slots":{"type":"array","items":{"$ref":"#/components/schemas/WeeklyPostingSlot"},"description":"Weekly proposed times. Empty clears the plan. Duplicate day/time pairs are folded together. A missing spring-forward time is skipped; an autumn repeated time uses its first occurrence once."},"if_empty":{"type":"boolean","description":"Only write if the current plan has no slots (atomic). Importers use this to avoid overwriting another writer; a nonempty plan returns conflict."}},"required":["zone","slots"],"additionalProperties":false}}}}}},"/api/v1/workspaces/{workspace}/connections/{id}/slots/next":{"get":{"operationId":"getNextConnectionSlots","summary":"Preview the next open exact times; does not reserve or schedule them","tags":["Connections"],"description":"Pages at 1000 times per response. When next is present, call again with after=next and count=remaining until remaining is zero. Remote MCP returns one page per call and tells you how to continue; Node CLI and stdio MCP follow all pages automatically. Who can call it: An API token with the \"read\" scope, or a member of this workspace signed in with a passkey.","security":[{"token":["read"]},{"session":[]}],"x-auth":"scope","x-scope":"read","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"after","in":"query","required":false,"schema":{"type":"string","format":"date-time","description":"Only times strictly after this instant. Defaults to now plus the existing two-minute scheduling lead time. Use the previous page's next to continue."},"description":"Only times strictly after this instant. Defaults to now plus the existing two-minute scheduling lead time. Use the previous page's next to continue."},{"name":"count","in":"query","required":false,"schema":{"type":"string","pattern":"^[1-9]\\d*$","description":"Total open slots requested. Default 5. A response contains up to 1000; next and remaining continue the request without a total-result cap."},"description":"Total open slots requested. Default 5. A response contains up to 1000; next and remaining continue the request without a total-result cap."}],"responses":{"200":{"description":"Preview the next open exact times; does not reserve or schedule them","content":{"application/json":{"schema":{"type":"object","properties":{"zone":{"type":"string"},"slots":{"type":"array","items":{"type":"string","format":"date-time"}},"next":{"type":["string","null"],"format":"date-time","description":"Continue with after equal to this timestamp and count equal to remaining; null when complete."},"remaining":{"type":"integer","minimum":0}},"required":["zone","slots","next","remaining"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/workspaces/{workspace}/connections/{id}/verify":{"post":{"operationId":"verifyConnection","summary":"Check a login still works and is the pinned account","tags":["Connections"],"description":"Reads the platform's identity endpoint; may refresh saved credentials or mark a login expired, and records X read usage. Who can call it: An API token with the \"read\" scope, or a member of this workspace signed in with a passkey.","security":[{"token":["read"]},{"session":[]}],"x-auth":"scope","x-scope":"read","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Check a login still works and is the pinned account","content":{"application/json":{"schema":{"type":"object","properties":{"at":{"type":"string","format":"date-time"},"ok":{"type":"boolean"},"account_matches":{"type":"boolean"},"error":{"type":"string"},"error_code":{"type":["string","null"]}},"required":["at","ok"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/workspaces/{workspace}/connections/authorize":{"post":{"operationId":"authorizeConnection","summary":"Start connecting (or reconnecting) an account as its owner; open the returned URL","tags":["Connections"],"description":"Who can call it: An owner of this workspace, signed in with a passkey (session cookie). API tokens are always refused (owner_session_required), whatever their scopes. Refused (402 workspace_inactive) once the workspace's plan has lapsed.","security":[{"session":[]}],"x-auth":"owner","x-plan":"not_lapsed","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}}],"responses":{"200":{"description":"Start connecting (or reconnecting) an account as its owner; open the returned URL","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string"}},"required":["url"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"platform":{"type":"string","enum":["x","linkedin","tiktok"]},"connection_id":{"type":"string","description":"Reconnect this connection. It accepts only the account it's pinned to."}},"required":["platform"],"additionalProperties":false}}}}}},"/api/v1/workspaces/{workspace}/connections/{platform}/start":{"post":{"operationId":"startConnection","summary":"Start connecting an account and get the sign-in link for its owner to open (agents can)","tags":["Connections"],"description":"Who can call it: An API token with the \"connections\" scope, or a member of this workspace signed in with a passkey. Refused (402 workspace_inactive) once the workspace's plan has lapsed.","security":[{"token":["connections"]},{"session":[]}],"x-auth":"scope","x-scope":"connections","x-plan":"not_lapsed","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"platform","in":"path","required":true,"schema":{"type":"string","enum":["x","linkedin","tiktok"]}}],"responses":{"200":{"description":"Start connecting an account and get the sign-in link for its owner to open (agents can)","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","description":"The platform's own sign-in and consent page. Give it to the account's owner; it works once, for 15 minutes."},"platform":{"type":"string"},"expires_at":{"type":"string","format":"date-time"}},"required":["url","platform","expires_at"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"connection_id":{"type":"string","description":"Reconnect this connection. It accepts only the account it's pinned to."}},"required":[],"additionalProperties":false}}}}}},"/api/v1/workspaces/{workspace}/connections/{id}":{"delete":{"operationId":"disconnectConnection","summary":"Disconnect an account: revokes the login at the platform where it can (X, TikTok) and deletes the stored tokens","tags":["Connections"],"description":"Who can call it: An owner of this workspace, signed in with a passkey (session cookie). API tokens are always refused (owner_session_required), whatever their scopes.","security":[{"session":[]}],"x-auth":"owner","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Disconnect an account: revokes the login at the platform where it can (X, TikTok) and deletes the stored tokens","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string"},"platform":{"type":"string"},"revoked":{"type":"boolean","description":"The platform confirmed the login is revoked. When false, also remove the app in the platform's settings."},"revoke_error":{"type":["string","null"]}},"required":["id","revoked"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/workspaces/{workspace}/connections/{id}/creator-info":{"post":{"operationId":"refreshCreatorInfo","summary":"TikTok: ask TikTok for the account's current posting options (creator info)","tags":["Connections"],"description":"Who can call it: An API token with the \"read\" scope, or a member of this workspace signed in with a passkey. Refused (402 workspace_inactive) once the workspace's plan has lapsed.","security":[{"token":["read"]},{"session":[]}],"x-auth":"scope","x-scope":"read","x-plan":"not_lapsed","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"TikTok: ask TikTok for the account's current posting options (creator info)","content":{"application/json":{"schema":{"type":"object","properties":{"creator":{"$ref":"#/components/schemas/TikTokCreatorInfo"},"audit_posture":{"type":"string"}},"required":["creator","audit_posture"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/workspaces/{workspace}/drafts":{"get":{"operationId":"listDrafts","summary":"List drafts, newest change first","tags":["Drafts"],"description":"Who can call it: An API token with the \"read\" scope, or a member of this workspace signed in with a passkey.","security":[{"token":["read"]},{"session":[]}],"x-auth":"scope","x-scope":"read","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"q","in":"query","required":false,"schema":{"type":"string","description":"Literal, case-insensitive substring in every part's text, titles and planning labels. Unicode letters are lowercased; surrounding whitespace is trimmed, blank means no search. Applied within this workspace before the result limit, together with the other filters. Archived drafts are excluded unless drafts explicitly requests archived=true."},"description":"Literal, case-insensitive substring in every part's text, titles and planning labels. Unicode letters are lowercased; surrounding whitespace is trimmed, blank means no search. Applied within this workspace before the result limit, together with the other filters. Archived drafts are excluded unless drafts explicitly requests archived=true."},{"name":"import_key","in":"query","required":false,"schema":{"type":"string","minLength":1,"description":"Stable source identity for an import, unique in this workspace. A repeat returns the current existing draft, including if edited or archived, without overwriting it. It never approves or schedules. Use the same key to recover a lost response; a different key intentionally makes another draft."},"description":"Stable source identity for an import, unique in this workspace. A repeat returns the current existing draft, including if edited or archived, without overwriting it. It never approves or schedules. Use the same key to recover a lost response; a different key intentionally makes another draft."},{"name":"label","in":"query","required":false,"schema":{"type":"string","description":"Only drafts with this exact label (case-sensitive). Combines with status and archived; applied before limit."},"description":"Only drafts with this exact label (case-sensitive). Combines with status and archived; applied before limit."},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["draft","approval_requested","changes_requested","approved"]}},{"name":"archived","in":"query","required":false,"schema":{"type":"string","enum":["true","false"]}},{"name":"limit","in":"query","required":false,"schema":{"type":"string","pattern":"^\\d{1,3}$"}}],"responses":{"200":{"description":"List drafts, newest change first","content":{"application/json":{"schema":{"type":"object","properties":{"drafts":{"type":"array","items":{"$ref":"#/components/schemas/Draft"}}},"required":["drafts"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"createDraft","summary":"Create a draft","tags":["Drafts"],"description":"Who can call it: An API token with the \"drafts\" scope, or a member of this workspace signed in with a passkey. Refused (402 workspace_inactive) once the workspace's plan has lapsed.","security":[{"token":["drafts"]},{"session":[]}],"x-auth":"scope","x-scope":"drafts","x-plan":"not_lapsed","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","minLength":1,"maxLength":255,"pattern":"^[\\x20-\\x7e]+$","description":"Any unique string, like a UUID. Sending the same key and request again answers what the first request answered (Idempotent-Replayed: true) and makes nothing twice; the same key with another request is refused (idempotency_key_reused).","examples":["b3f1c7e2-6f0a-4d1e-9c4a-2f8e5d7a1b90"]},"description":"Any unique string, like a UUID. Sending the same key and request again answers what the first request answered (Idempotent-Replayed: true) and makes nothing twice; the same key with another request is refused (idempotency_key_reused)."}],"responses":{"201":{"description":"Create a draft","headers":{"Idempotent-Replayed":{"description":"true when this is the answer an earlier request with the same Idempotency-Key got; nothing new was made.","schema":{"type":"string","enum":["true"]}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Draft"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"import_key":{"type":"string","minLength":1,"description":"Stable source identity for an import, unique in this workspace. A repeat returns the current existing draft, including if edited or archived, without overwriting it. It never approves or schedules. Use the same key to recover a lost response; a different key intentionally makes another draft."},"kind":{"type":"string","enum":["x_post","x_thread","linkedin_post","tiktok_photo","tiktok_video"]},"body":{"type":"object","description":"Shaped by kind.","oneOf":[{"$ref":"#/components/schemas/XPostBody"},{"$ref":"#/components/schemas/XThreadBody"},{"$ref":"#/components/schemas/LinkedInPostBody"},{"$ref":"#/components/schemas/TikTokPhotoBody"},{"$ref":"#/components/schemas/TikTokVideoBody"}]},"connection_id":{"type":["string","null"],"description":"The account it posts as: an active connection's id from GET …/connections, for the kind's platform."},"title":{"type":["string","null"],"maxLength":200,"description":"A title for the desk. Never posted."},"labels":{"$ref":"#/components/schemas/DraftLabels"},"post_at":{"type":["string","null"],"anyOf":[{"type":["string","null"],"format":"date-time"},{"type":"string","const":"next_slot"}],"description":"An exact RFC 3339 time with UTC offset, null, or \"next_slot\" (new drafts only). next_slot needs connection_id and a weekly plan, and is not available for TikTok (post_now only); creation atomically takes the next open time and returns an exact post_at. No approval or job is created."},"meta":{"$ref":"#/components/schemas/DraftMeta"}},"required":["kind","body"],"additionalProperties":false}}}}}},"/api/v1/workspaces/{workspace}/drafts/{id}":{"get":{"operationId":"getDraft","summary":"One draft, with its current approval and latest job","tags":["Drafts"],"description":"Who can call it: An API token with the \"read\" scope, or a member of this workspace signed in with a passkey.","security":[{"token":["read"]},{"session":[]}],"x-auth":"scope","x-scope":"read","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^drf_[a-z0-9]{16}$"}}],"responses":{"200":{"description":"One draft, with its current approval and latest job","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Draft"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"operationId":"updateDraft","summary":"Change a draft. Changing what would be posted voids its approval","tags":["Drafts"],"description":"Who can call it: An API token with the \"drafts\" scope, or a member of this workspace signed in with a passkey. Refused (402 workspace_inactive) once the workspace's plan has lapsed.","security":[{"token":["drafts"]},{"session":[]}],"x-auth":"scope","x-scope":"drafts","x-plan":"not_lapsed","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^drf_[a-z0-9]{16}$"}}],"responses":{"200":{"description":"Change a draft. Changing what would be posted voids its approval","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Draft"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"rev":{"type":"integer","description":"The rev you read. A stale rev is refused (rev_conflict)."},"kind":{"type":"string","enum":["x_post","x_thread"],"description":"Turn an X post into a thread or back; send the body in the new shape with it."},"body":{"type":"object","oneOf":[{"$ref":"#/components/schemas/XPostBody"},{"$ref":"#/components/schemas/XThreadBody"},{"$ref":"#/components/schemas/LinkedInPostBody"},{"$ref":"#/components/schemas/TikTokPhotoBody"},{"$ref":"#/components/schemas/TikTokVideoBody"}]},"archived":{"type":"boolean"},"connection_id":{"type":["string","null"],"description":"The account it posts as: an active connection's id from GET …/connections, for the kind's platform."},"title":{"type":["string","null"],"maxLength":200,"description":"A title for the desk. Never posted."},"labels":{"$ref":"#/components/schemas/DraftLabels"},"post_at":{"type":["string","null"],"format":"date-time","description":"When to post it, once approved for scheduling. RFC 3339 with a UTC offset (Z or ±HH:MM), like 2026-10-26T09:00:00-07:00."},"meta":{"$ref":"#/components/schemas/DraftMeta"}},"required":["rev"],"additionalProperties":false}}}}}},"/api/v1/workspaces/{workspace}/drafts/{id}/check":{"post":{"operationId":"checkDraft","summary":"Everything that would stop it posting exactly as previewed, with one-click fixes","tags":["Drafts"],"description":"Does not save a draft revision or approve or post it. TikTok checks may save media renditions, video facts, creator info or refreshed credentials. Who can call it: An API token with the \"read\" scope, or a member of this workspace signed in with a passkey.","security":[{"token":["read"]},{"session":[]}],"x-auth":"scope","x-scope":"read","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^drf_[a-z0-9]{16}$"}}],"responses":{"200":{"description":"Everything that would stop it posting exactly as previewed, with one-click fixes","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Check"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"mode":{"type":"string","enum":["approve","schedule","post_now"],"description":"approve: the decision only. schedule: post at post_at (or, with by_hand \"first\", as soon as post 1 is live). post_now: post right away (the only way TikTok posts go out). Left out: what the owner's approval page does with the draft: post_now with no post_at, schedule with one (or with by_hand \"first\"), approve with by_hand \"all\"."},"post_at":{"type":["string","null"],"format":"date-time","description":"Checks this time instead of the draft's post_at. RFC 3339 with a UTC offset (Z or ±HH:MM), like 2026-10-26T09:00:00-07:00."},"tiktok":{"type":"object","properties":{"privacy_level":{"type":"string","enum":["PUBLIC_TO_EVERYONE","MUTUAL_FOLLOW_FRIENDS","FOLLOWER_OF_CREATOR","SELF_ONLY"],"description":"Who can view it: one of the options TikTok gives for the account right now (check's owner_choices.privacy_level_options). Required to post directly."},"allow_comments":{"type":"boolean","description":"Off unless turned on. Not allowed when the account's TikTok settings turn comments off."},"allow_duet":{"type":"boolean","description":"Videos only. Off unless turned on."},"allow_stitch":{"type":"boolean","description":"Videos only. Off unless turned on."},"disclose":{"type":["object","null"],"properties":{"your_brand":{"type":"boolean","description":"Promoting the owner's own business: TikTok labels it \"Promotional content\"."},"branded_content":{"type":"boolean","description":"Promoting a third party: TikTok labels it \"Paid partnership\". Can't be Only me."}},"required":[],"additionalProperties":false,"description":"Disclose post content. Leave it out (or null) when it isn't commercial content; when present, at least one is true."}},"required":[],"additionalProperties":false,"description":"TikTok: the owner's posting-screen choices, to check them too. Left out, check judges everything else and lists the choices in owner_choices."}},"required":[],"additionalProperties":false}}}}}},"/api/v1/workspaces/{workspace}/link-cards":{"post":{"operationId":"createLinkCard","summary":"Fetch a page's link card and save its image to this workspace","tags":["Link cards"],"description":"Reads only the HTML head (Open Graph, then Twitter tags, then title/description). Returns a card ready for a linkedin_post body; does not create or change a draft. Images use the ordinary upload checks and retention. Missing metadata falls back to the page URL. Image failures return a warning and a text-only card. Public HTTP(S) only: no credentials, private/local addresses or LinkedIn page scraping; redirects and image URLs obey the same rules. No script runs, credentials are never forwarded, and approval/posting never fetch metadata again. Who can call it: An API token with the \"media\" scope, or a member of this workspace signed in with a passkey. Refused (402 workspace_inactive) once the workspace's plan has lapsed.","security":[{"token":["media"]},{"session":[]}],"x-auth":"scope","x-scope":"media","x-plan":"not_lapsed","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","minLength":1,"maxLength":255,"pattern":"^[\\x20-\\x7e]+$","description":"Any unique string, like a UUID. Sending the same key and request again answers what the first request answered (Idempotent-Replayed: true) and makes nothing twice; the same key with another request is refused (idempotency_key_reused).","examples":["b3f1c7e2-6f0a-4d1e-9c4a-2f8e5d7a1b90"]},"description":"Any unique string, like a UUID. Sending the same key and request again answers what the first request answered (Idempotent-Replayed: true) and makes nothing twice; the same key with another request is refused (idempotency_key_reused)."}],"responses":{"200":{"description":"Fetch a page's link card and save its image to this workspace","headers":{"Idempotent-Replayed":{"description":"true when this is the answer an earlier request with the same Idempotency-Key got; nothing new was made.","schema":{"type":"string","enum":["true"]}}},"content":{"application/json":{"schema":{"type":"object","properties":{"card":{"type":["object","null"],"properties":{"url":{"type":"string","pattern":"^https?://"},"title":{"type":"string","maxLength":400},"description":{"type":"string","maxLength":4000},"image_media_id":{"type":"string","pattern":"^med_[a-z0-9]{16}$"}},"required":["url"],"additionalProperties":false,"description":"A link card, used when there is no media. Can't accompany a video."},"media":{"$ref":"#/components/schemas/Media"},"warnings":{"type":"array","items":{"type":"string"}}},"required":["card","warnings"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"page_url":{"type":"string","description":"The public HTTP(S) page to link to, not the API origin.","examples":["https://example.com/article"]}},"required":["page_url"],"additionalProperties":false}}}}}},"/api/v1/workspaces/{workspace}/media/{id}/probe":{"get":{"operationId":"probeMedia","summary":"Read a stored file's dimensions and duration without changing its metadata","tags":["Media"],"description":"Reads this workspace's original stored bytes, not supplied metadata. JPEG dimensions include EXIF orientation; PNG and still WebP use the shared photo header reader; GIF uses its logical screen header. MP4/MOV dimensions support one enabled video track with integral tkhd dimensions and unscaled right-angle movie/track matrices. Unsupported or malformed fields are null, including animated WebP dimensions; this is not proof the file can decode or post. Duration uses the existing bounded mvhd reader. No bytes or metadata change; use updateMedia explicitly to save repaired fields. Who can call it: An API token with the \"read\" scope, or a member of this workspace signed in with a passkey.","security":[{"token":["read"]},{"session":[]}],"x-auth":"scope","x-scope":"read","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^med_[a-z0-9]{16}$"}}],"responses":{"200":{"description":"Read a stored file's dimensions and duration without changing its metadata","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MediaProbe"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/workspaces/{workspace}/media":{"post":{"operationId":"uploadMedia","summary":"Upload a photo, video or PDF document (the raw bytes as the body)","tags":["Media"],"description":"Who can call it: An API token with the \"media\" scope, or a member of this workspace signed in with a passkey. Refused (402 workspace_inactive) once the workspace's plan has lapsed.","security":[{"token":["media"]},{"session":[]}],"x-auth":"scope","x-scope":"media","x-plan":"not_lapsed","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"alt","in":"query","required":false,"schema":{"type":"string","maxLength":4086}},{"name":"filename","in":"query","required":false,"schema":{"type":"string","maxLength":200}},{"name":"width","in":"query","required":false,"schema":{"type":"string","pattern":"^(?!0+$)\\d{1,5}$","description":"The image's width in pixels, for the previews: digits only, without px, at least 1. Leave it out if you don't know it.","examples":["800"]},"description":"The image's width in pixels, for the previews: digits only, without px, at least 1. Leave it out if you don't know it."},{"name":"height","in":"query","required":false,"schema":{"type":"string","pattern":"^(?!0+$)\\d{1,5}$","description":"The image's height in pixels, for the previews: digits only, without px, at least 1. Leave it out if you don't know it.","examples":["600"]},"description":"The image's height in pixels, for the previews: digits only, without px, at least 1. Leave it out if you don't know it."},{"name":"duration","in":"query","required":false,"schema":{"type":"string","pattern":"^(?!0+(\\.0+)?$)\\d{1,5}(\\.\\d{1,3})?$","description":"A video's length in seconds, without a unit suffix, more than 0.","examples":["12.5"]},"description":"A video's length in seconds, without a unit suffix, more than 0."},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","minLength":1,"maxLength":255,"pattern":"^[\\x20-\\x7e]+$","description":"Any unique string, like a UUID. Sending the same key and request again answers what the first request answered (Idempotent-Replayed: true) and makes nothing twice; the same key with another request is refused (idempotency_key_reused).","examples":["b3f1c7e2-6f0a-4d1e-9c4a-2f8e5d7a1b90"]},"description":"Any unique string, like a UUID. Sending the same key and request again answers what the first request answered (Idempotent-Replayed: true) and makes nothing twice; the same key with another request is refused (idempotency_key_reused)."}],"responses":{"201":{"description":"Upload a photo, video or PDF document (the raw bytes as the body)","headers":{"Idempotent-Replayed":{"description":"true when this is the answer an earlier request with the same Idempotency-Key got; nothing new was made.","schema":{"type":"string","enum":["true"]}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Media"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"description":"The file's raw bytes, with its Content-Type.","content":{"image/jpeg":{"schema":{"type":"string","format":"binary"}},"image/png":{"schema":{"type":"string","format":"binary"}},"image/webp":{"schema":{"type":"string","format":"binary"}},"image/gif":{"schema":{"type":"string","format":"binary"}},"video/mp4":{"schema":{"type":"string","format":"binary"}},"video/quicktime":{"schema":{"type":"string","format":"binary"}},"application/pdf":{"schema":{"type":"string","format":"binary"}}}}},"get":{"operationId":"listMedia","summary":"The media library (uploads; a slideshow's rendered slides are in its render)","tags":["Media"],"description":"Who can call it: An API token with the \"read\" scope, or a member of this workspace signed in with a passkey.","security":[{"token":["read"]},{"session":[]}],"x-auth":"scope","x-scope":"read","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}}],"responses":{"200":{"description":"The media library (uploads; a slideshow's rendered slides are in its render)","content":{"application/json":{"schema":{"type":"object","properties":{"media":{"type":"array","items":{"$ref":"#/components/schemas/Media"}}},"required":["media"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/workspaces/{workspace}/media/{id}":{"get":{"operationId":"getMedia","summary":"One media item","tags":["Media"],"description":"Who can call it: An API token with the \"read\" scope, or a member of this workspace signed in with a passkey.","security":[{"token":["read"]},{"session":[]}],"x-auth":"scope","x-scope":"read","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^med_[a-z0-9]{16}$"}}],"responses":{"200":{"description":"One media item","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Media"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"operationId":"updateMedia","summary":"Change a media item's default alt text (drafts keep their own copy)","tags":["Media"],"description":"Who can call it: An API token with the \"media\" scope, or a member of this workspace signed in with a passkey. Refused (402 workspace_inactive) once the workspace's plan has lapsed.","security":[{"token":["media"]},{"session":[]}],"x-auth":"scope","x-scope":"media","x-plan":"not_lapsed","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^med_[a-z0-9]{16}$"}}],"responses":{"200":{"description":"Change a media item's default alt text (drafts keep their own copy)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Media"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"alt":{"type":["string","null"],"maxLength":4086},"filename":{"type":["string","null"],"maxLength":200},"width":{"type":["integer","null"],"minimum":1,"maximum":99999},"height":{"type":["integer","null"],"minimum":1,"maximum":99999},"duration":{"type":["number","null"],"exclusiveMinimum":0,"description":"A video's length in seconds."}},"required":[],"additionalProperties":false}}}}}},"/api/v1/workspaces/{workspace}/media/{id}/content":{"get":{"operationId":"getMediaContent","summary":"The file itself, or the copy made for a platform","tags":["Media"],"description":"Who can call it: An API token with the \"read\" scope, or a member of this workspace signed in with a passkey.","security":[{"token":["read"]},{"session":[]}],"x-auth":"scope","x-scope":"read","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^med_[a-z0-9]{16}$"}},{"name":"rendition","in":"query","required":false,"schema":{"type":"string","enum":["tiktok"],"description":"tiktok: the photo exactly as it goes to TikTok (made when a TikTok draft using it is checked)."},"description":"tiktok: the photo exactly as it goes to TikTok (made when a TikTok draft using it is checked)."}],"responses":{"200":{"description":"The file itself, or the copy made for a platform","content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/workspaces/{workspace}/approval-requests":{"post":{"operationId":"requestApproval","summary":"Ask the owner to approve checked drafts (a plan); returns the owner's approval URL","tags":["Approvals"],"description":"Who can call it: An API token with the \"request-approval\" scope, or a member of this workspace signed in with a passkey. Refused (402 workspace_inactive) once the workspace's plan has lapsed.","security":[{"token":["request-approval"]},{"session":[]}],"x-auth":"scope","x-scope":"request-approval","x-plan":"not_lapsed","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}}],"responses":{"200":{"description":"Ask the owner to approve checked drafts (a plan); returns the owner's approval URL","content":{"application/json":{"schema":{"type":"object","properties":{"approval_url":{"type":"string"},"items":{"type":"array","items":{"type":"object","properties":{"draft_id":{"type":"string"},"rev":{"type":"integer"},"mode":{"type":"string","enum":["approve","schedule","post_now"],"description":"approve: the decision only. schedule: post at post_at (or, with by_hand \"first\", as soon as post 1 is live). post_now: post right away (the only way TikTok posts go out)."},"snapshot_hash":{"type":"string"}},"required":[],"additionalProperties":false}},"note":{"type":["string","null"]}},"required":["approval_url","items"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"draft_id":{"type":"string","pattern":"^drf_[a-z0-9]{16}$"},"rev":{"type":"integer"},"mode":{"type":"string","enum":["approve","schedule","post_now"],"description":"approve: the decision only. schedule: post at post_at (or, with by_hand \"first\", as soon as post 1 is live). post_now: post right away (the only way TikTok posts go out). Left out: what the owner's approval page does with the draft: post_now with no post_at, schedule with one (or with by_hand \"first\"), approve with by_hand \"all\"."}},"required":["draft_id","rev"],"additionalProperties":false},"minItems":1,"maxItems":50},"note":{"type":"string","maxLength":2000,"description":"Shown to the owner with the request."}},"required":["items"],"additionalProperties":false}}}}},"get":{"operationId":"listApprovalRequests","summary":"Drafts waiting for the owner's approval, with each request","tags":["Approvals"],"description":"Who can call it: An API token with the \"read\" scope, or a member of this workspace signed in with a passkey.","security":[{"token":["read"]},{"session":[]}],"x-auth":"scope","x-scope":"read","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}}],"responses":{"200":{"description":"Drafts waiting for the owner's approval, with each request","content":{"application/json":{"schema":{"type":"object","properties":{"requests":{"type":"array","items":{"type":"object","properties":{"draft":{"$ref":"#/components/schemas/Draft"},"event":{"$ref":"#/components/schemas/Event"}},"required":[],"additionalProperties":false}}},"required":["requests"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/workspaces/{workspace}/approval-requests/send-back":{"post":{"operationId":"sendBackDrafts","summary":"Owner: send requested drafts back to the agent with a note","tags":["Approvals"],"description":"A signed-in workspace owner only; no fresh passkey assertion. API tokens and editors cannot send drafts back. Every item must still be waiting for approval at the named rev. Advances each rev, removes the request, voids prior approvals and records draft.changes_requested with the note. Creates no job and never posts. Who can call it: An owner of this workspace, signed in with a passkey (session cookie). API tokens are always refused (owner_session_required), whatever their scopes.","security":[{"session":[]}],"x-auth":"owner","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}}],"responses":{"200":{"description":"Owner: send requested drafts back to the agent with a note","content":{"application/json":{"schema":{"type":"object","properties":{"drafts":{"type":"array","items":{"$ref":"#/components/schemas/Draft"}}},"required":["drafts"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"draft_id":{"type":"string","pattern":"^drf_[a-z0-9]{16}$"},"rev":{"type":"integer"}},"required":["draft_id","rev"],"additionalProperties":false},"minItems":1},"note":{"type":"string","minLength":1,"description":"What the agent should change; must contain more than whitespace."}},"required":["items","note"],"additionalProperties":false}}}}}},"/api/v1/workspaces/{workspace}/approvals/options":{"post":{"operationId":"startApproval","summary":"Freeze drafts into snapshots and get the passkey challenge over their hashes","tags":["Approvals"],"description":"Who can call it: An owner of this workspace, signed in with a passkey (session cookie). API tokens are always refused (owner_session_required), whatever their scopes. The workspace's plan must be active (402 workspace_inactive otherwise).","security":[{"session":[]}],"x-auth":"owner","x-plan":"active","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}}],"responses":{"200":{"description":"Freeze drafts into snapshots and get the passkey challenge over their hashes","content":{"application/json":{"schema":{"type":"object","properties":{"challenge_id":{"type":"string"},"options":{"type":"object","description":"The browser's PublicKeyCredential, as JSON (what @simplewebauthn/browser returns)."},"nonce":{"type":"string"},"snapshot_hashes":{"type":"array","items":{"type":"string"}},"items":{"type":"array","items":{"type":"object","properties":{"draft_id":{"type":"string"},"rev":{"type":"integer"},"mode":{"type":"string","enum":["approve","schedule","post_now"],"description":"approve: the decision only. schedule: post at post_at (or, with by_hand \"first\", as soon as post 1 is live). post_now: post right away (the only way TikTok posts go out)."},"snapshot_hash":{"type":"string"},"snapshot":{"$ref":"#/components/schemas/Snapshot"}},"required":[],"additionalProperties":false}}},"required":["challenge_id","options","items"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"draft_id":{"type":"string","pattern":"^drf_[a-z0-9]{16}$"},"rev":{"type":"integer"},"mode":{"type":"string","enum":["approve","schedule","post_now"],"description":"approve: the decision only. schedule: post at post_at (or, with by_hand \"first\", as soon as post 1 is live). post_now: post right away (the only way TikTok posts go out)."},"post_at":{"type":["string","null"],"format":"date-time","description":"schedule only: overrides the draft's post_at. RFC 3339 with a UTC offset (Z or ±HH:MM), like 2026-10-26T09:00:00-07:00."},"thread":{"type":"object","properties":{"repost_live":{"type":"boolean","description":"Owner only: explicitly post the whole X thread again, knowing its recorded prefix is already live and X cannot edit it. Default false: continue after that prefix. Frozen with the live ids and parts into the passkey snapshot."}},"required":["repost_live"],"additionalProperties":false},"tiktok":{"type":"object","properties":{"privacy_level":{"type":"string","enum":["PUBLIC_TO_EVERYONE","MUTUAL_FOLLOW_FRIENDS","FOLLOWER_OF_CREATOR","SELF_ONLY"],"description":"Who can view it: one of the options TikTok gives for the account right now (check's owner_choices.privacy_level_options). Required to post directly."},"allow_comments":{"type":"boolean","description":"Off unless turned on. Not allowed when the account's TikTok settings turn comments off."},"allow_duet":{"type":"boolean","description":"Videos only. Off unless turned on."},"allow_stitch":{"type":"boolean","description":"Videos only. Off unless turned on."},"disclose":{"type":["object","null"],"properties":{"your_brand":{"type":"boolean","description":"Promoting the owner's own business: TikTok labels it \"Promotional content\"."},"branded_content":{"type":"boolean","description":"Promoting a third party: TikTok labels it \"Paid partnership\". Can't be Only me."}},"required":[],"additionalProperties":false,"description":"Disclose post content. Leave it out (or null) when it isn't commercial content; when present, at least one is true."}},"required":[],"additionalProperties":false,"description":"TikTok drafts posted directly: the owner's choices on TikTok's posting screen. They're frozen into the snapshot with TikTok's current settings and the consent statement."}},"required":["draft_id","rev","mode"],"additionalProperties":false},"minItems":1,"maxItems":50}},"required":["items"],"additionalProperties":false}}}}}},"/api/v1/workspaces/{workspace}/approvals":{"post":{"operationId":"approve","summary":"Approve with the passkey assertion: stores the approvals, then schedules or posts what they cover","tags":["Approvals"],"description":"Who can call it: An owner of this workspace, signed in with a passkey (session cookie). API tokens are always refused (owner_session_required), whatever their scopes. The workspace's plan must be active (402 workspace_inactive otherwise).","security":[{"session":[]}],"x-auth":"owner","x-plan":"active","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}}],"responses":{"201":{"description":"Approve with the passkey assertion: stores the approvals, then schedules or posts what they cover","content":{"application/json":{"schema":{"type":"object","properties":{"approvals":{"type":"array","items":{"$ref":"#/components/schemas/Approval"}},"jobs":{"type":"array","items":{"type":"object","description":"A Job, or { draft_id, error } when its job couldn't be created."}}},"required":["approvals","jobs"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"challenge_id":{"type":"string"},"response":{"type":"object","description":"The browser's PublicKeyCredential, as JSON (what @simplewebauthn/browser returns)."}},"required":["challenge_id","response"],"additionalProperties":false}}}}},"get":{"operationId":"listApprovals","summary":"Approvals, newest first","tags":["Approvals"],"description":"Who can call it: An API token with the \"read\" scope, or a member of this workspace signed in with a passkey.","security":[{"token":["read"]},{"session":[]}],"x-auth":"scope","x-scope":"read","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"draft_id","in":"query","required":false,"schema":{"type":"string","pattern":"^drf_[a-z0-9]{16}$"}}],"responses":{"200":{"description":"Approvals, newest first","content":{"application/json":{"schema":{"type":"object","properties":{"approvals":{"type":"array","items":{"$ref":"#/components/schemas/Approval"}}},"required":["approvals"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/workspaces/{workspace}/posts":{"post":{"operationId":"createPost","summary":"Draft, check and request approval in one call: the owner's approval URL, or each draft's problems and fixes","tags":["Posts"],"description":"Each draft is created (or changed) as POST …/drafts makes it, checked as POST …/drafts/{id}/check checks it, and when every check passes, approval is requested for them all as one plan, as POST …/approval-requests requests it. When a check fails, nothing is requested: the answer has the saved drafts and each one's problems and fixes; apply them and send the drafts again by draft_id and rev. It never approves, schedules or posts: the owner approves at approval_url with their passkey. Who can call it: An API token with the \"drafts\" and \"request-approval\" scopes, or a member of this workspace signed in with a passkey. Refused (402 workspace_inactive) once the workspace's plan has lapsed.","security":[{"token":["drafts","request-approval"]},{"session":[]}],"x-auth":"scope","x-scope":["drafts","request-approval"],"x-plan":"not_lapsed","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","minLength":1,"maxLength":255,"pattern":"^[\\x20-\\x7e]+$","description":"Any unique string, like a UUID. Sending the same key and request again answers what the first request answered (Idempotent-Replayed: true) and makes nothing twice; the same key with another request is refused (idempotency_key_reused).","examples":["b3f1c7e2-6f0a-4d1e-9c4a-2f8e5d7a1b90"]},"description":"Any unique string, like a UUID. Sending the same key and request again answers what the first request answered (Idempotent-Replayed: true) and makes nothing twice; the same key with another request is refused (idempotency_key_reused)."}],"responses":{"200":{"description":"Draft, check and request approval in one call: the owner's approval URL, or each draft's problems and fixes","headers":{"Idempotent-Replayed":{"description":"true when this is the answer an earlier request with the same Idempotency-Key got; nothing new was made.","schema":{"type":"string","enum":["true"]}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Post"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"drafts":{"type":"array","items":{"$ref":"#/components/schemas/PostDraft"},"minItems":1,"maxItems":50,"description":"One plan: every post the owner approves with one passkey tap."},"note":{"type":"string","maxLength":2000,"description":"Shown to the owner with the request: who posts which part, when, and what they do by hand."}},"required":["drafts"],"additionalProperties":false}}}}}},"/api/v1/workspaces/{workspace}/jobs":{"get":{"operationId":"listJobs","summary":"Jobs, newest first","tags":["Jobs"],"description":"Who can call it: An API token with the \"read\" scope, or a member of this workspace signed in with a passkey.","security":[{"token":["read"]},{"session":[]}],"x-auth":"scope","x-scope":"read","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"q","in":"query","required":false,"schema":{"type":"string","description":"Literal, case-insensitive substring in every part's text, titles and planning labels. Unicode letters are lowercased; surrounding whitespace is trimmed, blank means no search. Applied within this workspace before the result limit, together with the other filters. Archived drafts are excluded unless drafts explicitly requests archived=true."},"description":"Literal, case-insensitive substring in every part's text, titles and planning labels. Unicode letters are lowercased; surrounding whitespace is trimmed, blank means no search. Applied within this workspace before the result limit, together with the other filters. Archived drafts are excluded unless drafts explicitly requests archived=true."},{"name":"draft_id","in":"query","required":false,"schema":{"type":"string"}},{"name":"state","in":"query","required":false,"schema":{"type":"string","enum":["waiting","scheduled","held","posting","processing","posted","inbox","failed","canceled"]}}],"responses":{"200":{"description":"Jobs, newest first","content":{"application/json":{"schema":{"type":"object","properties":{"jobs":{"type":"array","items":{"$ref":"#/components/schemas/Job"}}},"required":["jobs"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/workspaces/{workspace}/jobs/{id}":{"get":{"operationId":"getJob","summary":"One job","tags":["Jobs"],"description":"Who can call it: An API token with the \"read\" scope, or a member of this workspace signed in with a passkey.","security":[{"token":["read"]},{"session":[]}],"x-auth":"scope","x-scope":"read","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"One job","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/workspaces/{workspace}/jobs/{id}/retry":{"post":{"operationId":"retryHeldJob","summary":"Owner: retry a job held for X credits or a login, preserving its approval and already-posted ids","tags":["Jobs"],"description":"Who can call it: An owner of this workspace, signed in with a passkey (session cookie). API tokens are always refused (owner_session_required), whatever their scopes. The workspace's plan must be active (402 workspace_inactive otherwise).","security":[{"session":[]}],"x-auth":"owner","x-plan":"active","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Owner: retry a job held for X credits or a login, preserving its approval and already-posted ids","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/workspaces/{workspace}/jobs/{id}/cancel":{"post":{"operationId":"cancelJob","summary":"Cancel a waiting, scheduled, held or failed job, a processing LinkedIn video, or a posted job's pending repost (the original stays live)","tags":["Jobs"],"description":"Who can call it: An API token with the \"cancel\" scope, or a member of this workspace signed in with a passkey.","security":[{"token":["cancel"]},{"session":[]}],"x-auth":"scope","x-scope":"cancel","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Cancel a waiting, scheduled, held or failed job, a processing LinkedIn video, or a posted job's pending repost (the original stays live)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/workspaces/{workspace}/jobs/{id}/take-down/options":{"post":{"operationId":"startTakeDown","summary":"Owner: prepare a passkey take-down of this job's published X or LinkedIn posts (TikTok's API cannot delete)","tags":["Jobs"],"description":"Who can call it: An owner of this workspace, signed in with a passkey (session cookie). API tokens are always refused (owner_session_required), whatever their scopes.","security":[{"session":[]}],"x-auth":"owner","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Owner: prepare a passkey take-down of this job's published X or LinkedIn posts (TikTok's API cannot delete)","content":{"application/json":{"schema":{"type":"object","properties":{"challenge_id":{"type":"string"},"options":{"type":"object","description":"The browser's PublicKeyCredential, as JSON (what @simplewebauthn/browser returns)."},"snapshot":{"type":"object","description":"The exact workspace, job, platform, connection, account and published ids to delete."},"snapshot_hash":{"type":"string"},"nonce":{"type":"string"}},"required":["challenge_id","options","snapshot","snapshot_hash","nonce"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/workspaces/{workspace}/jobs/{id}/take-down":{"post":{"operationId":"takeDown","summary":"Owner: delete the published posts with a fresh passkey assertion; journals each part, safely retries remaining parts","tags":["Jobs"],"description":"Who can call it: An owner of this workspace, signed in with a passkey (session cookie). API tokens are always refused (owner_session_required), whatever their scopes.","security":[{"session":[]}],"x-auth":"owner","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Owner: delete the published posts with a fresh passkey assertion; journals each part, safely retries remaining parts","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"challenge_id":{"type":"string"},"response":{"type":"object","description":"Your passkey's assertion over the challenge from the matching …/options call."}},"required":["challenge_id","response"],"additionalProperties":false}}}}}},"/api/v1/workspaces/{workspace}/jobs/{id}/first":{"post":{"operationId":"checkFirstPost","summary":"A hand-first thread: look for post 1 now (or take its link) and post the rest under it","tags":["Jobs"],"description":"Who can call it: An owner of this workspace, signed in with a passkey (session cookie). API tokens are always refused (owner_session_required), whatever their scopes. The workspace's plan must be active (402 workspace_inactive otherwise).","security":[{"session":[]}],"x-auth":"owner","x-plan":"active","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"A hand-first thread: look for post 1 now (or take its link) and post the rest under it","content":{"application/json":{"schema":{"type":"object","properties":{"job":{"$ref":"#/components/schemas/Job"},"found":{"type":"boolean"}},"required":["job","found"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","description":"The link of post 1 on X."}},"required":[],"additionalProperties":false}}}}}},"/api/v1/workspaces/{workspace}/jobs/{id}/verify":{"post":{"operationId":"verifyJob","summary":"Read a posted X job back and compare it with the approval","tags":["Jobs"],"description":"Saves the live-check result and updates the job's timestamp; may refresh credentials and records X read usage. Never posts. Who can call it: An API token with the \"read\" scope, or a member of this workspace signed in with a passkey.","security":[{"token":["read"]},{"session":[]}],"x-auth":"scope","x-scope":"read","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Read a posted X job back and compare it with the approval","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/workspaces/{workspace}/jobs/{id}/status":{"post":{"operationId":"checkJobStatus","summary":"TikTok: read the attempt's status from TikTok again (never sends it again)","tags":["Jobs"],"description":"While processing, saves the polled result and job state. Final outcomes also remove pull links and record events for webhook delivery. Who can call it: An API token with the \"read\" scope, or a member of this workspace signed in with a passkey. Refused (402 workspace_inactive) once the workspace's plan has lapsed.","security":[{"token":["read"]},{"session":[]}],"x-auth":"scope","x-scope":"read","x-plan":"not_lapsed","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"TikTok: read the attempt's status from TikTok again (never sends it again)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/workspaces/{workspace}/jobs/{id}/metrics":{"get":{"operationId":"listJobMetrics","summary":"How a posted job performed: every record, newest first, with rates, and for X its numbers from X","tags":["Jobs"],"description":"Who can call it: An API token with the \"read\" scope, or a member of this workspace signed in with a passkey.","security":[{"token":["read"]},{"session":[]}],"x-auth":"scope","x-scope":"read","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"How a posted job performed: every record, newest first, with rates, and for X its numbers from X","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostMetricsReport"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"recordJobMetrics","summary":"Record how a posted job performed (TikTok's posting access doesn't read analytics)","tags":["Jobs"],"description":"Who can call it: An API token with the \"drafts\" scope, or a member of this workspace signed in with a passkey. Refused (402 workspace_inactive) once the workspace's plan has lapsed.","security":[{"token":["drafts"]},{"session":[]}],"x-auth":"scope","x-scope":"drafts","x-plan":"not_lapsed","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","minLength":1,"maxLength":255,"pattern":"^[\\x20-\\x7e]+$","description":"Any unique string, like a UUID. Sending the same key and request again answers what the first request answered (Idempotent-Replayed: true) and makes nothing twice; the same key with another request is refused (idempotency_key_reused).","examples":["b3f1c7e2-6f0a-4d1e-9c4a-2f8e5d7a1b90"]},"description":"Any unique string, like a UUID. Sending the same key and request again answers what the first request answered (Idempotent-Replayed: true) and makes nothing twice; the same key with another request is refused (idempotency_key_reused)."}],"responses":{"201":{"description":"Record how a posted job performed (TikTok's posting access doesn't read analytics)","headers":{"Idempotent-Replayed":{"description":"true when this is the answer an earlier request with the same Idempotency-Key got; nothing new was made.","schema":{"type":"string","enum":["true"]}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostMetricsReport"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"views":{"type":["integer","null"],"minimum":0},"likes":{"type":["integer","null"],"minimum":0},"comments":{"type":["integer","null"],"minimum":0},"shares":{"type":["integer","null"],"minimum":0},"saves":{"type":["integer","null"],"minimum":0},"average_watch_time_sec":{"type":["number","null"],"minimum":0},"completion_rate":{"type":["number","null"],"minimum":0,"maximum":100},"is_winning_post":{"type":["boolean","null"]},"notes":{"type":["string","null"],"maxLength":2000}},"required":[],"additionalProperties":false}}}}}},"/api/v1/workspaces/{workspace}/job-results":{"get":{"operationId":"listJobResults","summary":"How posted X jobs did: each post's latest views, likes, reposts, replies, quotes and bookmarks, newest first","tags":["Jobs"],"description":"From the reads best-times learning already makes (each post under 8 days old every hour, then once more), so it reads nothing from X. An account that doesn't learn answers not_measured, never zeros. Newest posted first, 200 a page: for the next page, pass the last job's posted_at as posted_before. Who can call it: An API token with the \"read\" scope, or a member of this workspace signed in with a passkey.","security":[{"token":["read"]},{"session":[]}],"x-auth":"scope","x-scope":"read","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"posted_after","in":"query","required":false,"schema":{"type":"string","format":"date-time","description":"Only jobs posted at or after this time, for example a week ago."},"description":"Only jobs posted at or after this time, for example a week ago."},{"name":"posted_before","in":"query","required":false,"schema":{"type":"string","format":"date-time","description":"Only jobs posted before this time."},"description":"Only jobs posted before this time."},{"name":"connection_id","in":"query","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"How posted X jobs did: each post's latest views, likes, reposts, replies, quotes and bookmarks, newest first","content":{"application/json":{"schema":{"type":"object","properties":{"jobs":{"type":"array","items":{"$ref":"#/components/schemas/JobResults"}}},"required":["jobs"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/workspaces/{workspace}/top-posts":{"get":{"operationId":"listTopPosts","summary":"The workspace's best X posts of the last days, ranked by views, likes or replies, with their text","tags":["Jobs"],"description":"For drafting from what worked. From the reads best-times learning already makes (each post under 8 days old every hour, then once more), so it reads nothing from X. A thread is ranked by post 1, whose numbers it carries. Posts without numbers yet (gone out since the last hourly read, or on an account that doesn't learn) come last, newest first, with null numbers and why, never zeros. Who can call it: An API token with the \"read\" scope, or a member of this workspace signed in with a passkey.","security":[{"token":["read"]},{"session":[]}],"x-auth":"scope","x-scope":"read","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"days","in":"query","required":false,"schema":{"type":"string","pattern":"^[1-9]\\d*$","description":"Posts that went out in the last this many days. 30 when left out."},"description":"Posts that went out in the last this many days. 30 when left out."},{"name":"by","in":"query","required":false,"schema":{"type":"string","enum":["views","likes","replies"],"description":"Post 1's latest views (the default), likes or replies."},"description":"Post 1's latest views (the default), likes or replies."},{"name":"connection_id","in":"query","required":false,"schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"type":"string","pattern":"^[1-9]\\d*$","description":"Only the best this many. All of them when left out."},"description":"Only the best this many. All of them when left out."}],"responses":{"200":{"description":"The workspace's best X posts of the last days, ranked by views, likes or replies, with their text","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TopPosts"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/workspaces/{workspace}/events":{"get":{"operationId":"listEvents","summary":"The audit log: who did what; after follows new events in order","tags":["Events"],"description":"Newest first by default. With after (start at 0), oldest unseen first and next_after is the next cursor; keep following until caught up. draft_id includes events about that draft and its jobs. Retry-After is a polling hint, not an enforced limit: 0 to drain a full page, otherwise the jobs cron's 60-second cadence. Prefer a webhook to avoid polling in an agent conversation. Who can call it: An API token with the \"read\" scope, or a member of this workspace signed in with a passkey.","security":[{"token":["read"]},{"session":[]}],"x-auth":"scope","x-scope":"read","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"action","in":"query","required":false,"schema":{"type":"string"}},{"name":"target","in":"query","required":false,"schema":{"type":"string","description":"Only events about this draft, job, connection or other item id."},"description":"Only events about this draft, job, connection or other item id."},{"name":"draft_id","in":"query","required":false,"schema":{"type":"string","pattern":"^drf_[a-z0-9]{16}$"}},{"name":"after","in":"query","required":false,"schema":{"type":"string","pattern":"^\\d+$","description":"Only events with a larger id, oldest first; start at 0 and follow next_after."},"description":"Only events with a larger id, oldest first; start at 0 and follow next_after."},{"name":"before","in":"query","required":false,"schema":{"type":"string","pattern":"^\\d+$","description":"Only events with a smaller id (paging)."},"description":"Only events with a smaller id (paging)."},{"name":"limit","in":"query","required":false,"schema":{"type":"string","pattern":"^\\d{1,3}$"}}],"responses":{"200":{"description":"The audit log: who did what; after follows new events in order","headers":{"Retry-After":{"description":"Seconds before following for new events (0 while draining a full page).","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"events":{"type":"array","items":{"$ref":"#/components/schemas/Event"}},"next_after":{"type":"string","description":"With after: the last returned id, or the input cursor if no events matched."}},"required":["events"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/tools/text":{"post":{"operationId":"analyzeText","summary":"Analyze X or LinkedIn text with the native previewer's text rules","tags":["Tools"],"x-read-only":true,"description":"Pure text analysis: nothing is saved, fetched, approved, scheduled or posted. URL spans and counts are not profile checks, a saved-draft check or a visual proof. LinkedIn line wrapping and its visual 'more' cut are browser-only. Who can call it: No sign-in needed.","security":[],"x-auth":"public","responses":{"200":{"description":"Analyze X or LinkedIn text with the native previewer's text rules","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TextAnalysis"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnalyzeTextInput"}}}}}},"/api/v1/tools/x-thread":{"post":{"operationId":"splitXThread","summary":"Split text into an X thread, optionally remapping or moving its splits","tags":["Tools"],"x-read-only":true,"description":"The free thread maker's exact algorithm: 280 weighted characters per post including optional i/n, preferred paragraph/line/sentence breaks, intact links and graphemes, and --- lines forcing breaks. No drafts or jobs are created and no posting limit is changed. Remap supplied pins from remap.before to text first, split, then optionally move one boundary and split again. Omit pins to reset splits. This utility has no post-count ceiling; a saved x_thread draft's existing limits still apply. Who can call it: No sign-in needed.","security":[],"x-auth":"public","responses":{"200":{"description":"Split text into an X thread, optionally remapping or moving its splits","content":{"application/json":{"schema":{"$ref":"#/components/schemas/XThreadSplit"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SplitXThreadInput"}}}}}},"/api/v1/workspaces/{workspace}/webhooks":{"post":{"operationId":"createWebhook","summary":"Register an HTTPS event receiver; its signing secret is shown once","tags":["Webhooks"],"description":"Future matching audit events are queued atomically and delivered on the jobs cron. Standard Webhooks headers sign the exact JSON: HMAC-SHA256 with the base64-decoded whsec_ secret over Webhook-Id + '.' + Webhook-Timestamp + '.' + raw body. A receiver must verify the signature and timestamp and deduplicate by delivery id. Any 2xx accepts it; redirects are not followed. Failures retry on subsequent ticks without a retry cutoff. Removing an endpoint cancels pending attempts, but a request already in flight can still arrive. No approval, scheduling or posting authority is granted. Who can call it: An API token with the \"webhooks\" scope, or an owner of this workspace signed in with a passkey. Refused (402 workspace_inactive) once the workspace's plan has lapsed.","security":[{"token":["webhooks"]},{"session":[]}],"x-auth":"scope","x-scope":"webhooks","x-plan":"not_lapsed","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","minLength":1,"maxLength":255,"pattern":"^[\\x20-\\x7e]+$","description":"Any unique string, like a UUID. Sending the same key and request again answers what the first request answered (Idempotent-Replayed: true) and makes nothing twice; the same key with another request is refused (idempotency_key_reused).","examples":["b3f1c7e2-6f0a-4d1e-9c4a-2f8e5d7a1b90"]},"description":"Any unique string, like a UUID. Sending the same key and request again answers what the first request answered (Idempotent-Replayed: true) and makes nothing twice; the same key with another request is refused (idempotency_key_reused)."}],"responses":{"201":{"description":"Register an HTTPS event receiver; its signing secret is shown once","headers":{"Idempotent-Replayed":{"description":"true when this is the answer an earlier request with the same Idempotency-Key got; nothing new was made.","schema":{"type":"string","enum":["true"]}}},"content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","pattern":"^whk_[a-z0-9]{16}$"},"endpoint_url":{"type":"string"},"actions":{"type":"array","items":{"type":"string"}},"created_by":{"type":["string","null"],"description":"The owner who registered this endpoint or created its token."},"token_id":{"type":["string","null"],"description":"A registering token; revoking it also removes its endpoints."},"created_at":{"type":"string","format":"date-time"},"removed_at":{"type":["string","null"],"format":"date-time"},"secret":{"type":"string","description":"Save this now: whsec_ followed by the base64 signing key. It is never returned again, except to the same caller resending the same request with its Idempotency-Key."}},"required":["id","endpoint_url","actions","created_at","removed_at","secret"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"endpoint_url":{"type":"string","format":"uri","description":"An HTTPS address you control, without embedded credentials or a fragment."},"actions":{"type":"array","items":{"type":"string","minLength":1},"minItems":1,"description":"Exact audit actions to receive, for example approval.created, job.posted, job.failed, job.held or draft.changes_requested. Only future events are sent."}},"required":["endpoint_url","actions"],"additionalProperties":false}}}}},"get":{"operationId":"listWebhooks","summary":"List webhook endpoints without their secrets (including removed endpoints)","tags":["Webhooks"],"description":"Who can call it: An API token with the \"webhooks\" scope, or an owner of this workspace signed in with a passkey.","security":[{"token":["webhooks"]},{"session":[]}],"x-auth":"scope","x-scope":"webhooks","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}}],"responses":{"200":{"description":"List webhook endpoints without their secrets (including removed endpoints)","content":{"application/json":{"schema":{"type":"object","properties":{"webhooks":{"type":"array","items":{"$ref":"#/components/schemas/WebhookEndpoint"}}},"required":["webhooks"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/workspaces/{workspace}/webhooks/{id}":{"delete":{"operationId":"deleteWebhook","summary":"Remove an endpoint and cancel its pending deliveries","tags":["Webhooks"],"description":"Who can call it: An API token with the \"webhooks\" scope, or an owner of this workspace signed in with a passkey.","security":[{"token":["webhooks"]},{"session":[]}],"x-auth":"scope","x-scope":"webhooks","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^whk_[a-z0-9]{16}$"}}],"responses":{"200":{"description":"Remove an endpoint and cancel its pending deliveries","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/workspaces/{workspace}/webhooks/{id}/deliveries":{"get":{"operationId":"listWebhookDeliveries","summary":"Delivery results and next retry for an endpoint, newest event first","tags":["Webhooks"],"description":"Who can call it: An API token with the \"webhooks\" scope, or an owner of this workspace signed in with a passkey.","security":[{"token":["webhooks"]},{"session":[]}],"x-auth":"scope","x-scope":"webhooks","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^whk_[a-z0-9]{16}$"}},{"name":"before","in":"query","required":false,"schema":{"type":"string","pattern":"^\\d+$","description":"Only deliveries for smaller event ids (paging)."},"description":"Only deliveries for smaller event ids (paging)."}],"responses":{"200":{"description":"Delivery results and next retry for an endpoint, newest event first","content":{"application/json":{"schema":{"type":"object","properties":{"deliveries":{"type":"array","items":{"$ref":"#/components/schemas/WebhookDelivery"}},"next_before":{"type":["string","null"]}},"required":["deliveries","next_before"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/workspaces/{workspace}/push-subscriptions":{"get":{"operationId":"listPushSubscriptions","summary":"Owner: list your notification devices and the public VAPID key","tags":["Notifications"],"description":"Only this signed-in owner's devices in this workspace. Push endpoints and encryption keys are never returned. A null public_key means Web Push is not configured. No API token can subscribe a person to notifications. Who can call it: An owner of this workspace, signed in with a passkey (session cookie). API tokens are always refused (owner_session_required), whatever their scopes.","security":[{"session":[]}],"x-auth":"owner","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}}],"responses":{"200":{"description":"Owner: list your notification devices and the public VAPID key","content":{"application/json":{"schema":{"type":"object","properties":{"public_key":{"type":["string","null"]},"subscriptions":{"type":"array","items":{"$ref":"#/components/schemas/PushDevice"}}},"required":["public_key","subscriptions"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"subscribePush","summary":"Owner: register this browser's permitted Web Push subscription","tags":["Notifications"],"description":"The browser first grants notification permission in Settings. Registration is idempotent for this owner, workspace and endpoint. Only future approval requests and failed/held posts queue count-only encrypted notifications; nothing approves, schedules or posts. A push service 404 or 410 removes the subscription. Who can call it: An owner of this workspace, signed in with a passkey (session cookie). API tokens are always refused (owner_session_required), whatever their scopes.","security":[{"session":[]}],"x-auth":"owner","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}}],"responses":{"201":{"description":"Owner: register this browser's permitted Web Push subscription","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PushDevice"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"label":{"type":"string","minLength":1},"subscription":{"type":"object","properties":{"endpoint":{"type":"string","format":"uri"},"expirationTime":{"type":["integer","null"]},"keys":{"type":"object","properties":{"p256dh":{"type":"string"},"auth":{"type":"string"}},"required":["p256dh","auth"],"additionalProperties":false}},"required":["endpoint","keys"],"additionalProperties":false}},"required":["label","subscription"],"additionalProperties":false}}}}}},"/api/v1/workspaces/{workspace}/push-subscriptions/{id}":{"delete":{"operationId":"unsubscribePush","summary":"Owner: remove one of your notification devices and its pending deliveries","tags":["Notifications"],"description":"Stops future sends and retries. A notification already on the wire may still arrive. Removing a device is workspace-scoped; it does not invalidate a browser subscription shared with another workspace. Who can call it: An owner of this workspace, signed in with a passkey (session cookie). API tokens are always refused (owner_session_required), whatever their scopes.","security":[{"session":[]}],"x-auth":"owner","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^psh_[a-z0-9]{16}$"}}],"responses":{"200":{"description":"Owner: remove one of your notification devices and its pending deliveries","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/workspaces/{workspace}/calendar-feed":{"get":{"operationId":"getCalendarFeed","summary":"Whether this workspace has a calendar subscription; never returns its private URL","tags":["Calendar feed"],"description":"Who can call it: An API token with the \"calendar\" scope, or an owner of this workspace signed in with a passkey.","security":[{"token":["calendar"]},{"session":[]}],"x-auth":"scope","x-scope":"calendar","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}}],"responses":{"200":{"description":"Whether this workspace has a calendar subscription; never returns its private URL","content":{"application/json":{"schema":{"type":"object","properties":{"enabled":{"type":"boolean"},"created_at":{"type":["string","null"],"format":"date-time"}},"required":["enabled","created_at"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"createCalendarFeed","summary":"Create or rotate the workspace's private calendar URL (shown once)","tags":["Calendar feed"],"description":"Returns a read-only RFC 5545 subscription URL for scheduled and posted jobs, including their approved account, first line and desk link. Every call immediately invalidates the previous URL: subscribe again after rotating. Anyone with the URL, including a subscribed calendar provider, can read it without signing in. Only its hash is stored. Revoking the token that made it disables its feed. No approval, scheduling or posting authority is granted. Who can call it: An API token with the \"calendar\" scope, or an owner of this workspace signed in with a passkey.","security":[{"token":["calendar"]},{"session":[]}],"x-auth":"scope","x-scope":"calendar","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}}],"responses":{"201":{"description":"Create or rotate the workspace's private calendar URL (shown once)","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","format":"uri","description":"Private bearer URL. Save it now; it cannot be retrieved later. Do not publish or log it."},"created_at":{"type":"string","format":"date-time"}},"required":["url","created_at"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"operationId":"deleteCalendarFeed","summary":"Disable the calendar feed; its URL immediately returns 404","tags":["Calendar feed"],"description":"Who can call it: An API token with the \"calendar\" scope, or an owner of this workspace signed in with a passkey.","security":[{"token":["calendar"]},{"session":[]}],"x-auth":"scope","x-scope":"calendar","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}}],"responses":{"200":{"description":"Disable the calendar feed; its URL immediately returns 404","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/workspaces/{workspace}/feeds":{"post":{"operationId":"createFeed","summary":"Add an RSS or Atom feed as a draft source (the first read only records existing items)","tags":["Feeds"],"description":"The cron reads it hourly. The first successful read records all current item identities and creates no drafts. Each later new identity makes one unassigned X draft and one LinkedIn draft, with the item's title and link and a LinkedIn card from its title/description. Nothing requests approval, schedules or posts. Re-adding the same URL returns the existing subscription without resetting its baseline. A broken feed retains its baseline and retries next hour. Who can call it: An API token with the \"drafts\" scope, or a member of this workspace signed in with a passkey. Refused (402 workspace_inactive) once the workspace's plan has lapsed.","security":[{"token":["drafts"]},{"session":[]}],"x-auth":"scope","x-scope":"drafts","x-plan":"not_lapsed","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","minLength":1,"maxLength":255,"pattern":"^[\\x20-\\x7e]+$","description":"Any unique string, like a UUID. Sending the same key and request again answers what the first request answered (Idempotent-Replayed: true) and makes nothing twice; the same key with another request is refused (idempotency_key_reused).","examples":["b3f1c7e2-6f0a-4d1e-9c4a-2f8e5d7a1b90"]},"description":"Any unique string, like a UUID. Sending the same key and request again answers what the first request answered (Idempotent-Replayed: true) and makes nothing twice; the same key with another request is refused (idempotency_key_reused)."}],"responses":{"201":{"description":"Add an RSS or Atom feed as a draft source (the first read only records existing items)","headers":{"Idempotent-Replayed":{"description":"true when this is the answer an earlier request with the same Idempotency-Key got; nothing new was made.","schema":{"type":"string","enum":["true"]}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Feed"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"feed_url":{"type":"string","format":"uri","description":"HTTP(S) RSS or Atom URL, for example a blog feed, changelog or https://github.com/owner/repo/releases.atom. No embedded credentials."}},"required":["feed_url"],"additionalProperties":false}}}}},"get":{"operationId":"listFeeds","summary":"List feed subscriptions and their last read/error","tags":["Feeds"],"description":"Who can call it: An API token with the \"read\" scope, or a member of this workspace signed in with a passkey.","security":[{"token":["read"]},{"session":[]}],"x-auth":"scope","x-scope":"read","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}}],"responses":{"200":{"description":"List feed subscriptions and their last read/error","content":{"application/json":{"schema":{"type":"object","properties":{"feeds":{"type":"array","items":{"$ref":"#/components/schemas/Feed"}}},"required":["feeds"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/workspaces/{workspace}/feeds/{id}":{"delete":{"operationId":"deleteFeed","summary":"Remove a feed; keep drafts it already made","tags":["Feeds"],"description":"Who can call it: An API token with the \"drafts\" scope, or a member of this workspace signed in with a passkey.","security":[{"token":["drafts"]},{"session":[]}],"x-auth":"scope","x-scope":"drafts","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^fed_[a-z0-9]{16}$"}}],"responses":{"200":{"description":"Remove a feed; keep drafts it already made","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/waitlist":{"post":{"operationId":"joinWaitlist","summary":"Join the public site's waitlist (no email is sent)","tags":["Sign-up"],"description":"Only submit the address of someone who asked to join. Repeats keep the original attribution. This cannot list subscribers or send invitations. Who can call it: No sign-in needed.","security":[],"x-auth":"public","responses":{"200":{"description":"Join the public site's waitlist (no email is sent)","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"status":{"type":"string","enum":["joined","already_joined"]}},"required":["ok","status"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string"},"utm_source":{"type":"string"},"utm_medium":{"type":"string"},"utm_campaign":{"type":"string"},"utm_content":{"type":"string"},"utm_term":{"type":"string"},"referrer":{"type":"string"},"landing_path":{"type":"string"}},"required":["email"],"additionalProperties":false}}}}}},"/api/v1/waitlist/unsubscribe":{"post":{"operationId":"unsubscribeWaitlist","summary":"Unsubscribe using the token from a waitlist email","tags":["Sign-up"],"description":"The token is the t query value in the email's unsubscribe link. Never put it in a draft or shared log. No email address lookup or operator access is available. Who can call it: No sign-in needed.","security":[],"x-auth":"public","responses":{"200":{"description":"Unsubscribe using the token from a waitlist email","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string","pattern":"^[A-Za-z0-9_-]{20,100}$"}},"required":["token"],"additionalProperties":false}}}}}},"/api/v1/status":{"get":{"operationId":"getStatus","summary":"The product's name, and the scheduler's heartbeat: when its once-a-minute run last ran","tags":["Meta"],"description":"Who can call it: No sign-in needed.","security":[],"x-auth":"public","responses":{"200":{"description":"The product's name, and the scheduler's heartbeat: when its once-a-minute run last ran","content":{"application/json":{"schema":{"type":"object","properties":{"now":{"type":"string","format":"date-time"},"last_tick":{"type":["string","null"],"format":"date-time"},"x_credits_exhausted":{"type":"boolean"},"product":{"type":"object","properties":{"name":{"type":"string"},"origin":{"type":"string"}},"required":["name","origin"],"additionalProperties":false}},"required":["now","last_tick","product"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/workspaces/{workspace}/connections/{id}/profile":{"patch":{"operationId":"updateConnectionProfile","summary":"Change how an account looks in previews (never what's posted)","tags":["Connections"],"description":"Who can call it: An API token with the \"drafts\" scope, or a member of this workspace signed in with a passkey. Refused (402 workspace_inactive) once the workspace's plan has lapsed.","security":[{"token":["drafts"]},{"session":[]}],"x-auth":"scope","x-scope":"drafts","x-plan":"not_lapsed","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Change how an account looks in previews (never what's posted)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Profile"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":["string","null"],"maxLength":120},"handle":{"type":["string","null"],"maxLength":60},"kind":{"type":["string","null"],"enum":["person","company","school","showcase"]},"verified":{"type":["string","null"],"enum":["blue","gold","gray"]},"premium":{"type":"boolean"},"headline":{"type":["string","null"],"maxLength":300},"cta":{"type":["string","null"],"maxLength":60},"avatar_media_id":{"type":["string","null"],"pattern":"^med_[a-z0-9]{16}$"},"affiliate_media_id":{"type":["string","null"],"pattern":"^med_[a-z0-9]{16}$"}},"required":[],"additionalProperties":false}}}}}},"/api/v1/workspaces/{workspace}/connections/{id}/profile/refresh":{"post":{"operationId":"refreshConnectionProfile","summary":"Fill an account's name, handle, avatar and badge from the platform (one read)","tags":["Connections"],"description":"Who can call it: An API token with the \"read\" scope, or a member of this workspace signed in with a passkey. Refused (402 workspace_inactive) once the workspace's plan has lapsed.","security":[{"token":["read"]},{"session":[]}],"x-auth":"scope","x-scope":"read","x-plan":"not_lapsed","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Fill an account's name, handle, avatar and badge from the platform (one read)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Profile"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/workspaces/{workspace}/lookups":{"get":{"operationId":"listLookups","summary":"Every mention check and quoted post in the workspace's cache, by key","tags":["Mentions"],"description":"Who can call it: An API token with the \"read\" scope, or a member of this workspace signed in with a passkey.","security":[{"token":["read"]},{"session":[]}],"x-auth":"scope","x-scope":"read","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}}],"responses":{"200":{"description":"Every mention check and quoted post in the workspace's cache, by key","content":{"application/json":{"schema":{"type":"object","properties":{"lookups":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/Lookup"}}},"required":["lookups"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/workspaces/{workspace}/lookups/x-profile":{"post":{"operationId":"checkXMention","summary":"Check an X @handle against the live account and cache it","tags":["Mentions"],"description":"Who can call it: An API token with the \"read\" scope, or a member of this workspace signed in with a passkey. Refused (402 workspace_inactive) once the workspace's plan has lapsed.","security":[{"token":["read"]},{"session":[]}],"x-auth":"scope","x-scope":"read","x-plan":"not_lapsed","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}}],"responses":{"200":{"description":"Check an X @handle against the live account and cache it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Lookup"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"handle":{"type":"string","pattern":"^[A-Za-z0-9_]{1,15}$"}},"required":["handle"],"additionalProperties":false}}}}}},"/api/v1/workspaces/{workspace}/lookups/x-post":{"post":{"operationId":"lookUpXPost","summary":"Read a live X post a draft quotes or replies to, for its preview, and cache it","tags":["Mentions"],"description":"Who can call it: An API token with the \"read\" scope, or a member of this workspace signed in with a passkey. Refused (402 workspace_inactive) once the workspace's plan has lapsed.","security":[{"token":["read"]},{"session":[]}],"x-auth":"scope","x-scope":"read","x-plan":"not_lapsed","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}}],"responses":{"200":{"description":"Read a live X post a draft quotes or replies to, for its preview, and cache it","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Lookup"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","pattern":"^https://(x|twitter)\\.com/[A-Za-z0-9_]{1,15}/status/\\d+"}},"required":["url"],"additionalProperties":false}}}}}},"/api/v1/workspaces/{workspace}/lookups/x-community":{"post":{"operationId":"lookUpXCommunity","summary":"Look up an X Community name once and cache it ($0.005 per resource)","tags":["Mentions"],"description":"Who can call it: An API token with the \"read\" scope, or a member of this workspace signed in with a passkey. Refused (402 workspace_inactive) once the workspace's plan has lapsed.","security":[{"token":["read"]},{"session":[]}],"x-auth":"scope","x-scope":"read","x-plan":"not_lapsed","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}}],"responses":{"200":{"description":"Look up an X Community name once and cache it ($0.005 per resource)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Lookup"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"community":{"type":"string","pattern":"^(?:[0-9]{1,19}|https://(?:x|twitter)\\.com/i/communities/[0-9]{1,19}/?(?:[?#][^\\s]*)?)$"},"connection_id":{"type":"string","description":"The workspace's X connection for the lookup; defaults to its most recent X connection."}},"required":["community"],"additionalProperties":false}}}}}},"/api/v1/workspaces/{workspace}/lookups/linkedin-tag":{"post":{"operationId":"linkLinkedInTag","summary":"Link a LinkedIn @[Tag] to its page by hand (nothing is read from LinkedIn)","tags":["Mentions"],"description":"Who can call it: An API token with the \"drafts\" scope, or a member of this workspace signed in with a passkey. Refused (402 workspace_inactive) once the workspace's plan has lapsed.","security":[{"token":["drafts"]},{"session":[]}],"x-auth":"scope","x-scope":"drafts","x-plan":"not_lapsed","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}}],"responses":{"200":{"description":"Link a LinkedIn @[Tag] to its page by hand (nothing is read from LinkedIn)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Lookup"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"tag":{"type":"string","minLength":1,"maxLength":80},"url":{"type":"string","description":"The page's link: linkedin.com/company/…, /in/…, /school/… or /showcase/…."},"urn":{"type":"string","description":"For a company page: its id, as a number or urn:li:organization:<number>."},"name":{"type":"string","maxLength":80,"description":"The page's exact LinkedIn name, if the tag says something else."}},"required":["tag","url"],"additionalProperties":false}}}}}},"/api/v1/workspaces/{workspace}/cache/{file}":{"get":{"operationId":"getCachedImage","summary":"An avatar, badge or quoted photo from the workspace's image cache","tags":["Mentions"],"description":"Who can call it: An API token with the \"read\" scope, or a member of this workspace signed in with a passkey.","security":[{"token":["read"]},{"session":[]}],"x-auth":"scope","x-scope":"read","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"file","in":"path","required":true,"schema":{"type":"string","pattern":"^[a-f0-9]{32}\\.(jpg|png|webp|gif)$"}}],"responses":{"200":{"description":"An avatar, badge or quoted photo from the workspace's image cache","content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/workspaces/{workspace}/drafts/{id}/posted-by-hand":{"post":{"operationId":"recordPostedByHand","summary":"Record the live link of an approved draft its owner posted themselves; the draft locks as posted","tags":["Jobs"],"description":"Who can call it: An API token with the \"drafts\" scope, or a member of this workspace signed in with a passkey. Refused (402 workspace_inactive) once the workspace's plan has lapsed.","security":[{"token":["drafts"]},{"session":[]}],"x-auth":"scope","x-scope":"drafts","x-plan":"not_lapsed","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^drf_[a-z0-9]{16}$"}}],"responses":{"201":{"description":"Record the live link of an approved draft its owner posted themselves; the draft locks as posted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Job"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string","maxLength":300}},"required":["url"],"additionalProperties":false}}}}}},"/api/v1/workspaces/{workspace}/best-times":{"get":{"operationId":"getBestTimes","summary":"The next best times to post, learned from the account's X history (LinkedIn: published research)","tags":["Best times"],"x-read-only":false,"description":"For an X account with stale learning data, refreshes credentials and insights using billed X reads before suggesting times. This GET can write caches and learning state. Who can call it: An API token with the \"read\" scope, or a member of this workspace signed in with a passkey.","security":[{"token":["read"]},{"session":[]}],"x-auth":"scope","x-scope":"read","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"platform","in":"query","required":false,"schema":{"type":"string","enum":["x","linkedin"]}},{"name":"metric","in":"query","required":false,"schema":{"type":"string","enum":["engagements","views"]}},{"name":"draft_id","in":"query","required":false,"schema":{"type":"string","pattern":"^drf_[a-z0-9]{16}$"}}],"responses":{"200":{"description":"The next best times to post, learned from the account's X history (LinkedIn: published research)","content":{"application/json":{"schema":{"type":"object","properties":{"platform":{"type":"string"},"metric":{"type":"string"},"tz":{"type":"string","description":"The workspace's IANA time zone. Suggestions use its local waking hours."},"slots":{"type":"array","items":{"type":"object","properties":{"at":{"type":"string","format":"date-time"},"label":{"type":"string","enum":["best","good","test","soon"]},"lift":{"type":"number"},"why":{"type":"string"}},"required":["at","label"],"additionalProperties":false}},"soon":{"type":["object","null"],"properties":{"at":{"type":"string","format":"date-time"},"label":{"type":"string","enum":["best","good","test","soon"]},"lift":{"type":"number"},"why":{"type":"string"}},"required":["at","label"],"additionalProperties":false},"flat":{"type":"boolean"},"personal":{"type":["object","null"]},"near":{"type":"array","items":{"type":"string","format":"date-time"}},"spacing":{"type":["object","null"]}},"required":["slots"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/workspaces/{workspace}/best-times/heatmap":{"get":{"operationId":"getBestTimesHeatmap","summary":"Every hour of the week with its lift, what the model rests on, and the posts behind it","tags":["Best times"],"description":"Who can call it: An API token with the \"read\" scope, or a member of this workspace signed in with a passkey.","security":[{"token":["read"]},{"session":[]}],"x-auth":"scope","x-scope":"read","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"platform","in":"query","required":false,"schema":{"type":"string","enum":["x","linkedin"]}},{"name":"metric","in":"query","required":false,"schema":{"type":"string","enum":["engagements","views"]}}],"responses":{"200":{"description":"Every hour of the week with its lift, what the model rests on, and the posts behind it","content":{"application/json":{"schema":{"type":"object","description":"cells (168, Sunday midnight first, in the workspace's IANA time zone given by tz), layers, content effects, backtest, audience activity, follower growth, posts, data and the next picks."}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/workspaces/{workspace}/best-times/refresh":{"post":{"operationId":"refreshBestTimes","summary":"Read new posts and fresh numbers from X now; the first refresh backfills the history (about $2.50 of X reads)","tags":["Best times"],"description":"Who can call it: An owner of this workspace, signed in with a passkey (session cookie). API tokens are always refused (owner_session_required), whatever their scopes. Refused (402 workspace_inactive) once the workspace's plan has lapsed.","security":[{"session":[]}],"x-auth":"owner","x-plan":"not_lapsed","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}}],"responses":{"200":{"description":"Read new posts and fresh numbers from X now; the first refresh backfills the history (about $2.50 of X reads)","content":{"application/json":{"schema":{"type":"object","properties":{"posts":{"type":"integer"},"fetched_at":{"type":"string","format":"date-time"}},"required":["posts","fetched_at"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"full":{"type":"boolean","description":"Re-read the whole history (up to 800 posts, about $2.50 at X's prices)."}},"required":[],"additionalProperties":false}}}}}},"/api/v1/slideshows/styles":{"get":{"operationId":"listSlideshowStyles","summary":"The shapes, fonts and text styles slides can use","tags":["Slideshows"],"description":"Who can call it: No sign-in needed.","security":[],"x-auth":"public","responses":{"200":{"description":"The shapes, fonts and text styles slides can use","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SlideshowStyles"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/workspaces/{workspace}/slideshows":{"get":{"operationId":"listSlideshows","summary":"List slideshow drafts, newest change first","tags":["Slideshows"],"description":"Who can call it: An API token with the \"read\" scope, or a member of this workspace signed in with a passkey.","security":[{"token":["read"]},{"session":[]}],"x-auth":"scope","x-scope":"read","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"label","in":"query","required":false,"schema":{"type":"string","description":"Only slideshow drafts with this exact, case-sensitive planning label."},"description":"Only slideshow drafts with this exact, case-sensitive planning label."},{"name":"archived","in":"query","required":false,"schema":{"type":"string","enum":["true","false"]}},{"name":"limit","in":"query","required":false,"schema":{"type":"string","pattern":"^\\d{1,3}$"}}],"responses":{"200":{"description":"List slideshow drafts, newest change first","content":{"application/json":{"schema":{"type":"object","properties":{"slideshows":{"type":"array","items":{"$ref":"#/components/schemas/SlideshowDraft"}}},"required":["slideshows"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"operationId":"createSlideshow","summary":"Create a TikTok slideshow draft (empty, or from a whole document)","tags":["Slideshows"],"description":"Who can call it: An API token with the \"drafts\" scope, or a member of this workspace signed in with a passkey. Refused (402 workspace_inactive) once the workspace's plan has lapsed.","security":[{"token":["drafts"]},{"session":[]}],"x-auth":"scope","x-scope":"drafts","x-plan":"not_lapsed","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","minLength":1,"maxLength":255,"pattern":"^[\\x20-\\x7e]+$","description":"Any unique string, like a UUID. Sending the same key and request again answers what the first request answered (Idempotent-Replayed: true) and makes nothing twice; the same key with another request is refused (idempotency_key_reused).","examples":["b3f1c7e2-6f0a-4d1e-9c4a-2f8e5d7a1b90"]},"description":"Any unique string, like a UUID. Sending the same key and request again answers what the first request answered (Idempotent-Replayed: true) and makes nothing twice; the same key with another request is refused (idempotency_key_reused)."}],"responses":{"201":{"description":"Create a TikTok slideshow draft (empty, or from a whole document)","headers":{"Idempotent-Replayed":{"description":"true when this is the answer an earlier request with the same Idempotency-Key got; nothing new was made.","schema":{"type":"string","enum":["true"]}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SlideshowDraft"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"connection_id":{"type":["string","null"],"description":"The TikTok connection it posts as."},"title":{"type":"string","maxLength":2200,"description":"The title TikTok shows with the photos. TikTok allows 90 characters; check says so."},"description":{"type":"string","maxLength":4000,"description":"The caption under it, up to 4,000 characters."},"cover_index":{"type":"integer","minimum":0,"maximum":34,"description":"Which photo (or slide) is the cover (0 is the first). Default 0."},"auto_add_music":{"type":"boolean","description":"Posting directly: let TikTok add recommended music. Default true."},"post_mode":{"type":"string","enum":["direct","inbox"],"description":"direct: publish to the account (the owner picks who can view it when approving). inbox: send it to the account's TikTok inbox as a draft, to finish and publish in TikTok. Default direct."},"slideshow":{"type":"object","properties":{"version":{"type":"integer","enum":[1],"description":"The document's schema version: 1."},"aspect":{"type":"string","enum":["9:16","4:5","1:1","16:9"],"description":"Every slide's shape. 9:16 (default) is 1080x1920; 4:5 1080x1350; 1:1 1080x1080; 16:9 1080x608."},"background":{"type":"string","pattern":"^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","description":"The color under every slide's photo (and of slides without one). Default #000000."},"overlay":{"type":["object","null"],"properties":{"color":{"type":"string","pattern":"^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","description":"A color: #rgb, #rrggbb or #rrggbbaa (aa is opacity)."},"opacity":{"type":"number","minimum":0,"maximum":1,"description":"0 to 1."}},"required":["color","opacity"],"additionalProperties":false,"description":"A tint over every slide's photo, unless a slide sets its own. Default none."},"slides":{"type":"array","items":{"$ref":"#/components/schemas/Slide"},"maxItems":35,"description":"Up to 35 slides, in order. Each becomes one photo of the TikTok post; rendering needs at least one."}},"required":["slides"],"additionalProperties":false,"description":"The whole document. Left out, the slideshow starts with no slides: add them with POST …/slides.","x-schema-version":1},"meta":{"$ref":"#/components/schemas/DraftMeta"},"labels":{"$ref":"#/components/schemas/DraftLabels"}},"required":[],"additionalProperties":false}}}}}},"/api/v1/workspaces/{workspace}/slideshows/{id}":{"get":{"operationId":"getSlideshow","summary":"One slideshow: the document, the draft and its latest render","tags":["Slideshows"],"description":"Who can call it: An API token with the \"read\" scope, or a member of this workspace signed in with a passkey.","security":[{"token":["read"]},{"session":[]}],"x-auth":"scope","x-scope":"read","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^drf_[a-z0-9]{16}$"}}],"responses":{"200":{"description":"One slideshow: the document, the draft and its latest render","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SlideshowDraft"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"operationId":"updateSlideshow","summary":"Change the post's text and settings, the slideshow's shape and colors, or replace the whole document","tags":["Slideshows"],"description":"Who can call it: An API token with the \"drafts\" scope, or a member of this workspace signed in with a passkey. Refused (402 workspace_inactive) once the workspace's plan has lapsed.","security":[{"token":["drafts"]},{"session":[]}],"x-auth":"scope","x-scope":"drafts","x-plan":"not_lapsed","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^drf_[a-z0-9]{16}$"}}],"responses":{"200":{"description":"Change the post's text and settings, the slideshow's shape and colors, or replace the whole document","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SlideshowDraft"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"rev":{"type":"integer","description":"The rev you read: the change is refused (rev_conflict) if the draft changed since. Left out, it applies to the draft as it is now."},"title":{"type":"string","maxLength":2200,"description":"The title TikTok shows with the photos. TikTok allows 90 characters; check says so."},"description":{"type":"string","maxLength":4000,"description":"The caption under it, up to 4,000 characters."},"cover_index":{"type":"integer","minimum":0,"maximum":34,"description":"Which photo (or slide) is the cover (0 is the first). Default 0."},"auto_add_music":{"type":"boolean","description":"Posting directly: let TikTok add recommended music. Default true."},"post_mode":{"type":"string","enum":["direct","inbox"],"description":"direct: publish to the account (the owner picks who can view it when approving). inbox: send it to the account's TikTok inbox as a draft, to finish and publish in TikTok. Default direct."},"labels":{"$ref":"#/components/schemas/DraftLabels"},"aspect":{"type":"string","enum":["9:16","4:5","1:1","16:9"],"description":"Every slide's shape. 9:16 (default) is 1080x1920; 4:5 1080x1350; 1:1 1080x1080; 16:9 1080x608."},"background":{"type":"string","pattern":"^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","description":"The color under every slide's photo (and of slides without one). Default #000000."},"overlay":{"type":["object","null"],"properties":{"color":{"type":"string","pattern":"^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","description":"A color: #rgb, #rrggbb or #rrggbbaa (aa is opacity)."},"opacity":{"type":"number","minimum":0,"maximum":1,"description":"0 to 1."}},"required":["color","opacity"],"additionalProperties":false,"description":"A tint over every slide's photo, unless a slide sets its own. Default none."},"slideshow":{"type":"object","properties":{"version":{"type":"integer","enum":[1],"description":"The document's schema version: 1."},"aspect":{"type":"string","enum":["9:16","4:5","1:1","16:9"],"description":"Every slide's shape. 9:16 (default) is 1080x1920; 4:5 1080x1350; 1:1 1080x1080; 16:9 1080x608."},"background":{"type":"string","pattern":"^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","description":"The color under every slide's photo (and of slides without one). Default #000000."},"overlay":{"type":["object","null"],"properties":{"color":{"type":"string","pattern":"^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","description":"A color: #rgb, #rrggbb or #rrggbbaa (aa is opacity)."},"opacity":{"type":"number","minimum":0,"maximum":1,"description":"0 to 1."}},"required":["color","opacity"],"additionalProperties":false,"description":"A tint over every slide's photo, unless a slide sets its own. Default none."},"slides":{"type":"array","items":{"$ref":"#/components/schemas/Slide"},"maxItems":35,"description":"Up to 35 slides, in order. Each becomes one photo of the TikTok post; rendering needs at least one."}},"required":["slides"],"additionalProperties":false,"description":"A whole new document, replacing every slide.","x-schema-version":1}},"required":[],"additionalProperties":false}}}}}},"/api/v1/workspaces/{workspace}/slideshows/{id}/slides":{"post":{"operationId":"addSlide","summary":"Add a slide: a photo from the media library, a background, a tint and texts","tags":["Slideshows"],"description":"Who can call it: An API token with the \"drafts\" scope, or a member of this workspace signed in with a passkey. Refused (402 workspace_inactive) once the workspace's plan has lapsed.","security":[{"token":["drafts"]},{"session":[]}],"x-auth":"scope","x-scope":"drafts","x-plan":"not_lapsed","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^drf_[a-z0-9]{16}$"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","minLength":1,"maxLength":255,"pattern":"^[\\x20-\\x7e]+$","description":"Any unique string, like a UUID. Sending the same key and request again answers what the first request answered (Idempotent-Replayed: true) and makes nothing twice; the same key with another request is refused (idempotency_key_reused).","examples":["b3f1c7e2-6f0a-4d1e-9c4a-2f8e5d7a1b90"]},"description":"Any unique string, like a UUID. Sending the same key and request again answers what the first request answered (Idempotent-Replayed: true) and makes nothing twice; the same key with another request is refused (idempotency_key_reused)."}],"responses":{"201":{"description":"Add a slide: a photo from the media library, a background, a tint and texts","headers":{"Idempotent-Replayed":{"description":"true when this is the answer an earlier request with the same Idempotency-Key got; nothing new was made.","schema":{"type":"string","enum":["true"]}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SlideshowDraft"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"rev":{"type":"integer","description":"The rev you read: the change is refused (rev_conflict) if the draft changed since. Left out, it applies to the draft as it is now."},"at":{"type":"integer","minimum":0,"maximum":35,"description":"Where the new slide goes (0 is first). Default last."},"media_id":{"type":"string","pattern":"^med_[a-z0-9]{16}$","description":"A photo from the media library (JPEG, PNG or WebP), cropped to fill the slide. image sets how."},"image":{"type":["object","null"],"properties":{"media_id":{"type":"string","pattern":"^med_[a-z0-9]{16}$","description":"A photo (JPEG, PNG or WebP) from the workspace's media library."},"fit":{"type":"string","enum":["cover","contain"],"description":"cover (default): fill the slide, cropping what doesn't fit. contain: show the whole photo on the background color."},"zoom":{"type":"number","minimum":1,"maximum":10,"description":"1 (default) is the fit's size; 2 shows half as much, twice as large."},"focus":{"type":"object","properties":{"x":{"type":"number","minimum":0,"maximum":1},"y":{"type":"number","minimum":0,"maximum":1}},"required":["x","y"],"additionalProperties":false,"description":"Which part of the photo stays in view when it's cropped: 0,0 the top left, 1,1 the bottom right, 0.5,0.5 (default) the center."}},"required":["media_id"],"additionalProperties":false},"background":{"type":["string","null"],"pattern":"^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","description":"This slide's background color, under its photo. null: the slideshow's."},"overlay":{"type":["object","null"],"properties":{"color":{"type":"string","pattern":"^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","description":"A color: #rgb, #rrggbb or #rrggbbaa (aa is opacity)."},"opacity":{"type":"number","minimum":0,"maximum":1,"description":"0 to 1."}},"required":["color","opacity"],"additionalProperties":false,"description":"This slide's tint. Left out: the slideshow's. null: none."},"texts":{"type":"array","items":{"type":"object","properties":{"text":{"type":"string","maxLength":2000,"description":"The words, with \\n for a new line. Emoji are drawn in color."},"preset":{"type":"string","enum":["outline","white","black","yellow","white_box","white_box_50","black_box","black_box_50","tiktok_white","tiktok_black","whiteText","blackText","yellowText","white_background","white_50_background","black_background","black_50_background"],"description":"Fills in the style fields you leave out (color, stroke, shadow, box). Default outline, ss-content's look. Not kept: the document keeps the result."},"x":{"type":"number","minimum":-10000,"maximum":10000,"description":"Left edge of the text's box, in pixels from the image's left. Default 90."},"y":{"type":"number","minimum":-10000,"maximum":10000,"description":"Top of the text's box. Default 0 with height the whole image, so the text sits in the middle."},"width":{"type":"number","minimum":1,"maximum":10000,"description":"The box's width: lines wrap to it. Default the image's width less 180."},"height":{"type":["number","null"],"minimum":1,"maximum":10000,"description":"The box's height, for valign. null: as tall as the text."},"align":{"type":"string","enum":["left","center","right"],"description":"Default center."},"valign":{"type":"string","enum":["top","middle","bottom"],"description":"Where the text sits in the box when height is set. Default middle."},"font":{"type":"string","enum":["tiktok-sans","geist","bebas-neue","cormorant-garamond"],"description":"Default tiktok-sans. GET /api/v1/slideshows/styles lists each family's weights."},"weight":{"type":"integer","minimum":100,"maximum":900,"description":"The nearest weight the font has is used. Default the font's own."},"italic":{"type":"boolean","description":"Only fonts with an italic have one."},"size":{"type":"number","minimum":8,"maximum":400,"description":"Font size in pixels. Default 64."},"line_height":{"type":"number","minimum":0.7,"maximum":3,"description":"Line spacing as a multiple of the size. Default 1.2."},"letter_spacing":{"type":"number","minimum":-50,"maximum":200,"description":"Extra pixels between characters. Default 0."},"color":{"type":"string","pattern":"^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","description":"The text's color. Default #ffffff."},"stroke":{"type":["object","null"],"properties":{"color":{"type":"string","pattern":"^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","description":"A color: #rgb, #rrggbb or #rrggbbaa (aa is opacity)."},"width":{"type":"number","minimum":0,"maximum":60,"description":"Outline thickness in pixels, outside the letters."}},"required":["color","width"],"additionalProperties":false,"description":"An outline around the letters (not emoji)."},"shadow":{"type":["object","null"],"properties":{"color":{"type":"string","pattern":"^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","description":"A color: #rgb, #rrggbb or #rrggbbaa (aa is opacity)."},"x":{"type":"number","minimum":-200,"maximum":200},"y":{"type":"number","minimum":-200,"maximum":200},"blur":{"type":"number","minimum":0,"maximum":200,"description":"Blur radius in pixels, as in CSS."}},"required":["color","x","y","blur"],"additionalProperties":false,"description":"A drop shadow under the text and its box."},"box":{"type":["object","null"],"properties":{"color":{"type":"string","pattern":"^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","description":"A color: #rgb, #rrggbb or #rrggbbaa (aa is opacity)."},"padding_x":{"type":"number","minimum":0,"maximum":200},"padding_y":{"type":"number","minimum":0,"maximum":200},"radius":{"type":"number","minimum":0,"maximum":200},"mode":{"type":"string","enum":["block","lines"],"description":"block: one box around all the lines. lines: a box hugging each line (TikTok's own text background)."}},"required":["color"],"additionalProperties":false,"description":"A background box behind the text."}},"required":["text"],"additionalProperties":false,"description":"A text on the slide (POST …/slides/{slide_id}/texts adds more)."},"maxItems":20}},"required":[],"additionalProperties":false}}}}}},"/api/v1/workspaces/{workspace}/slideshows/{id}/slides/{slide_id}":{"patch":{"operationId":"updateSlide","summary":"Change a slide's photo, crop, background, tint or texts","tags":["Slideshows"],"description":"Who can call it: An API token with the \"drafts\" scope, or a member of this workspace signed in with a passkey. Refused (402 workspace_inactive) once the workspace's plan has lapsed.","security":[{"token":["drafts"]},{"session":[]}],"x-auth":"scope","x-scope":"drafts","x-plan":"not_lapsed","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^drf_[a-z0-9]{16}$"}},{"name":"slide_id","in":"path","required":true,"schema":{"type":"string","pattern":"^sld_[a-z0-9]{8,16}$"}}],"responses":{"200":{"description":"Change a slide's photo, crop, background, tint or texts","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SlideshowDraft"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"rev":{"type":"integer","description":"The rev you read: the change is refused (rev_conflict) if the draft changed since. Left out, it applies to the draft as it is now."},"media_id":{"type":"string","pattern":"^med_[a-z0-9]{16}$","description":"Another photo from the media library, keeping the crop settings."},"image":{"type":["object","null"],"properties":{"media_id":{"type":"string","pattern":"^med_[a-z0-9]{16}$","description":"A photo (JPEG, PNG or WebP) from the workspace's media library."},"fit":{"type":"string","enum":["cover","contain"],"description":"cover (default): fill the slide, cropping what doesn't fit. contain: show the whole photo on the background color."},"zoom":{"type":"number","minimum":1,"maximum":10,"description":"1 (default) is the fit's size; 2 shows half as much, twice as large."},"focus":{"type":"object","properties":{"x":{"type":"number","minimum":0,"maximum":1},"y":{"type":"number","minimum":0,"maximum":1}},"required":["x","y"],"additionalProperties":false,"description":"Which part of the photo stays in view when it's cropped: 0,0 the top left, 1,1 the bottom right, 0.5,0.5 (default) the center."}},"required":[],"additionalProperties":false,"description":"The photo and its crop. Fields you send replace those fields; null removes the photo."},"background":{"type":["string","null"],"pattern":"^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","description":"This slide's background color, under its photo. null: the slideshow's."},"overlay":{"type":["object","null"],"properties":{"color":{"type":"string","pattern":"^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","description":"A color: #rgb, #rrggbb or #rrggbbaa (aa is opacity)."},"opacity":{"type":"number","minimum":0,"maximum":1,"description":"0 to 1."}},"required":["color","opacity"],"additionalProperties":false,"description":"This slide's tint. null: none."},"texts":{"type":"array","items":{"$ref":"#/components/schemas/SlideText"},"maxItems":20,"description":"Every text on the slide, replacing them all. A text with the id of one there keeps what it doesn't set."}},"required":[],"additionalProperties":false}}}}},"delete":{"operationId":"deleteSlide","summary":"Remove a slide","tags":["Slideshows"],"description":"Who can call it: An API token with the \"drafts\" scope, or a member of this workspace signed in with a passkey. Refused (402 workspace_inactive) once the workspace's plan has lapsed.","security":[{"token":["drafts"]},{"session":[]}],"x-auth":"scope","x-scope":"drafts","x-plan":"not_lapsed","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^drf_[a-z0-9]{16}$"}},{"name":"slide_id","in":"path","required":true,"schema":{"type":"string","pattern":"^sld_[a-z0-9]{8,16}$"}}],"responses":{"200":{"description":"Remove a slide","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SlideshowDraft"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"rev":{"type":"integer","description":"The rev you read: the change is refused (rev_conflict) if the draft changed since. Left out, it applies to the draft as it is now."}},"required":[],"additionalProperties":false}}}}}},"/api/v1/workspaces/{workspace}/slideshows/{id}/reorder":{"post":{"operationId":"reorderSlides","summary":"Put the slides in a new order (the cover stays on its slide)","tags":["Slideshows"],"description":"Who can call it: An API token with the \"drafts\" scope, or a member of this workspace signed in with a passkey. Refused (402 workspace_inactive) once the workspace's plan has lapsed.","security":[{"token":["drafts"]},{"session":[]}],"x-auth":"scope","x-scope":"drafts","x-plan":"not_lapsed","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^drf_[a-z0-9]{16}$"}}],"responses":{"200":{"description":"Put the slides in a new order (the cover stays on its slide)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SlideshowDraft"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"rev":{"type":"integer","description":"The rev you read: the change is refused (rev_conflict) if the draft changed since. Left out, it applies to the draft as it is now."},"slide_ids":{"type":"array","items":{"type":"string","pattern":"^sld_[a-z0-9]{8,16}$"},"minItems":1,"maxItems":35,"description":"Every slide's id, once each, in the new order."}},"required":["slide_ids"],"additionalProperties":false}}}}}},"/api/v1/workspaces/{workspace}/slideshows/{id}/slides/{slide_id}/texts":{"post":{"operationId":"addText","summary":"Add a text to a slide; a preset fills in the style","tags":["Slideshows"],"description":"Who can call it: An API token with the \"drafts\" scope, or a member of this workspace signed in with a passkey. Refused (402 workspace_inactive) once the workspace's plan has lapsed.","security":[{"token":["drafts"]},{"session":[]}],"x-auth":"scope","x-scope":"drafts","x-plan":"not_lapsed","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^drf_[a-z0-9]{16}$"}},{"name":"slide_id","in":"path","required":true,"schema":{"type":"string","pattern":"^sld_[a-z0-9]{8,16}$"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","minLength":1,"maxLength":255,"pattern":"^[\\x20-\\x7e]+$","description":"Any unique string, like a UUID. Sending the same key and request again answers what the first request answered (Idempotent-Replayed: true) and makes nothing twice; the same key with another request is refused (idempotency_key_reused).","examples":["b3f1c7e2-6f0a-4d1e-9c4a-2f8e5d7a1b90"]},"description":"Any unique string, like a UUID. Sending the same key and request again answers what the first request answered (Idempotent-Replayed: true) and makes nothing twice; the same key with another request is refused (idempotency_key_reused)."}],"responses":{"201":{"description":"Add a text to a slide; a preset fills in the style","headers":{"Idempotent-Replayed":{"description":"true when this is the answer an earlier request with the same Idempotency-Key got; nothing new was made.","schema":{"type":"string","enum":["true"]}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SlideshowDraft"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"rev":{"type":"integer","description":"The rev you read: the change is refused (rev_conflict) if the draft changed since. Left out, it applies to the draft as it is now."},"at":{"type":"integer","minimum":0,"maximum":20,"description":"Where the new text goes (0 is first). Default last."},"text":{"type":"string","maxLength":2000,"description":"The words, with \\n for a new line. Emoji are drawn in color."},"preset":{"type":"string","enum":["outline","white","black","yellow","white_box","white_box_50","black_box","black_box_50","tiktok_white","tiktok_black","whiteText","blackText","yellowText","white_background","white_50_background","black_background","black_50_background"],"description":"Fills in the style fields you leave out (color, stroke, shadow, box). Default outline, ss-content's look. Not kept: the document keeps the result."},"x":{"type":"number","minimum":-10000,"maximum":10000,"description":"Left edge of the text's box, in pixels from the image's left. Default 90."},"y":{"type":"number","minimum":-10000,"maximum":10000,"description":"Top of the text's box. Default 0 with height the whole image, so the text sits in the middle."},"width":{"type":"number","minimum":1,"maximum":10000,"description":"The box's width: lines wrap to it. Default the image's width less 180."},"height":{"type":["number","null"],"minimum":1,"maximum":10000,"description":"The box's height, for valign. null: as tall as the text."},"align":{"type":"string","enum":["left","center","right"],"description":"Default center."},"valign":{"type":"string","enum":["top","middle","bottom"],"description":"Where the text sits in the box when height is set. Default middle."},"font":{"type":"string","enum":["tiktok-sans","geist","bebas-neue","cormorant-garamond"],"description":"Default tiktok-sans. GET /api/v1/slideshows/styles lists each family's weights."},"weight":{"type":"integer","minimum":100,"maximum":900,"description":"The nearest weight the font has is used. Default the font's own."},"italic":{"type":"boolean","description":"Only fonts with an italic have one."},"size":{"type":"number","minimum":8,"maximum":400,"description":"Font size in pixels. Default 64."},"line_height":{"type":"number","minimum":0.7,"maximum":3,"description":"Line spacing as a multiple of the size. Default 1.2."},"letter_spacing":{"type":"number","minimum":-50,"maximum":200,"description":"Extra pixels between characters. Default 0."},"color":{"type":"string","pattern":"^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","description":"The text's color. Default #ffffff."},"stroke":{"type":["object","null"],"properties":{"color":{"type":"string","pattern":"^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","description":"A color: #rgb, #rrggbb or #rrggbbaa (aa is opacity)."},"width":{"type":"number","minimum":0,"maximum":60,"description":"Outline thickness in pixels, outside the letters."}},"required":["color","width"],"additionalProperties":false,"description":"An outline around the letters (not emoji)."},"shadow":{"type":["object","null"],"properties":{"color":{"type":"string","pattern":"^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","description":"A color: #rgb, #rrggbb or #rrggbbaa (aa is opacity)."},"x":{"type":"number","minimum":-200,"maximum":200},"y":{"type":"number","minimum":-200,"maximum":200},"blur":{"type":"number","minimum":0,"maximum":200,"description":"Blur radius in pixels, as in CSS."}},"required":["color","x","y","blur"],"additionalProperties":false,"description":"A drop shadow under the text and its box."},"box":{"type":["object","null"],"properties":{"color":{"type":"string","pattern":"^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","description":"A color: #rgb, #rrggbb or #rrggbbaa (aa is opacity)."},"padding_x":{"type":"number","minimum":0,"maximum":200},"padding_y":{"type":"number","minimum":0,"maximum":200},"radius":{"type":"number","minimum":0,"maximum":200},"mode":{"type":"string","enum":["block","lines"],"description":"block: one box around all the lines. lines: a box hugging each line (TikTok's own text background)."}},"required":["color"],"additionalProperties":false,"description":"A background box behind the text."}},"required":["text"],"additionalProperties":false}}}}}},"/api/v1/workspaces/{workspace}/slideshows/{id}/texts/{text_id}":{"patch":{"operationId":"updateText","summary":"Change a text: words, place, font or style (a preset sets the style fields you don't)","tags":["Slideshows"],"description":"Who can call it: An API token with the \"drafts\" scope, or a member of this workspace signed in with a passkey. Refused (402 workspace_inactive) once the workspace's plan has lapsed.","security":[{"token":["drafts"]},{"session":[]}],"x-auth":"scope","x-scope":"drafts","x-plan":"not_lapsed","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^drf_[a-z0-9]{16}$"}},{"name":"text_id","in":"path","required":true,"schema":{"type":"string","pattern":"^txt_[a-z0-9]{8,16}$"}}],"responses":{"200":{"description":"Change a text: words, place, font or style (a preset sets the style fields you don't)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SlideshowDraft"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"rev":{"type":"integer","description":"The rev you read: the change is refused (rev_conflict) if the draft changed since. Left out, it applies to the draft as it is now."},"text":{"type":"string","maxLength":2000,"description":"The words, with \\n for a new line. Emoji are drawn in color."},"preset":{"type":"string","enum":["outline","white","black","yellow","white_box","white_box_50","black_box","black_box_50","tiktok_white","tiktok_black","whiteText","blackText","yellowText","white_background","white_50_background","black_background","black_50_background"],"description":"Fills in the style fields you leave out (color, stroke, shadow, box). Default outline, ss-content's look. Not kept: the document keeps the result."},"x":{"type":"number","minimum":-10000,"maximum":10000,"description":"Left edge of the text's box, in pixels from the image's left. Default 90."},"y":{"type":"number","minimum":-10000,"maximum":10000,"description":"Top of the text's box. Default 0 with height the whole image, so the text sits in the middle."},"width":{"type":"number","minimum":1,"maximum":10000,"description":"The box's width: lines wrap to it. Default the image's width less 180."},"height":{"type":["number","null"],"minimum":1,"maximum":10000,"description":"The box's height, for valign. null: as tall as the text."},"align":{"type":"string","enum":["left","center","right"],"description":"Default center."},"valign":{"type":"string","enum":["top","middle","bottom"],"description":"Where the text sits in the box when height is set. Default middle."},"font":{"type":"string","enum":["tiktok-sans","geist","bebas-neue","cormorant-garamond"],"description":"Default tiktok-sans. GET /api/v1/slideshows/styles lists each family's weights."},"weight":{"type":"integer","minimum":100,"maximum":900,"description":"The nearest weight the font has is used. Default the font's own."},"italic":{"type":"boolean","description":"Only fonts with an italic have one."},"size":{"type":"number","minimum":8,"maximum":400,"description":"Font size in pixels. Default 64."},"line_height":{"type":"number","minimum":0.7,"maximum":3,"description":"Line spacing as a multiple of the size. Default 1.2."},"letter_spacing":{"type":"number","minimum":-50,"maximum":200,"description":"Extra pixels between characters. Default 0."},"color":{"type":"string","pattern":"^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","description":"The text's color. Default #ffffff."},"stroke":{"type":["object","null"],"properties":{"color":{"type":"string","pattern":"^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","description":"A color: #rgb, #rrggbb or #rrggbbaa (aa is opacity)."},"width":{"type":"number","minimum":0,"maximum":60,"description":"Outline thickness in pixels, outside the letters."}},"required":["color","width"],"additionalProperties":false,"description":"An outline around the letters (not emoji)."},"shadow":{"type":["object","null"],"properties":{"color":{"type":"string","pattern":"^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","description":"A color: #rgb, #rrggbb or #rrggbbaa (aa is opacity)."},"x":{"type":"number","minimum":-200,"maximum":200},"y":{"type":"number","minimum":-200,"maximum":200},"blur":{"type":"number","minimum":0,"maximum":200,"description":"Blur radius in pixels, as in CSS."}},"required":["color","x","y","blur"],"additionalProperties":false,"description":"A drop shadow under the text and its box."},"box":{"type":["object","null"],"properties":{"color":{"type":"string","pattern":"^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","description":"A color: #rgb, #rrggbb or #rrggbbaa (aa is opacity)."},"padding_x":{"type":"number","minimum":0,"maximum":200},"padding_y":{"type":"number","minimum":0,"maximum":200},"radius":{"type":"number","minimum":0,"maximum":200},"mode":{"type":"string","enum":["block","lines"],"description":"block: one box around all the lines. lines: a box hugging each line (TikTok's own text background)."}},"required":["color"],"additionalProperties":false,"description":"A background box behind the text."}},"required":[],"additionalProperties":false}}}}},"delete":{"operationId":"deleteText","summary":"Remove a text","tags":["Slideshows"],"description":"Who can call it: An API token with the \"drafts\" scope, or a member of this workspace signed in with a passkey. Refused (402 workspace_inactive) once the workspace's plan has lapsed.","security":[{"token":["drafts"]},{"session":[]}],"x-auth":"scope","x-scope":"drafts","x-plan":"not_lapsed","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^drf_[a-z0-9]{16}$"}},{"name":"text_id","in":"path","required":true,"schema":{"type":"string","pattern":"^txt_[a-z0-9]{8,16}$"}}],"responses":{"200":{"description":"Remove a text","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SlideshowDraft"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"rev":{"type":"integer","description":"The rev you read: the change is refused (rev_conflict) if the draft changed since. Left out, it applies to the draft as it is now."}},"required":[],"additionalProperties":false}}}}}},"/api/v1/workspaces/{workspace}/slideshows/{id}/pdf":{"post":{"operationId":"exportSlideshowPdf","summary":"Export the current slides as a lossless PDF document for a separate LinkedIn draft","tags":["Slideshows"],"description":"Renders the slideshow's current slides if needed, then exports their exact PNG pixels in order, one PDF page per slide. Attach the returned media.id as document.media_id on a separate linkedin_post draft, with its own document.title. The slideshow stays a TikTok draft; this does not approve, schedule or post anything. Who can call it: An API token with the \"drafts\" scope, or a member of this workspace signed in with a passkey. Refused (402 workspace_inactive) once the workspace's plan has lapsed.","security":[{"token":["drafts"]},{"session":[]}],"x-auth":"scope","x-scope":"drafts","x-plan":"not_lapsed","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^drf_[a-z0-9]{16}$"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","minLength":1,"maxLength":255,"pattern":"^[\\x20-\\x7e]+$","description":"Any unique string, like a UUID. Sending the same key and request again answers what the first request answered (Idempotent-Replayed: true) and makes nothing twice; the same key with another request is refused (idempotency_key_reused).","examples":["b3f1c7e2-6f0a-4d1e-9c4a-2f8e5d7a1b90"]},"description":"Any unique string, like a UUID. Sending the same key and request again answers what the first request answered (Idempotent-Replayed: true) and makes nothing twice; the same key with another request is refused (idempotency_key_reused)."}],"responses":{"200":{"description":"Export the current slides as a lossless PDF document for a separate LinkedIn draft","headers":{"Idempotent-Replayed":{"description":"true when this is the answer an earlier request with the same Idempotency-Key got; nothing new was made.","schema":{"type":"string","enum":["true"]}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SlideshowPdfResult"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"rev":{"type":"integer","description":"The rev you read: the change is refused (rev_conflict) if the draft changed since. Left out, it applies to the draft as it is now."}},"required":[],"additionalProperties":false}}}}}},"/api/v1/workspaces/{workspace}/slideshows/{id}/render":{"post":{"operationId":"renderSlideshow","summary":"Render every slide to a PNG (the photos that go to TikTok); returns their URLs and the content hash","tags":["Slideshows"],"description":"Who can call it: An API token with the \"drafts\" scope, or a member of this workspace signed in with a passkey. Refused (402 workspace_inactive) once the workspace's plan has lapsed.","security":[{"token":["drafts"]},{"session":[]}],"x-auth":"scope","x-scope":"drafts","x-plan":"not_lapsed","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"id","in":"path","required":true,"schema":{"type":"string","pattern":"^drf_[a-z0-9]{16}$"}},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","minLength":1,"maxLength":255,"pattern":"^[\\x20-\\x7e]+$","description":"Any unique string, like a UUID. Sending the same key and request again answers what the first request answered (Idempotent-Replayed: true) and makes nothing twice; the same key with another request is refused (idempotency_key_reused).","examples":["b3f1c7e2-6f0a-4d1e-9c4a-2f8e5d7a1b90"]},"description":"Any unique string, like a UUID. Sending the same key and request again answers what the first request answered (Idempotent-Replayed: true) and makes nothing twice; the same key with another request is refused (idempotency_key_reused)."}],"responses":{"200":{"description":"Render every slide to a PNG (the photos that go to TikTok); returns their URLs and the content hash","headers":{"Idempotent-Replayed":{"description":"true when this is the answer an earlier request with the same Idempotency-Key got; nothing new was made.","schema":{"type":"string","enum":["true"]}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SlideshowRenderResult"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/plans":{"get":{"operationId":"listPlans","summary":"The plans on sale, their prices and the trial (from Stripe)","tags":["Sign-up"],"description":"Who can call it: No sign-in needed.","security":[],"x-auth":"public","responses":{"200":{"description":"The plans on sale, their prices and the trial (from Stripe)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Plans"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/signup":{"post":{"operationId":"startSignup","summary":"Start a sign-up; returns the link a person opens to add their passkey and card","tags":["Sign-up"],"description":"Who can call it: No sign-in needed.","security":[],"x-auth":"public","responses":{"201":{"description":"Start a sign-up; returns the link a person opens to add their passkey and card","content":{"application/json":{"schema":{"type":"object","properties":{"signup_url":{"type":"string","description":"For the person: their passkey, then Stripe Checkout."},"claim_code":{"type":"string","description":"For the agent: POST it to /signup/claim to get an API token once the passkey is added."},"email":{"type":"string"},"plan":{"type":["string","null"],"enum":["monthly","yearly","founding"]},"expires_at":{"type":"string","format":"date-time"}},"required":["signup_url","claim_code","expires_at"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","pattern":"^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$","maxLength":254,"description":"Kept for receipts and account recovery only."},"time_zone":{"type":"string","description":"An IANA time zone, for example Europe/London or Asia/Tokyo. Best-times research, waking hours and heatmap cells use this local clock. Defaults to America/Los_Angeles when omitted at sign-up."},"plan":{"type":"string","enum":["monthly","yearly","founding"]},"invite":{"type":"string","maxLength":64,"description":"The signup_id from a waitlist invite link (#invite=…)."},"agent_name":{"type":"string","maxLength":80,"description":"Who started it, like \"Claude Code\". Names the token the claim returns."},"referrer":{"type":"string","maxLength":2000,"description":"The page that sent them here; only its host is kept."},"landing_path":{"type":"string","maxLength":500},"utm_source":{"type":"string","maxLength":500},"utm_medium":{"type":"string","maxLength":500},"utm_campaign":{"type":"string","maxLength":500},"utm_content":{"type":"string","maxLength":500},"utm_term":{"type":"string","maxLength":500}},"required":["email"],"additionalProperties":false}}}}}},"/api/v1/signup/claim":{"post":{"operationId":"claimSignup","summary":"Collect the API token for a sign-up you started (once); 202 while the person hasn't added their passkey yet","tags":["Sign-up"],"description":"Who can call it: No sign-in needed.","security":[],"x-auth":"public","responses":{"200":{"description":"Collect the API token for a sign-up you started (once); 202 while the person hasn't added their passkey yet","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","enum":["pending","ready"]},"expires_at":{"type":"string","format":"date-time"},"workspace_id":{"type":"string"},"token":{"type":"string","description":"Shown only now. It has every scope; none can approve, schedule or post."},"token_id":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"}}},"required":["status"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"claim_code":{"type":"string"}},"required":["claim_code"],"additionalProperties":false}}}}}},"/api/v1/invites/{signup_id}":{"get":{"operationId":"getInvite","summary":"The email a waitlist invite was sent to, to fill in the sign-up form","tags":["Sign-up"],"description":"Who can call it: No sign-in needed.","security":[],"x-auth":"public","parameters":[{"name":"signup_id","in":"path","required":true,"schema":{"type":"string","pattern":"^[0-9a-fA-F-]{8,64}$"}}],"responses":{"200":{"description":"The email a waitlist invite was sent to, to fill in the sign-up form","content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string"}},"required":["email"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/auth/recover":{"post":{"operationId":"recoverAccount","summary":"Lost your passkey: emails the account a one-time link to add a new one","tags":["Sign-in"],"description":"At most one recovery email per account every five minutes; unknown target emails get the same window. Repeats send nothing and don't extend it. Always answers 200 {\"ok\":true}, without an account-existence or cooldown hint. Receipts and other routes are unaffected. Who can call it: No sign-in needed.","security":[],"x-auth":"public","responses":{"200":{"description":"Lost your passkey: emails the account a one-time link to add a new one","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}},"required":["ok"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","pattern":"^[^@\\s]+@[^@\\s]+\\.[^@\\s]+$","maxLength":254,"description":"Kept for receipts and account recovery only."}},"required":["email"],"additionalProperties":false}}}}}},"/api/v1/workspaces/{workspace}/billing":{"get":{"operationId":"getBilling","summary":"The workspace's plan and whether it can post","tags":["Billing"],"description":"Who can call it: An API token with the \"read\" scope, or a member of this workspace signed in with a passkey.","security":[{"token":["read"]},{"session":[]}],"x-auth":"scope","x-scope":"read","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}}],"responses":{"200":{"description":"The workspace's plan and whether it can post","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Billing"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/workspaces/{workspace}/billing/checkout":{"post":{"operationId":"startCheckout","summary":"A Stripe Checkout link to start the trial, or restart a plan; the person enters the card there","tags":["Billing"],"description":"Who can call it: An API token with the \"billing\" scope, or an owner of this workspace signed in with a passkey.","security":[{"token":["billing"]},{"session":[]}],"x-auth":"scope","x-scope":"billing","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}}],"responses":{"200":{"description":"A Stripe Checkout link to start the trial, or restart a plan; the person enters the card there","content":{"application/json":{"schema":{"type":"object","properties":{"checkout_url":{"type":"string"},"checkout_session_id":{"type":"string"},"plan":{"type":"string","enum":["monthly","yearly","founding"]},"trial_days":{"type":"integer"},"expires_at":{"type":"string","format":"date-time"}},"required":["checkout_url"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"plan":{"type":"string","enum":["monthly","yearly","founding"]}},"required":[],"additionalProperties":false}}}}}},"/api/v1/workspaces/{workspace}/billing/sync":{"post":{"operationId":"confirmCheckout","summary":"Read a finished Checkout from Stripe now, without waiting for its webhook","tags":["Billing"],"description":"Who can call it: An API token with the \"billing\" scope, or an owner of this workspace signed in with a passkey.","security":[{"token":["billing"]},{"session":[]}],"x-auth":"scope","x-scope":"billing","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}}],"responses":{"200":{"description":"Read a finished Checkout from Stripe now, without waiting for its webhook","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Billing"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"checkout_session_id":{"type":"string","pattern":"^cs_"}},"required":["checkout_session_id"],"additionalProperties":false}}}}}},"/api/v1/workspaces/{workspace}/billing/portal":{"post":{"operationId":"openBillingPortal","summary":"A Stripe Customer Portal link: card, invoices, monthly or yearly, cancel","tags":["Billing"],"description":"Who can call it: An API token with the \"billing\" scope, or an owner of this workspace signed in with a passkey.","security":[{"token":["billing"]},{"session":[]}],"x-auth":"scope","x-scope":"billing","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}}],"responses":{"200":{"description":"A Stripe Customer Portal link: card, invoices, monthly or yearly, cancel","content":{"application/json":{"schema":{"type":"object","properties":{"portal_url":{"type":"string"}},"required":["portal_url"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/workspaces/{workspace}/billing/cancel":{"post":{"operationId":"cancelPlan","summary":"Cancel at the end of the period (a trial: when it ends, with nothing charged)","tags":["Billing"],"description":"Who can call it: An API token with the \"billing\" scope, or an owner of this workspace signed in with a passkey.","security":[{"token":["billing"]},{"session":[]}],"x-auth":"scope","x-scope":"billing","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}}],"responses":{"200":{"description":"Cancel at the end of the period (a trial: when it ends, with nothing charged)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Billing"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/workspaces/{workspace}/billing/resume":{"post":{"operationId":"resumePlan","summary":"Keep the plan: undo a cancel before the period ends","tags":["Billing"],"description":"Who can call it: An API token with the \"billing\" scope, or an owner of this workspace signed in with a passkey.","security":[{"token":["billing"]},{"session":[]}],"x-auth":"scope","x-scope":"billing","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}}],"responses":{"200":{"description":"Keep the plan: undo a cancel before the period ends","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Billing"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/workspaces/{workspace}/export":{"get":{"operationId":"exportWorkspace","summary":"Everything in the workspace, in any plan state: a zip of Markdown drafts, media and one JSON file, or (format=json) just the JSON","tags":["Workspaces"],"description":"Who can call it: An API token with the \"read\" scope, or a member of this workspace signed in with a passkey.","security":[{"token":["read"]},{"session":[]}],"x-auth":"scope","x-scope":"read","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}},{"name":"format","in":"query","required":false,"schema":{"type":"string","enum":["zip","json"]}}],"responses":{"200":{"description":"Everything in the workspace, in any plan state: a zip of Markdown drafts, media and one JSON file, or (format=json) just the JSON","content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/workspaces/{workspace}/delete/options":{"post":{"operationId":"startDeleteWorkspace","summary":"The passkey challenge for deleting the account and its workspace","tags":["Workspaces"],"description":"Who can call it: An owner of this workspace, signed in with a passkey (session cookie). API tokens are always refused (owner_session_required), whatever their scopes.","security":[{"session":[]}],"x-auth":"owner","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}}],"responses":{"200":{"description":"The passkey challenge for deleting the account and its workspace","content":{"application/json":{"schema":{"type":"object","properties":{"challenge_id":{"type":"string"},"options":{"type":"object"}},"required":["challenge_id","options"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/workspaces/{workspace}/delete":{"post":{"operationId":"deleteWorkspace","summary":"Delete the account: cancels the plan at once (no refund), revokes the platform logins and deletes everything but the billing and consent records","tags":["Workspaces"],"description":"Who can call it: An owner of this workspace, signed in with a passkey (session cookie). API tokens are always refused (owner_session_required), whatever their scopes.","security":[{"session":[]}],"x-auth":"owner","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}}],"responses":{"200":{"description":"Delete the account: cancels the plan at once (no refund), revokes the platform logins and deletes everything but the billing and consent records","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"canceled_subscription":{"type":["string","null"]},"media_deleted":{"type":"integer"},"people_deleted":{"type":"integer"}},"required":["ok"],"additionalProperties":false}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"challenge_id":{"type":"string"},"response":{"type":"object","description":"The passkey assertion over the challenge."}},"required":["challenge_id","response"],"additionalProperties":false}}}}}},"/api/v1/workspaces/{workspace}/setup":{"get":{"operationId":"getSetup","summary":"Check saved posting setup, upcoming login or plan ends, agent scopes and failing webhooks","tags":["Workspaces"],"description":"Read-only saved-state report with next steps. Does not refresh credentials, call platforms or Stripe, retry webhooks, approve, schedule or post. Works on inactive plans. A healthy report does not replace checkDraft or the owner's passkey approval. Login warnings match the desk's existing seven-day warning; the current posting check retains its existing headroom. Who can call it: An API token with the \"read\" scope, or a member of this workspace signed in with a passkey.","security":[{"token":["read"]},{"session":[]}],"x-auth":"scope","x-scope":"read","x-plan":"any","parameters":[{"name":"workspace","in":"path","required":true,"schema":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"}}],"responses":{"200":{"description":"Check saved posting setup, upcoming login or plan ends, agent scopes and failing webhooks","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkspaceSetup"}}}},"default":{"description":"An error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message"],"additionalProperties":true,"properties":{"code":{"type":"string","enum":["invalid_request","invalid_json","unauthorized","invalid_token","session_expired","scope_missing","owner_session_required","bad_origin","approval_required","passkey_failed","not_found","method_not_allowed","rev_conflict","draft_locked","draft_changed","job_exists","job_state","challenge_expired","conflict","workspace_inactive","signup_closed","account_exists","payload_too_large","unsupported_media_type","check_failed","idempotency_key_reused","not_supported","x_credits_exhausted","upstream_failed","internal"]},"message":{"type":"string","description":"One plain sentence."}}}}},"Workspace":{"type":"object","properties":{"id":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"},"name":{"type":"string"},"time_zone":{"type":"string","description":"An IANA time zone, for example Europe/London or Asia/Tokyo. Best-times research, waking hours and heatmap cells use this local clock. Defaults to America/Los_Angeles when omitted at sign-up."},"role":{"type":"string","enum":["owner","editor","token"]},"created_at":{"type":"string","format":"date-time"}},"required":["id","name","time_zone"],"additionalProperties":false},"Me":{"type":"object","properties":{"kind":{"type":"string","enum":["user","token"]},"user":{"type":["object","null"],"properties":{"id":{"type":"string","pattern":"^usr_[a-z0-9]{16}$"},"email":{"type":"string"},"name":{"type":["string","null"]}},"required":["id","email"],"additionalProperties":false},"token":{"type":["object","null"],"properties":{"id":{"type":"string","pattern":"^tkn_[a-z0-9]{16}$"},"name":{"type":"string"},"prefix":{"type":"string"},"scopes":{"type":"array","items":{"type":"string","enum":["read","drafts","media","request-approval","cancel","connections","billing","webhooks","calendar"]}},"created_by":{"type":["string","null"]},"created_at":{"type":"string","format":"date-time"},"last_used_at":{"type":["string","null"],"format":"date-time"},"revoked_at":{"type":["string","null"],"format":"date-time"},"oauth":{"type":["object","null"],"properties":{"client_id":{"type":"string","description":"The app's client_id: its metadata document's URL, or the id it registered."},"redirect_host":{"type":["string","null"],"description":"Where its sign-in went back to; localhost for an app on the owner's computer."}},"required":["client_id"],"additionalProperties":false,"description":"Set when an app got this token by OAuth (the owner signed in and allowed it); null for a token made by hand."}},"required":["id","name","scopes"],"additionalProperties":false},"workspaces":{"type":"array","items":{"$ref":"#/components/schemas/Workspace"}}},"required":["kind","workspaces"],"additionalProperties":false},"User":{"type":"object","properties":{"id":{"type":"string","pattern":"^usr_[a-z0-9]{16}$"},"email":{"type":"string"},"name":{"type":["string","null"]}},"required":["id","email"],"additionalProperties":false},"Notifications":{"type":"object","properties":{"post_attention_email":{"type":"boolean","description":"Email the workspace's owners when an approved post fails or is held. On by default for existing and new workspaces; off discards unsent notices. No success or marketing emails."}},"required":["post_attention_email"],"additionalProperties":false},"Voice":{"type":"object","properties":{"text":{"type":"string","description":"Markdown: how the owner writes, topics to skip, links and handles to use. Empty until someone writes one."},"updated_at":{"type":["string","null"],"format":"date-time"},"updated_by":{"type":["object","null"],"properties":{"type":{"type":"string","enum":["user","token","operator","system"]},"id":{"type":["string","null"]}},"required":["type","id"],"additionalProperties":false}},"required":["text","updated_at","updated_by"],"additionalProperties":false,"description":"The workspace's voice note: every agent reads it before drafting. Never posted and never approved."},"Member":{"type":"object","properties":{"user_id":{"type":"string","pattern":"^usr_[a-z0-9]{16}$"},"email":{"type":"string"},"name":{"type":["string","null"]},"role":{"type":"string","enum":["owner","editor"]},"passkeys":{"type":"integer"},"created_at":{"type":"string","format":"date-time"}},"required":["user_id","role"],"additionalProperties":false},"Token":{"type":"object","properties":{"id":{"type":"string","pattern":"^tkn_[a-z0-9]{16}$"},"name":{"type":"string"},"prefix":{"type":"string"},"scopes":{"type":"array","items":{"type":"string","enum":["read","drafts","media","request-approval","cancel","connections","billing","webhooks","calendar"]}},"created_by":{"type":["string","null"]},"created_at":{"type":"string","format":"date-time"},"last_used_at":{"type":["string","null"],"format":"date-time"},"revoked_at":{"type":["string","null"],"format":"date-time"},"oauth":{"type":["object","null"],"properties":{"client_id":{"type":"string","description":"The app's client_id: its metadata document's URL, or the id it registered."},"redirect_host":{"type":["string","null"],"description":"Where its sign-in went back to; localhost for an app on the owner's computer."}},"required":["client_id"],"additionalProperties":false,"description":"Set when an app got this token by OAuth (the owner signed in and allowed it); null for a token made by hand."}},"required":["id","name","scopes"],"additionalProperties":false},"NewToken":{"type":"object","properties":{"id":{"type":"string","pattern":"^tkn_[a-z0-9]{16}$"},"name":{"type":"string"},"prefix":{"type":"string"},"scopes":{"type":"array","items":{"type":"string","enum":["read","drafts","media","request-approval","cancel","connections","billing","webhooks","calendar"]}},"created_by":{"type":["string","null"]},"created_at":{"type":"string","format":"date-time"},"last_used_at":{"type":["string","null"],"format":"date-time"},"revoked_at":{"type":["string","null"],"format":"date-time"},"oauth":{"type":["object","null"],"properties":{"client_id":{"type":"string","description":"The app's client_id: its metadata document's URL, or the id it registered."},"redirect_host":{"type":["string","null"],"description":"Where its sign-in went back to; localhost for an app on the owner's computer."}},"required":["client_id"],"additionalProperties":false,"description":"Set when an app got this token by OAuth (the owner signed in and allowed it); null for a token made by hand."},"token":{"type":"string","description":"Send as Authorization: Bearer <token>. Shown only now."}},"required":["id","token","scopes"],"additionalProperties":false},"Profile":{"type":"object","properties":{"name":{"type":["string","null"]},"handle":{"type":["string","null"]},"kind":{"type":["string","null"],"description":"person, company, school or showcase (LinkedIn)."},"verified":{"type":["string","null"],"enum":["blue","gold","gray"]},"premium":{"type":"boolean"},"headline":{"type":["string","null"]},"cta":{"type":["string","null"],"description":"A LinkedIn profile's button, like \"Visit my website\"."},"avatar_url":{"type":["string","null"],"description":"Relative to the API's origin."},"affiliate_url":{"type":["string","null"],"description":"An X affiliate badge. Relative to the API's origin."},"updated_at":{"type":["string","null"],"format":"date-time"}},"required":[],"additionalProperties":false,"description":"How the account looks in previews. Changing it never changes what's posted."},"Connection":{"type":"object","properties":{"id":{"type":"string"},"platform":{"type":"string","enum":["x","linkedin","tiktok"]},"external_id":{"type":["string","null"]},"handle":{"type":["string","null"]},"display_name":{"type":["string","null"]},"status":{"type":"string","description":"active, expired (connect it again) or removed."},"scopes":{"type":"array","items":{"type":"string"}},"expires_at":{"type":["string","null"],"format":"date-time","description":"Last usable login instant when known: the non-renewing access expiry or the later of access and refresh expiry. Null for a renewable login with no known end."},"renews":{"type":"boolean"},"connected_at":{"type":["string","null"],"format":"date-time"},"subscription_type":{"type":["string","null"],"description":"X only: subscription_type from the authenticated user object, not the editable preview profile. Basic, Premium and PremiumPlus allow a single longer post; null means unknown. Long-post checks refresh this before approval."},"creator":{"type":["object","null"],"properties":{"username":{"type":["string","null"]},"nickname":{"type":["string","null"]},"avatar_url":{"type":["string","null"]},"privacy_level_options":{"type":"array","items":{"type":"string"}},"comment_disabled":{"type":["boolean","null"]},"duet_disabled":{"type":["boolean","null"]},"stitch_disabled":{"type":["boolean","null"]},"max_video_post_duration_sec":{"type":["integer","null"]},"can_post":{"type":["boolean","null"]},"fetched_at":{"type":"string","format":"date-time"}},"required":[],"additionalProperties":false,"description":"What TikTok says about the account (creator info), as of fetched_at."},"audit_posture":{"type":["string","null"],"enum":["audited","unaudited","unknown"],"description":"TikTok only. unaudited: posting directly works only for a private account, seen only by its owner. unknown: no posting directly. Drafts sent to the inbox work either way."},"profile":{"$ref":"#/components/schemas/Profile"}},"required":["id","platform","status"],"additionalProperties":false},"WeeklyPostingSlot":{"type":"object","properties":{"day":{"type":"integer","minimum":0,"maximum":6,"description":"Day of the week: Sunday 0 through Saturday 6."},"time":{"type":"string","pattern":"^(?:[01]\\d|2[0-3]):[0-5]\\d$","description":"Local wall-clock time, HH:MM, in this connection's zone."}},"required":["day","time"],"additionalProperties":false},"ConnectionPostingSlots":{"type":"object","properties":{"zone":{"type":"string","description":"IANA time zone, like America/New_York or UTC. Independent of the workspace display zone."},"slots":{"type":"array","items":{"$ref":"#/components/schemas/WeeklyPostingSlot"},"description":"Weekly proposed times. Empty clears the plan. Duplicate day/time pairs are folded together. A missing spring-forward time is skipped; an autumn repeated time uses its first occurrence once."}},"required":["zone","slots"],"additionalProperties":false},"TikTokCreatorInfo":{"type":"object","properties":{"username":{"type":["string","null"]},"nickname":{"type":["string","null"]},"avatar_url":{"type":["string","null"]},"privacy_level_options":{"type":"array","items":{"type":"string"}},"comment_disabled":{"type":["boolean","null"]},"duet_disabled":{"type":["boolean","null"]},"stitch_disabled":{"type":["boolean","null"]},"max_video_post_duration_sec":{"type":["integer","null"]},"can_post":{"type":["boolean","null"]},"fetched_at":{"type":"string","format":"date-time"}},"required":[],"additionalProperties":false,"description":"What TikTok says about the account (creator info), as of fetched_at."},"DraftLabels":{"type":"array","items":{"type":"string"},"description":"Planning labels, never posted or included in approval content. Exact, case-sensitive strings; duplicates are stored once. Defaults to []; PATCH replaces the whole list, [] clears it."},"MediaRef":{"type":"object","properties":{"media_id":{"type":"string","pattern":"^med_[a-z0-9]{16}$"},"alt":{"type":"string","maxLength":4086,"description":"Alt text. Left out, the media library's alt text is copied in."}},"required":["media_id"],"additionalProperties":false},"XPoll":{"type":"object","properties":{"options":{"type":"array","items":{"type":"string","minLength":1,"maxLength":25},"minItems":2,"maxItems":4,"description":"Choices in display order. No media on the same post."},"duration_minutes":{"type":"integer","minimum":5,"maximum":10080,"description":"How long voting stays open, in whole minutes (5 minutes to 7 days)."}},"required":["options","duration_minutes"],"additionalProperties":false},"XPostBody":{"type":"object","properties":{"text":{"type":"string","description":"The post's exact text. Check applies X's weighted limit, not a raw code-point limit: links count as 23 and an emoji sequence as 2. @handles are posted as typed."},"media":{"type":"array","items":{"$ref":"#/components/schemas/MediaRef"},"maxItems":4},"poll":{"$ref":"#/components/schemas/XPoll"},"reply_settings":{"type":"string","enum":["following","mentionedUsers","verified","subscribers"],"description":"Who can reply: people you follow, mentioned users, verified accounts, or subscribers. Omit for everyone. On a thread, set it on posts[0] only."},"repost_after_hours":{"type":"number","exclusiveMinimum":0,"description":"Repost this post (a thread's first post) this many hours after it goes live. Optional; omitted means no repost. Included in the owner's passkey approval. Fractions of an hour are supported."},"quote_url":{"type":["string","null"],"pattern":"^https://(x|twitter)\\.com/[A-Za-z0-9_]{1,15}/status/\\d+","description":"A live X post to quote. X no longer lets apps quote posts, even your own: check offers posting it by hand or dropping the quote."},"reply_to_url":{"type":["string","null"],"pattern":"^https://(x|twitter)\\.com/[A-Za-z0-9_]{1,15}/status/\\d+","description":"A live X post to reply to."},"community":{"type":["string","null"],"pattern":"^(?:[0-9]{1,19}|https://(?:x|twitter)\\.com/i/communities/[0-9]{1,19}/?(?:[?#][^\\s]*)?)$","description":"An X Community ID or https://x.com/i/communities/<id> URL. Check looks up and freezes its name once per workspace ($0.005); the posting account must be allowed to post there."},"share_with_followers":{"type":["boolean","null"],"description":"Optional X API share_with_followers value, sent unchanged with a Community destination. X's July 28, 2025 changelog says true with community_id also broadcasts the post to the authenticated user's followers' timelines. Its 2.172 schema still describes super-follower sharing. No default is selected. X does not return this flag for read-back, so follower sharing cannot be verified."},"by_hand":{"type":["string","null"],"enum":["all"],"description":"\"all\": the owner posts it by hand; approval records the decision only."}},"required":["text"],"additionalProperties":false,"description":"One X post. Check counts X's weighted characters: 280 normally, up to 25,000 when X confirms Basic, Premium or PremiumPlus for its connection. A long-post check re-reads subscription_type; the scheduler checks again before posting. Its timeline preview has Show more; the signed text and live read-back keep the full post."},"XThreadBody":{"type":"object","properties":{"posts":{"type":"array","items":{"type":"object","properties":{"text":{"type":"string","maxLength":25000,"description":"The post's exact text. @handles are posted as typed."},"media":{"type":"array","items":{"$ref":"#/components/schemas/MediaRef"},"maxItems":4},"poll":{"$ref":"#/components/schemas/XPoll"},"reply_settings":{"type":"string","enum":["following","mentionedUsers","verified","subscribers"],"description":"Who can reply: people you follow, mentioned users, verified accounts, or subscribers. Omit for everyone. On a thread, set it on posts[0] only."}},"required":["text"],"additionalProperties":false},"minItems":2,"maxItems":25,"description":"Posted in order, each a reply to the one before. Only posts[0] may have a poll or reply_settings."},"repost_after_hours":{"type":"number","exclusiveMinimum":0,"description":"Repost this post (a thread's first post) this many hours after it goes live. Optional; omitted means no repost. Included in the owner's passkey approval. Fractions of an hour are supported."},"quote_url":{"type":["string","null"],"pattern":"^https://(x|twitter)\\.com/[A-Za-z0-9_]{1,15}/status/\\d+","description":"A live X post that post 1 quotes. X no longer lets apps quote posts: check offers posting post 1 by hand or dropping the quote."},"reply_to_url":{"type":["string","null"],"pattern":"^https://(x|twitter)\\.com/[A-Za-z0-9_]{1,15}/status/\\d+","description":"A live X post that post 1 replies to."},"by_hand":{"type":["string","null"],"enum":["first","all"],"description":"\"first\": the owner posts post 1 in X and the rest are posted under it once it's live. \"all\": the owner posts it all."}},"required":["posts"],"additionalProperties":false,"description":"An X thread: one draft, one approval."},"LinkedInPoll":{"type":"object","properties":{"question":{"type":"string","minLength":1,"maxLength":140,"description":"The poll's question, shown above the options. Plain text: @[Tag]s and hashtags work only in text."},"options":{"type":"array","items":{"type":"string","minLength":1,"maxLength":30},"minItems":2,"maxItems":4,"description":"Options in display order."},"duration":{"type":"string","enum":["ONE_DAY","THREE_DAYS","SEVEN_DAYS","FOURTEEN_DAYS"],"description":"How long voting stays open: 1, 3, 7 or 14 days from posting."}},"required":["question","options","duration"],"additionalProperties":false,"description":"A poll instead of media or a link card. Single choice; LinkedIn shows the author who voted for what."},"LinkedInPostBody":{"type":"object","properties":{"text":{"type":"string","maxLength":6000,"description":"The post's text. @[Exact Page Name] tags a company page listed in tags; LinkedIn allows 3,000 characters, counted as UTF-16 code units of the text as posted: a tag counts its name, most emoji and formatter bold or italic letters 2, a flag or skin-toned emoji 4."},"media":{"type":"array","items":{"$ref":"#/components/schemas/MediaRef"},"maxItems":20,"description":"Up to 20 photos, or one MP4 video on its own without a link card. Videos API: 75 KB–500 MB, 3 seconds–30 minutes; the shared upload endpoint currently accepts files up to 50 MB. Check reads duration from the file, not supplied metadata. Video alt text is not sent (LinkedIn has no video alt-text field)."},"document":{"type":["object","null"],"properties":{"media_id":{"type":"string","pattern":"^med_[a-z0-9]{16}$","description":"An uploaded PDF, or media.id from POST …/slideshows/{id}/pdf."},"title":{"type":"string","minLength":1,"description":"The document's visible LinkedIn title, frozen with the PDF in the approval."}},"required":["media_id","title"],"additionalProperties":false,"description":"One swipeable PDF document. Use instead of media or card. LinkedIn allows up to 300 pages and 100 MB; the shared uploader accepts 50 MB."},"card":{"type":["object","null"],"properties":{"url":{"type":"string","pattern":"^https?://"},"title":{"type":"string","maxLength":400},"description":{"type":"string","maxLength":4000},"image_media_id":{"type":"string","pattern":"^med_[a-z0-9]{16}$"}},"required":["url"],"additionalProperties":false,"description":"A link card, used when there is no media. Can't accompany a video."},"tags":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80},"kind":{"type":"string","enum":["organization","person"]},"urn":{"type":"string","pattern":"^urn:li:(organization|company|person):"}},"required":["name","kind"],"additionalProperties":false},"maxItems":50,"description":"Every @[Tag] in text, linked to its LinkedIn page."},"poll":{"$ref":"#/components/schemas/LinkedInPoll"},"by_hand":{"type":["string","null"],"enum":["all"]}},"required":["text"],"additionalProperties":false,"description":"One LinkedIn post."},"SlideText":{"type":"object","properties":{"id":{"type":"string","pattern":"^txt_[a-z0-9]{8,16}$","description":"Assigned when the text is added."},"text":{"type":"string","maxLength":2000,"description":"The words, with \\n for a new line. Emoji are drawn in color."},"preset":{"type":"string","enum":["outline","white","black","yellow","white_box","white_box_50","black_box","black_box_50","tiktok_white","tiktok_black","whiteText","blackText","yellowText","white_background","white_50_background","black_background","black_50_background"],"description":"Fills in the style fields you leave out (color, stroke, shadow, box). Default outline, ss-content's look. Not kept: the document keeps the result."},"x":{"type":"number","minimum":-10000,"maximum":10000,"description":"Left edge of the text's box, in pixels from the image's left. Default 90."},"y":{"type":"number","minimum":-10000,"maximum":10000,"description":"Top of the text's box. Default 0 with height the whole image, so the text sits in the middle."},"width":{"type":"number","minimum":1,"maximum":10000,"description":"The box's width: lines wrap to it. Default the image's width less 180."},"height":{"type":["number","null"],"minimum":1,"maximum":10000,"description":"The box's height, for valign. null: as tall as the text."},"align":{"type":"string","enum":["left","center","right"],"description":"Default center."},"valign":{"type":"string","enum":["top","middle","bottom"],"description":"Where the text sits in the box when height is set. Default middle."},"font":{"type":"string","enum":["tiktok-sans","geist","bebas-neue","cormorant-garamond"],"description":"Default tiktok-sans. GET /api/v1/slideshows/styles lists each family's weights."},"weight":{"type":"integer","minimum":100,"maximum":900,"description":"The nearest weight the font has is used. Default the font's own."},"italic":{"type":"boolean","description":"Only fonts with an italic have one."},"size":{"type":"number","minimum":8,"maximum":400,"description":"Font size in pixels. Default 64."},"line_height":{"type":"number","minimum":0.7,"maximum":3,"description":"Line spacing as a multiple of the size. Default 1.2."},"letter_spacing":{"type":"number","minimum":-50,"maximum":200,"description":"Extra pixels between characters. Default 0."},"color":{"type":"string","pattern":"^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","description":"The text's color. Default #ffffff."},"stroke":{"type":["object","null"],"properties":{"color":{"type":"string","pattern":"^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","description":"A color: #rgb, #rrggbb or #rrggbbaa (aa is opacity)."},"width":{"type":"number","minimum":0,"maximum":60,"description":"Outline thickness in pixels, outside the letters."}},"required":["color","width"],"additionalProperties":false,"description":"An outline around the letters (not emoji)."},"shadow":{"type":["object","null"],"properties":{"color":{"type":"string","pattern":"^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","description":"A color: #rgb, #rrggbb or #rrggbbaa (aa is opacity)."},"x":{"type":"number","minimum":-200,"maximum":200},"y":{"type":"number","minimum":-200,"maximum":200},"blur":{"type":"number","minimum":0,"maximum":200,"description":"Blur radius in pixels, as in CSS."}},"required":["color","x","y","blur"],"additionalProperties":false,"description":"A drop shadow under the text and its box."},"box":{"type":["object","null"],"properties":{"color":{"type":"string","pattern":"^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","description":"A color: #rgb, #rrggbb or #rrggbbaa (aa is opacity)."},"padding_x":{"type":"number","minimum":0,"maximum":200},"padding_y":{"type":"number","minimum":0,"maximum":200},"radius":{"type":"number","minimum":0,"maximum":200},"mode":{"type":"string","enum":["block","lines"],"description":"block: one box around all the lines. lines: a box hugging each line (TikTok's own text background)."}},"required":["color"],"additionalProperties":false,"description":"A background box behind the text."}},"required":["text"],"additionalProperties":false},"Slide":{"type":"object","properties":{"id":{"type":"string","pattern":"^sld_[a-z0-9]{8,16}$","description":"Assigned when the slide is added."},"image":{"type":["object","null"],"properties":{"media_id":{"type":"string","pattern":"^med_[a-z0-9]{16}$","description":"A photo (JPEG, PNG or WebP) from the workspace's media library."},"fit":{"type":"string","enum":["cover","contain"],"description":"cover (default): fill the slide, cropping what doesn't fit. contain: show the whole photo on the background color."},"zoom":{"type":"number","minimum":1,"maximum":10,"description":"1 (default) is the fit's size; 2 shows half as much, twice as large."},"focus":{"type":"object","properties":{"x":{"type":"number","minimum":0,"maximum":1},"y":{"type":"number","minimum":0,"maximum":1}},"required":["x","y"],"additionalProperties":false,"description":"Which part of the photo stays in view when it's cropped: 0,0 the top left, 1,1 the bottom right, 0.5,0.5 (default) the center."}},"required":["media_id"],"additionalProperties":false},"background":{"type":["string","null"],"pattern":"^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","description":"This slide's background color, under its photo. null: the slideshow's."},"overlay":{"type":["object","null"],"properties":{"color":{"type":"string","pattern":"^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","description":"A color: #rgb, #rrggbb or #rrggbbaa (aa is opacity)."},"opacity":{"type":"number","minimum":0,"maximum":1,"description":"0 to 1."}},"required":["color","opacity"],"additionalProperties":false,"description":"This slide's tint. Left out: the slideshow's. null: none."},"texts":{"type":"array","items":{"$ref":"#/components/schemas/SlideText"},"maxItems":20,"description":"Drawn in order: later texts on top."}},"required":[],"additionalProperties":false},"Slideshow":{"type":"object","properties":{"version":{"type":"integer","enum":[1],"description":"The document's schema version: 1."},"aspect":{"type":"string","enum":["9:16","4:5","1:1","16:9"],"description":"Every slide's shape. 9:16 (default) is 1080x1920; 4:5 1080x1350; 1:1 1080x1080; 16:9 1080x608."},"background":{"type":"string","pattern":"^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","description":"The color under every slide's photo (and of slides without one). Default #000000."},"overlay":{"type":["object","null"],"properties":{"color":{"type":"string","pattern":"^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$","description":"A color: #rgb, #rrggbb or #rrggbbaa (aa is opacity)."},"opacity":{"type":"number","minimum":0,"maximum":1,"description":"0 to 1."}},"required":["color","opacity"],"additionalProperties":false,"description":"A tint over every slide's photo, unless a slide sets its own. Default none."},"slides":{"type":"array","items":{"$ref":"#/components/schemas/Slide"},"maxItems":35,"description":"Up to 35 slides, in order. Each becomes one photo of the TikTok post; rendering needs at least one."}},"required":["slides"],"additionalProperties":false,"description":"A slideshow document, schema version 1: the slides of a TikTok photo post (the slideshow field of a tiktok_photo draft's body), rendered to one 1080-wide PNG per slide by POST …/slideshows/{id}/render. Send media or slideshow in a body, not both.","x-schema-version":1},"TikTokPhotoBody":{"type":"object","properties":{"title":{"type":"string","maxLength":2200,"description":"The title TikTok shows with the photos. TikTok allows 90 characters; check says so."},"description":{"type":"string","maxLength":4000,"description":"The caption under it, up to 4,000 characters."},"media":{"type":"array","items":{"$ref":"#/components/schemas/MediaRef"},"maxItems":35,"description":"1 to 35 photos (JPEG, PNG or WebP), in order, posted as they are. Each is converted to TikTok's specs (lossless WebP inside 1080x1920) and check shows the copy that goes out. Send media or slideshow, not both."},"slideshow":{"$ref":"#/components/schemas/Slideshow"},"cover_index":{"type":"integer","minimum":0,"maximum":34,"description":"Which photo (or slide) is the cover (0 is the first). Default 0."},"auto_add_music":{"type":"boolean","description":"Posting directly: let TikTok add recommended music. Default true."},"post_mode":{"type":"string","enum":["direct","inbox"],"description":"direct: publish to the account (the owner picks who can view it when approving). inbox: send it to the account's TikTok inbox as a draft, to finish and publish in TikTok. Default direct."}},"required":[],"additionalProperties":false,"description":"A TikTok photo post: photos as they are (media), or slides designed and rendered here (slideshow). It goes out when the owner approves it to post now; there's no scheduling on TikTok."},"TikTokVideoBody":{"type":"object","properties":{"title":{"type":"string","maxLength":2200,"description":"The video's caption, up to 2,200 characters."},"media":{"type":"array","items":{"$ref":"#/components/schemas/MediaRef"},"maxItems":1,"description":"Exactly one MP4 or MOV video, up to 64 MB (sent to TikTok in one piece)."},"post_mode":{"type":"string","enum":["direct","inbox"],"description":"direct: publish to the account. inbox: send it to the TikTok inbox as a draft. Default direct."}},"required":["media"],"additionalProperties":false,"description":"A TikTok video. It goes out when the owner approves it to post now."},"Actor":{"type":"object","properties":{"type":{"type":"string","enum":["user","token","operator","system"]},"id":{"type":["string","null"]}},"required":["type","id"],"additionalProperties":false},"TikTokAttempt":{"type":"object","properties":{"publish_id":{"type":["string","null"],"description":"TikTok's id for this attempt, kept from the moment TikTok gives it. Its status is read, never sent again."},"status":{"type":["string","null"],"description":"TikTok's last status: PROCESSING_UPLOAD, PROCESSING_DOWNLOAD, SEND_TO_USER_INBOX, PUBLISH_COMPLETE, FAILED, …"},"post_mode":{"type":["string","null"],"enum":["DIRECT_POST","MEDIA_UPLOAD"]},"fail_reason":{"type":["string","null"]},"unconfirmed":{"type":"boolean","description":"TikTok didn't answer clearly, so the post may or may not be on TikTok. It's never sent again on its own."},"refused":{"type":["object","null"],"description":"TikTok refused the request: { code, message, log_id, at }. Nothing was created at TikTok."},"upload_error":{"type":["string","null"]},"polled_at":{"type":["string","null"],"format":"date-time"},"previous":{"type":"array","items":{"type":"object"},"description":"Earlier attempts of the same content, with their publish ids."}},"required":[],"additionalProperties":false},"Draft":{"type":"object","properties":{"id":{"type":"string","pattern":"^drf_[a-z0-9]{16}$"},"workspace_id":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"},"connection_id":{"type":["string","null"]},"kind":{"type":"string","enum":["x_post","x_thread","linkedin_post","tiktok_photo","tiktok_video"]},"title":{"type":["string","null"]},"labels":{"$ref":"#/components/schemas/DraftLabels"},"body":{"type":"object","description":"Shaped by kind.","oneOf":[{"$ref":"#/components/schemas/XPostBody"},{"$ref":"#/components/schemas/XThreadBody"},{"$ref":"#/components/schemas/LinkedInPostBody"},{"$ref":"#/components/schemas/TikTokPhotoBody"},{"$ref":"#/components/schemas/TikTokVideoBody"}]},"post_at":{"type":["string","null"],"format":"date-time"},"rev":{"type":"integer","description":"Send it back with every change."},"status":{"type":"string","enum":["draft","approval_requested","changes_requested","approved"]},"request_note":{"type":["string","null"]},"requested_at":{"type":["string","null"],"format":"date-time"},"changes_requested_note":{"type":["string","null"],"description":"The owner's latest send-back note. Read it, edit the draft, check and request approval again. Retained after edits and renewed requests; earlier notes remain in draft.changes_requested events."},"changes_requested_at":{"type":["string","null"],"format":"date-time","description":"When the owner last sent this draft back."},"created_by":{"$ref":"#/components/schemas/Actor"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"archived":{"type":"boolean"},"meta":{"type":["object","null"],"properties":{"notes":{"type":["array","null"],"items":{"type":"string","maxLength":4000},"maxItems":50,"description":"Source notes for whoever reviews it."},"warnings":{"type":["array","null"],"items":{"type":"string","maxLength":2000},"maxItems":50,"description":"Things to check before posting."},"when":{"type":["string","null"],"maxLength":300,"description":"A note on timing, like \"Tuesday morning\"."},"note":{"type":["string","null"],"maxLength":2000,"description":"The owner's private note."},"original":{"type":["object","null"],"description":"The body as first written, for Reset draft."},"quote_draft_id":{"type":["string","null"],"pattern":"^drf_[a-z0-9]{16}$"}},"required":[],"additionalProperties":false,"description":"Never posted and never approved: notes for the desk."},"legacy_id":{"type":["string","null"],"description":"The local desk's id, for a draft an import moved in."},"approval":{"type":["object","null"],"properties":{"id":{"type":"string","pattern":"^apr_[a-z0-9]{16}$"},"draft_id":{"type":"string","pattern":"^drf_[a-z0-9]{16}$"},"draft_rev":{"type":"integer"},"mode":{"type":"string","enum":["approve","schedule","post_now"],"description":"approve: the decision only. schedule: post at post_at (or, with by_hand \"first\", as soon as post 1 is live). post_now: post right away (the only way TikTok posts go out)."},"snapshot_hash":{"type":"string"},"approved_by":{"type":"string","pattern":"^usr_[a-z0-9]{16}$"},"passkey_id":{"type":"string"},"approved_at":{"type":"string","format":"date-time"},"voided_at":{"type":["string","null"],"format":"date-time"},"void_reason":{"type":["string","null"]},"source":{"type":"string","enum":["passkey","local"],"description":"local: approved in the local desk before passkeys and moved in by an import. It records the decision only; approving again with a passkey is needed to schedule or post."}},"required":["id","draft_id","snapshot_hash"],"additionalProperties":false},"job":{"type":["object","null"],"properties":{"id":{"type":"string"},"draft_id":{"type":"string"},"platform":{"type":"string","enum":["x","linkedin","tiktok"]},"connection_id":{"type":["string","null"]},"approval_id":{"type":["string","null"]},"state":{"type":"string","enum":["waiting","scheduled","held","posting","processing","posted","inbox","failed","canceled"],"description":"held: X credits exhausted or the connection needs a new login; retains approval and posted ids, consumes no retry budget, owner retries after top-up or reconnect. processing: LinkedIn is processing an uploaded video or document, or TikTok is working on the post. inbox: TikTok put it in the creator's TikTok inbox as a draft (finished here, not published: they publish it in TikTok)."},"run_at":{"type":["string","null"],"format":"date-time"},"wait_until":{"type":["string","null"],"format":"date-time"},"result":{"type":["object","null"],"properties":{"ids":{"type":"array","items":{"type":"string"}},"urls":{"type":"array","items":{"type":"string"}},"check":{"type":["object","null"],"description":"The live read-back: { at, ok, issues } or { at, error }. LinkedIn: { available: false, error_code: linkedin_readback_unavailable, error }; no post read-back is attempted with the app's write-only member grant."},"first_by_hand":{"type":"boolean"},"by_hand":{"type":"boolean","description":"The owner posted it themselves; this records its link."},"tiktok":{"$ref":"#/components/schemas/TikTokAttempt"}},"required":[],"additionalProperties":false},"take_down":{"type":["object","null"],"properties":{"complete":{"type":"boolean","description":"Every published id was deleted or already absent. The job stays posted as a historical record; its original result and approval are retained."},"requested_at":{"type":"string","format":"date-time"},"requested_by":{"type":"string"},"passkey_id":{"type":"string"},"posts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","enum":["deleted","already_deleted","failed"]},"at":{"type":"string","format":"date-time"},"error":{"type":"string"}},"required":["id","status","at"],"additionalProperties":false}}},"required":["complete","posts"],"additionalProperties":false},"repost":{"type":["object","null"],"properties":{"state":{"type":"string","enum":["scheduled","posting","posted","canceled","failed"]},"after_hours":{"type":"number"},"run_at":{"type":["string","null"],"format":"date-time"},"posted_at":{"type":["string","null"],"format":"date-time"},"error":{"type":["string","null"]}},"required":["state","after_hours"],"additionalProperties":false,"description":"The approved delayed repost of an X post or thread opening. Separate from the original posting state and result. Failures never resend automatically."},"error":{"type":["string","null"]},"error_code":{"type":["string","null"],"description":"x_credits_exhausted when held for Flaghand's prepaid X credits; connection_login_expired when held until the owner reconnects in Settings and retries; workspace_inactive when the plan wasn't active; duplicate_post when X or LinkedIn refused it as a repeat of a recent post (failed: change the text and approve it again)."},"attempts":{"type":"integer"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"posted_at":{"type":["string","null"],"format":"date-time"}},"required":["id","state"],"additionalProperties":false},"x_thread_live":{"type":["object","null"],"description":"The draft's live X prefix, independent of edits and job ids: { connection_id, ids, parts, root, first_by_hand }. ids and approved posting expectations are in thread order (a hand-first opening may differ on X). null before any thread post is live. The next approval continues after these posts; X cannot edit them."}},"required":["id","kind","body","rev","status"],"additionalProperties":false},"DraftMeta":{"type":"object","properties":{"notes":{"type":["array","null"],"items":{"type":"string","maxLength":4000},"maxItems":50,"description":"Source notes for whoever reviews it."},"warnings":{"type":["array","null"],"items":{"type":"string","maxLength":2000},"maxItems":50,"description":"Things to check before posting."},"when":{"type":["string","null"],"maxLength":300,"description":"A note on timing, like \"Tuesday morning\"."},"note":{"type":["string","null"],"maxLength":2000,"description":"The owner's private note."},"original":{"type":["object","null"],"description":"The body as first written, for Reset draft."},"quote_draft_id":{"type":["string","null"],"pattern":"^drf_[a-z0-9]{16}$"}},"required":[],"additionalProperties":false,"description":"Never posted and never approved: notes for the desk."},"Fix":{"type":"object","properties":{"kind":{"type":"string","description":"connect, hand-first, hand-all, drop-quote, plain-names, approve-only, inbox (send a TikTok post to the inbox as a draft), post-now, render (render the slideshow: POST …/slideshows/{id}/render), repost-live (X threads with live posts: post the whole thread again, an owner-only choice on the approval page, item thread.repost_live; tokens can't) or edit-text (the text repeats a recent post of the account, which X or LinkedIn would refuse: change it; posts lists a thread's repeated post numbers)."},"platform":{"type":"string"},"connection_id":{"type":"string","description":"For a connect fix: reconnect this pinned account, not another profile."},"expires_at":{"type":"string","format":"date-time","description":"For a login-expiry fix: the login's last usable instant, including a known refresh-token expiry."},"tags":{"type":"array","items":{"type":"string"}},"posts":{"type":"array","items":{"type":"integer","description":"For an edit-text fix on a thread: the numbers (from 1) of the posts to change."}},"patch":{"type":["object","null"],"description":"The change to the draft's body that applies this fix (send it with PATCH)."}},"required":["kind"],"additionalProperties":true},"Problem":{"type":"object","properties":{"text":{"type":"string","description":"One plain sentence."},"fixes":{"type":"array","items":{"$ref":"#/components/schemas/Fix"}}},"required":["text"],"additionalProperties":false},"Snapshot":{"type":"object","description":"Exactly what an approval freezes: workspace, draft and rev, platform, the account it posts as, mode, time, the content with resolved media, and the exact request sent to the platform (payload). Its hash is SHA-256 over its canonical JSON (keys sorted)."},"TikTokOwnerChoices":{"type":"object","properties":{"privacy_level_options":{"type":"array","items":{"type":"string"},"description":"Who-can-view options TikTok gives for the account right now. The owner picks one; none is preselected."},"allowed_privacy_levels":{"type":"array","items":{"type":"string"},"description":"The ones this app can use now: all of them once TikTok has audited it, only SELF_ONLY while it's unaudited."},"comments":{"type":["boolean","null"],"description":"Whether comments can be allowed (the account's TikTok settings may turn them off)."},"duet":{"type":"boolean"},"stitch":{"type":"boolean"},"disclose":{"type":"object","properties":{"your_brand":{"type":"boolean"},"branded_content":{"type":"boolean","description":"Branded content can't be Only me, so it needs another privacy level this app can use."}},"required":[],"additionalProperties":false},"consent":{"type":"object","properties":{"statement":{"type":"string"},"links":{"type":"array","items":{"type":"object","properties":{"label":{"type":"string"},"url":{"type":"string"}},"required":[],"additionalProperties":false}}},"required":[],"additionalProperties":false},"audit_posture":{"type":"string"}},"required":[],"additionalProperties":false,"description":"TikTok drafts posted directly: what the owner chooses on TikTok's posting screen when approving."},"Check":{"type":"object","properties":{"ok":{"type":"boolean"},"problems":{"type":"array","items":{"$ref":"#/components/schemas/Problem"}},"snapshot_hash":{"type":"string"},"snapshot":{"$ref":"#/components/schemas/Snapshot"},"owner_choices":{"$ref":"#/components/schemas/TikTokOwnerChoices"}},"required":["ok","problems","snapshot_hash"],"additionalProperties":false},"Media":{"type":"object","properties":{"id":{"type":"string","pattern":"^med_[a-z0-9]{16}$"},"type":{"type":"string"},"kind":{"type":"string","enum":["image","video","document"]},"size":{"type":"integer"},"pages":{"type":"integer","description":"PDF documents only: page count read from the file."},"sha256":{"type":"string","description":"PDF documents only: SHA-256 of the immutable PDF bytes."},"alt":{"type":["string","null"]},"filename":{"type":["string","null"]},"width":{"type":["integer","null"]},"height":{"type":["integer","null"]},"duration":{"type":["number","null"]},"created_by":{"$ref":"#/components/schemas/Actor"},"created_at":{"type":"string","format":"date-time"}},"required":["id","type","kind","size"],"additionalProperties":false},"MediaProbe":{"type":"object","properties":{"width":{"type":["integer","null"],"minimum":1,"description":"Display width in pixels, or null when it can't be established from the supported headers."},"height":{"type":["integer","null"],"minimum":1,"description":"Display height in pixels, or null when it can't be established from the supported headers."},"duration":{"type":["number","null"],"exclusiveMinimum":0,"description":"MP4/MOV duration in seconds from mvhd, or null for images or when unreadable."}},"required":["width","height","duration"],"additionalProperties":false},"Event":{"type":"object","properties":{"id":{"type":"integer"},"actor":{"$ref":"#/components/schemas/Actor"},"action":{"type":"string"},"target":{"type":"object","properties":{"type":{"type":["string","null"]},"id":{"type":["string","null"]}},"required":[],"additionalProperties":false},"data":{"type":["object","null"]},"at":{"type":"string","format":"date-time"}},"required":["id","action","at"],"additionalProperties":false},"Approval":{"type":"object","properties":{"id":{"type":"string","pattern":"^apr_[a-z0-9]{16}$"},"draft_id":{"type":"string","pattern":"^drf_[a-z0-9]{16}$"},"draft_rev":{"type":"integer"},"mode":{"type":"string","enum":["approve","schedule","post_now"],"description":"approve: the decision only. schedule: post at post_at (or, with by_hand \"first\", as soon as post 1 is live). post_now: post right away (the only way TikTok posts go out)."},"snapshot_hash":{"type":"string"},"approved_by":{"type":"string","pattern":"^usr_[a-z0-9]{16}$"},"passkey_id":{"type":"string"},"approved_at":{"type":"string","format":"date-time"},"voided_at":{"type":["string","null"],"format":"date-time"},"void_reason":{"type":["string","null"]},"source":{"type":"string","enum":["passkey","local"],"description":"local: approved in the local desk before passkeys and moved in by an import. It records the decision only; approving again with a passkey is needed to schedule or post."}},"required":["id","draft_id","snapshot_hash"],"additionalProperties":false},"PostCheck":{"type":"object","properties":{"draft_id":{"type":"string","pattern":"^drf_[a-z0-9]{16}$"},"mode":{"type":"string","enum":["approve","schedule","post_now"],"description":"approve: the decision only. schedule: post at post_at (or, with by_hand \"first\", as soon as post 1 is live). post_now: post right away (the only way TikTok posts go out)."},"ok":{"type":"boolean"},"problems":{"type":"array","items":{"$ref":"#/components/schemas/Problem"}},"snapshot_hash":{"type":"string"},"snapshot":{"$ref":"#/components/schemas/Snapshot"},"owner_choices":{"$ref":"#/components/schemas/TikTokOwnerChoices"}},"required":["draft_id","mode","ok","problems","snapshot_hash"],"additionalProperties":false,"description":"One draft's check, as POST …/drafts/{id}/check answers it in that mode."},"Post":{"type":"object","properties":{"ok":{"type":"boolean","description":"Every check passed and approval was requested."},"approval_url":{"type":["string","null"],"description":"Give the owner this link: they approve the plan there with their passkey. null when a check failed."},"items":{"type":"array","items":{"type":"object","properties":{"draft_id":{"type":"string"},"rev":{"type":"integer"},"mode":{"type":"string","enum":["approve","schedule","post_now"],"description":"approve: the decision only. schedule: post at post_at (or, with by_hand \"first\", as soon as post 1 is live). post_now: post right away (the only way TikTok posts go out)."},"snapshot_hash":{"type":"string"}},"required":[],"additionalProperties":false},"description":"What was requested, as POST …/approval-requests answers it. Empty when a check failed."},"note":{"type":["string","null"]},"message":{"type":"string","description":"One plain sentence: what happened and what to do next."},"drafts":{"type":"array","items":{"$ref":"#/components/schemas/Draft"},"description":"Each draft in the order sent, as GET …/drafts/{id} shows it now."},"checks":{"type":"array","items":{"$ref":"#/components/schemas/PostCheck"},"description":"Each draft's check, in the same order."}},"required":["ok","approval_url","items","message","drafts","checks"],"additionalProperties":false},"PostDraft":{"type":"object","properties":{"draft_id":{"type":"string","pattern":"^drf_[a-z0-9]{16}$","description":"A draft an earlier call made (drafts[].id in its answer), to fix it and ask again. Send its rev and only the top-level fields that change; a body is always the complete new body."},"rev":{"type":"integer","description":"With draft_id: the rev you read. A stale rev is refused (rev_conflict)."},"kind":{"type":"string","enum":["x_post","x_thread","linkedin_post","tiktok_photo","tiktok_video"],"description":"A new draft's kind. With draft_id, only x_post and x_thread change into each other, with the body in the new shape."},"body":{"type":"object","description":"Shaped by kind, as POST …/drafts takes it. With draft_id, the complete new body.","oneOf":[{"$ref":"#/components/schemas/XPostBody"},{"$ref":"#/components/schemas/XThreadBody"},{"$ref":"#/components/schemas/LinkedInPostBody"},{"$ref":"#/components/schemas/TikTokPhotoBody"},{"$ref":"#/components/schemas/TikTokVideoBody"}]},"connection_id":{"type":["string","null"],"description":"The account it posts as: an active connection's id from GET …/connections, for the kind's platform."},"title":{"type":["string","null"],"maxLength":200,"description":"A title for the desk. Never posted."},"labels":{"$ref":"#/components/schemas/DraftLabels"},"post_at":{"type":["string","null"],"anyOf":[{"type":["string","null"],"format":"date-time"},{"type":"string","const":"next_slot"}],"description":"An exact RFC 3339 time with UTC offset, null, or \"next_slot\" (new drafts only). next_slot needs connection_id and a weekly plan, and is not available for TikTok (post_now only); creation atomically takes the next open time and returns an exact post_at. No approval or job is created."},"meta":{"$ref":"#/components/schemas/DraftMeta"},"mode":{"type":"string","enum":["approve","schedule","post_now"],"description":"What the owner is asked to approve: approve (the decision only; nothing is posted), schedule (post at post_at) or post_now (post as soon as it's approved). Left out: what the owner's approval page does with the draft: post_now with no post_at, schedule with one (or with by_hand \"first\"), approve with by_hand \"all\" (TikTok: post_now)."}},"required":[],"additionalProperties":false,"description":"A new draft (kind and body, as POST …/drafts takes them), or a draft an earlier call made (draft_id and rev, with any changes as PATCH …/drafts/{id} takes them)."},"Job":{"type":"object","properties":{"id":{"type":"string"},"draft_id":{"type":"string"},"platform":{"type":"string","enum":["x","linkedin","tiktok"]},"connection_id":{"type":["string","null"]},"approval_id":{"type":["string","null"]},"state":{"type":"string","enum":["waiting","scheduled","held","posting","processing","posted","inbox","failed","canceled"],"description":"held: X credits exhausted or the connection needs a new login; retains approval and posted ids, consumes no retry budget, owner retries after top-up or reconnect. processing: LinkedIn is processing an uploaded video or document, or TikTok is working on the post. inbox: TikTok put it in the creator's TikTok inbox as a draft (finished here, not published: they publish it in TikTok)."},"run_at":{"type":["string","null"],"format":"date-time"},"wait_until":{"type":["string","null"],"format":"date-time"},"result":{"type":["object","null"],"properties":{"ids":{"type":"array","items":{"type":"string"}},"urls":{"type":"array","items":{"type":"string"}},"check":{"type":["object","null"],"description":"The live read-back: { at, ok, issues } or { at, error }. LinkedIn: { available: false, error_code: linkedin_readback_unavailable, error }; no post read-back is attempted with the app's write-only member grant."},"first_by_hand":{"type":"boolean"},"by_hand":{"type":"boolean","description":"The owner posted it themselves; this records its link."},"tiktok":{"$ref":"#/components/schemas/TikTokAttempt"}},"required":[],"additionalProperties":false},"take_down":{"type":["object","null"],"properties":{"complete":{"type":"boolean","description":"Every published id was deleted or already absent. The job stays posted as a historical record; its original result and approval are retained."},"requested_at":{"type":"string","format":"date-time"},"requested_by":{"type":"string"},"passkey_id":{"type":"string"},"posts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","enum":["deleted","already_deleted","failed"]},"at":{"type":"string","format":"date-time"},"error":{"type":"string"}},"required":["id","status","at"],"additionalProperties":false}}},"required":["complete","posts"],"additionalProperties":false},"repost":{"type":["object","null"],"properties":{"state":{"type":"string","enum":["scheduled","posting","posted","canceled","failed"]},"after_hours":{"type":"number"},"run_at":{"type":["string","null"],"format":"date-time"},"posted_at":{"type":["string","null"],"format":"date-time"},"error":{"type":["string","null"]}},"required":["state","after_hours"],"additionalProperties":false,"description":"The approved delayed repost of an X post or thread opening. Separate from the original posting state and result. Failures never resend automatically."},"error":{"type":["string","null"]},"error_code":{"type":["string","null"],"description":"x_credits_exhausted when held for Flaghand's prepaid X credits; connection_login_expired when held until the owner reconnects in Settings and retries; workspace_inactive when the plan wasn't active; duplicate_post when X or LinkedIn refused it as a repeat of a recent post (failed: change the text and approve it again)."},"attempts":{"type":"integer"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"posted_at":{"type":["string","null"],"format":"date-time"}},"required":["id","state"],"additionalProperties":false},"PostMetrics":{"type":"object","properties":{"id":{"type":"integer"},"recorded_at":{"type":"string","format":"date-time"},"recorded_by":{"$ref":"#/components/schemas/Actor"},"views":{"type":["integer","null"]},"likes":{"type":["integer","null"]},"comments":{"type":["integer","null"]},"shares":{"type":["integer","null"]},"saves":{"type":["integer","null"]},"average_watch_time_sec":{"type":["number","null"]},"completion_rate":{"type":["number","null"],"description":"0 to 100."},"is_winning_post":{"type":["boolean","null"]},"notes":{"type":["string","null"]}},"required":["id","recorded_at"],"additionalProperties":false},"PostMetricsReport":{"type":"object","properties":{"latest":{"type":["object","null"],"properties":{"id":{"type":"integer"},"recorded_at":{"type":"string","format":"date-time"},"recorded_by":{"$ref":"#/components/schemas/Actor"},"views":{"type":["integer","null"]},"likes":{"type":["integer","null"]},"comments":{"type":["integer","null"]},"shares":{"type":["integer","null"]},"saves":{"type":["integer","null"]},"average_watch_time_sec":{"type":["number","null"]},"completion_rate":{"type":["number","null"],"description":"0 to 100."},"is_winning_post":{"type":["boolean","null"]},"notes":{"type":["string","null"]}},"required":["id","recorded_at"],"additionalProperties":false},"history":{"type":"array","items":{"$ref":"#/components/schemas/PostMetrics"}},"rates":{"type":"object","properties":{"engagement":{"type":["number","null"]},"likes":{"type":["number","null"]},"comments":{"type":["number","null"]},"shares":{"type":["number","null"]},"saves":{"type":["number","null"]}},"required":[],"additionalProperties":false,"description":"Percent of views, from the latest record."},"x":{"type":["object","null"],"properties":{"status":{"type":"string","enum":["measured","not_measured"],"description":"not_measured: none of its posts has numbers. Missing numbers are null, never zeros."},"note":{"type":"string","description":"One plain sentence: which posts have numbers and why the others don't."},"learning":{"type":"boolean","description":"The account learns from its posts (Best times), whose hourly reads these numbers come from."},"reading":{"type":"boolean","description":"The account is still connected, so its posts under 8 days old are read again every hour."},"read_at":{"type":["string","null"],"format":"date-time","description":"The account's last hourly read."},"posts":{"type":"array","items":{"type":"object","properties":{"position":{"type":"integer","description":"1 is the post itself; a thread's replies follow in order."},"id":{"type":["string","null"]},"url":{"type":["string","null"]},"measured":{"type":"boolean"},"why":{"type":["string","null"],"enum":["reply","not_read_yet","not_kept","not_learning",null],"description":"Why it has no numbers. reply: the account's reads leave replies out (a thread's posts after the first, or a reply to another post). not_read_yet: it went out after the last hourly read. not_kept: it isn't in the account's history (deleted on X, or older than what learning read back). not_learning: the account doesn't learn from its posts."},"views":{"type":["integer","null"],"minimum":0},"likes":{"type":["integer","null"],"minimum":0},"reposts":{"type":["integer","null"],"minimum":0},"replies":{"type":["integer","null"],"minimum":0},"quotes":{"type":["integer","null"],"minimum":0},"bookmarks":{"type":["integer","null"],"minimum":0},"engagements":{"type":["integer","null"],"minimum":0,"description":"likes + reposts + replies + quotes + bookmarks."},"read_at":{"type":["string","null"],"format":"date-time","description":"When X gave these numbers."},"final":{"type":"boolean","description":"These won't change: past 8 days and read for the last time, or taken down."}},"required":["position","measured","why"],"additionalProperties":false}}},"required":["status","note","posts"],"additionalProperties":false,"description":"A posted X job's numbers from X, as listJobResults gives them. null for other jobs."}},"required":["history"],"additionalProperties":false},"XResults":{"type":"object","properties":{"status":{"type":"string","enum":["measured","not_measured"],"description":"not_measured: none of its posts has numbers. Missing numbers are null, never zeros."},"note":{"type":"string","description":"One plain sentence: which posts have numbers and why the others don't."},"learning":{"type":"boolean","description":"The account learns from its posts (Best times), whose hourly reads these numbers come from."},"reading":{"type":"boolean","description":"The account is still connected, so its posts under 8 days old are read again every hour."},"read_at":{"type":["string","null"],"format":"date-time","description":"The account's last hourly read."},"posts":{"type":"array","items":{"type":"object","properties":{"position":{"type":"integer","description":"1 is the post itself; a thread's replies follow in order."},"id":{"type":["string","null"]},"url":{"type":["string","null"]},"measured":{"type":"boolean"},"why":{"type":["string","null"],"enum":["reply","not_read_yet","not_kept","not_learning",null],"description":"Why it has no numbers. reply: the account's reads leave replies out (a thread's posts after the first, or a reply to another post). not_read_yet: it went out after the last hourly read. not_kept: it isn't in the account's history (deleted on X, or older than what learning read back). not_learning: the account doesn't learn from its posts."},"views":{"type":["integer","null"],"minimum":0},"likes":{"type":["integer","null"],"minimum":0},"reposts":{"type":["integer","null"],"minimum":0},"replies":{"type":["integer","null"],"minimum":0},"quotes":{"type":["integer","null"],"minimum":0},"bookmarks":{"type":["integer","null"],"minimum":0},"engagements":{"type":["integer","null"],"minimum":0,"description":"likes + reposts + replies + quotes + bookmarks."},"read_at":{"type":["string","null"],"format":"date-time","description":"When X gave these numbers."},"final":{"type":"boolean","description":"These won't change: past 8 days and read for the last time, or taken down."}},"required":["position","measured","why"],"additionalProperties":false}}},"required":["status","note","posts"],"additionalProperties":false},"JobResults":{"type":"object","properties":{"job_id":{"type":"string"},"draft_id":{"type":"string"},"connection_id":{"type":["string","null"]},"posted_at":{"type":["string","null"],"format":"date-time"},"urls":{"type":"array","items":{"type":"string"}},"text":{"type":["string","null"],"description":"Post 1's text as approved. null for a post its owner made by hand."},"x":{"$ref":"#/components/schemas/XResults"}},"required":["job_id","draft_id","x"],"additionalProperties":false},"TopPost":{"type":"object","properties":{"job_id":{"type":"string"},"draft_id":{"type":"string"},"connection_id":{"type":["string","null"]},"handle":{"type":["string","null"],"description":"The X account it went out on, without the @."},"posted_at":{"type":["string","null"],"format":"date-time"},"url":{"type":["string","null"],"description":"Post 1's link."},"text":{"type":["string","null"],"description":"Post 1's text as approved. null for a post its owner made by hand."},"post_count":{"type":"integer","description":"How many posts it went out as: 1, or a thread's length. The numbers are post 1's."},"measured":{"type":"boolean"},"why":{"type":["string","null"],"enum":["reply","not_read_yet","not_kept","not_learning",null],"description":"Why it has no numbers. reply: the account's reads leave replies out (a thread's posts after the first, or a reply to another post). not_read_yet: it went out after the last hourly read. not_kept: it isn't in the account's history (deleted on X, or older than what learning read back). not_learning: the account doesn't learn from its posts."},"views":{"type":["integer","null"],"minimum":0},"likes":{"type":["integer","null"],"minimum":0},"reposts":{"type":["integer","null"],"minimum":0},"replies":{"type":["integer","null"],"minimum":0},"quotes":{"type":["integer","null"],"minimum":0},"bookmarks":{"type":["integer","null"],"minimum":0},"engagements":{"type":["integer","null"],"minimum":0,"description":"likes + reposts + replies + quotes + bookmarks."},"read_at":{"type":["string","null"],"format":"date-time","description":"When X gave these numbers."},"final":{"type":"boolean","description":"These won't change: past 8 days and read for the last time, or taken down."}},"required":["job_id","draft_id","posted_at","post_count","measured","why"],"additionalProperties":false},"TopPosts":{"type":"object","properties":{"by":{"type":"string","enum":["views","likes","replies"]},"since":{"type":"string","format":"date-time","description":"The window's start: posts that went out at or after it."},"posts":{"type":"array","items":{"$ref":"#/components/schemas/TopPost"}}},"required":["by","since","posts"],"additionalProperties":false},"TextAnalysis":{"type":"object","properties":{"platform":{"type":"string","enum":["x","linkedin"]},"text":{"type":"string","description":"The input, unchanged. URL offsets refer to this text."},"plain_text":{"type":"string","description":"X: unchanged input. LinkedIn: @[Name] becomes Name, as copied text does. Counts refer to this string."},"counts":{"type":"object","properties":{"utf16":{"type":"integer","description":"UTF-16 code units in plain_text (JavaScript string.length). LinkedIn's composer uses this count."},"code_points":{"type":"integer","description":"Unicode code points in plain_text, without normalization. This is not the UTF-16 or X weighted count."},"graphemes":{"type":"integer","description":"Grapheme clusters in plain_text using Intl.Segmenter('en'); not a platform posting limit."}},"required":["utf16","code_points","graphemes"],"additionalProperties":false},"urls":{"type":"array","items":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"description":"Inclusive UTF-16 offset into the response's text."},"end":{"type":"integer","minimum":0,"description":"Exclusive UTF-16 offset into the response's text."},"url":{"type":"string"},"explicit":{"type":"boolean","description":"The recognized URL starts with http:// or https://."},"display_url":{"type":"string","description":"The renderer's link label, without a trailing ellipsis. LinkedIn's simulated lnkd.in labels are placeholders, not assigned short links."},"display_url_truncated":{"type":"boolean","description":"X adds an ellipsis after display_url."},"display_url_is_placeholder":{"type":"boolean","description":"LinkedIn would assign a short link at posting time; this label is only the renderer's stable stand-in. Never use it as a destination."}},"required":["start","end","url","explicit","display_url","display_url_truncated","display_url_is_placeholder"],"additionalProperties":false}},"mentions":{"type":"array","items":{"type":"string"},"description":"In first-occurrence order without repeats: X handles without @, or LinkedIn draft @[Tag] names. Syntax only; no profile lookup or proof a mention can be posted."},"x":{"type":"object","properties":{"weighted_length":{"type":"integer","description":"The native X renderer's NFC-normalized count: recognized links cost 23 and emoji 2."},"standard_limit":{"type":"integer","const":280},"over_standard_limit":{"type":"boolean","description":"Over the standard 280 weighted characters; not a verdict about a Premium account's posting access."},"timeline_text":{"type":"string","description":"The native renderer's text-only timeline cut, before link-card URL removal. Not a measured line-wrap or image preview."},"timeline_truncated":{"type":"boolean"}},"required":["weighted_length","standard_limit","over_standard_limit","timeline_text","timeline_truncated"],"additionalProperties":false,"description":"Present only for X."},"linkedin":{"type":"object","properties":{"limit_utf16":{"type":"integer","const":3000},"over_limit_utf16":{"type":"boolean","description":"plain_text exceeds LinkedIn's 3000 UTF-16-unit composer limit. A saved draft's check counts the same way."}},"required":["limit_utf16","over_limit_utf16"],"additionalProperties":false,"description":"Present only for LinkedIn. A visual 'more' cut depends on DOM layout and is deliberately not predicted here."}},"required":["platform","text","plain_text","counts","urls","mentions"],"additionalProperties":false},"AnalyzeTextInput":{"type":"object","properties":{"platform":{"type":"string","enum":["x","linkedin"]},"text":{"type":"string"}},"required":["platform","text"],"additionalProperties":false},"XThreadSplit":{"type":"object","properties":{"text":{"type":"string","description":"The input with CRLF and CR normalized to LF. All post, link and pin offsets refer to this string."},"numbered":{"type":"boolean"},"pins":{"type":"array","items":{"type":"integer","minimum":0,"description":"A split's UTF-16 offset into LF-normalized text, never inside a grapheme or recognized URL."},"default":[],"description":"Moved splits to preserve. At the start or end of the trimmed body a pin has no effect, as in the thread maker. No post-count ceiling is imposed by this utility."},"posts":{"type":"array","items":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"description":"Inclusive UTF-16 source offset, before any numbering suffix."},"end":{"type":"integer","minimum":0,"description":"Exclusive UTF-16 source offset, before any numbering suffix."},"text":{"type":"string","description":"The post's exact text, including its i/n suffix when numbered."},"weight":{"type":"integer","description":"X's weighted length, including numbering."},"brk":{"type":"string","enum":["end","force","pin","para","line","sent","clause","word","char"],"description":"Why this post ends here: end of text, a --- line, a moved split, paragraph, line, sentence, clause, word or grapheme."}},"required":["start","end","text","weight","brk"],"additionalProperties":false}},"links":{"type":"array","items":{"type":"object","properties":{"start":{"type":"integer","minimum":0,"description":"Inclusive UTF-16 offset into the response's text."},"end":{"type":"integer","minimum":0,"description":"Exclusive UTF-16 offset into the response's text."},"url":{"type":"string"}},"required":["start","end","url"],"additionalProperties":false}},"moved":{"type":"boolean","description":"Present only when move was requested: true if a boundary move was available and applied, false if the split and pins stayed unchanged."}},"required":["text","numbered","pins","posts","links"],"additionalProperties":false},"SplitXThreadInput":{"type":"object","properties":{"text":{"type":"string"},"numbered":{"type":"boolean","default":false},"pins":{"type":"array","items":{"type":"integer","minimum":0,"description":"A split's UTF-16 offset into LF-normalized text, never inside a grapheme or recognized URL."},"default":[],"description":"Moved splits to preserve. At the start or end of the trimmed body a pin has no effect, as in the thread maker. No post-count ceiling is imposed by this utility."},"remap":{"type":"object","properties":{"before":{"type":"string","description":"The previous text these pins belong to. Both before and text are LF-normalized before remapping."}},"required":["before"],"additionalProperties":false},"move":{"type":"object","properties":{"post_index":{"type":"integer","minimum":0,"description":"Zero-based post whose following boundary moves. An absent boundary (including after the last post) cannot move."},"direction":{"type":"string","enum":["earlier","later"]}},"required":["post_index","direction"],"additionalProperties":false}},"required":["text"],"additionalProperties":false},"WebhookEndpoint":{"type":"object","properties":{"id":{"type":"string","pattern":"^whk_[a-z0-9]{16}$"},"endpoint_url":{"type":"string"},"actions":{"type":"array","items":{"type":"string"}},"created_by":{"type":["string","null"],"description":"The owner who registered this endpoint or created its token."},"token_id":{"type":["string","null"],"description":"A registering token; revoking it also removes its endpoints."},"created_at":{"type":"string","format":"date-time"},"removed_at":{"type":["string","null"],"format":"date-time"}},"required":["id","endpoint_url","actions","created_at","removed_at"],"additionalProperties":false},"WebhookDelivery":{"type":"object","properties":{"id":{"type":"string"},"endpoint_id":{"type":"string","pattern":"^whk_[a-z0-9]{16}$"},"event_id":{"type":"integer"},"state":{"type":"string","enum":["pending","delivered","canceled"]},"attempts":{"type":"integer"},"next_attempt_at":{"type":["string","null"],"format":"date-time"},"last_attempt_at":{"type":["string","null"],"format":"date-time"},"last_status":{"type":["integer","null"]},"last_error":{"type":["string","null"]},"delivered_at":{"type":["string","null"],"format":"date-time"}},"required":["id","endpoint_id","event_id","state","attempts"],"additionalProperties":false},"PushDevice":{"type":"object","properties":{"id":{"type":"string","pattern":"^psh_[a-z0-9]{16}$"},"label":{"type":"string"},"subscription_hash":{"type":"string","description":"SHA-256 of the browser subscription's endpoint and encryption keys, for this device's Settings state. Never the endpoint or keys themselves."},"created_at":{"type":"string","format":"date-time"}},"required":["id","label","subscription_hash","created_at"],"additionalProperties":false},"Feed":{"type":"object","properties":{"id":{"type":"string","pattern":"^fed_[a-z0-9]{16}$"},"feed_url":{"type":"string"},"title":{"type":["string","null"]},"created_at":{"type":"string","format":"date-time"},"initialized_at":{"type":["string","null"],"format":"date-time"},"last_checked_at":{"type":["string","null"],"format":"date-time"},"last_success_at":{"type":["string","null"],"format":"date-time"},"last_error":{"type":["string","null"],"description":"http_NNN, feed_unreadable or invocation_budget; the next hourly read retries without changing the baseline."},"next_poll_at":{"type":"string","format":"date-time"}},"required":["id","feed_url","title","created_at","initialized_at","last_checked_at","last_success_at","last_error","next_poll_at"],"additionalProperties":false},"Lookup":{"type":"object","additionalProperties":true,"description":"A mention check (x:<handle>, li:<tag>), Community (community:<id>), or a live X post a draft quotes or replies to (post:<id>): { key, platform, status: ok | missing | error | unverified, checked, source, … }. Image URLs are relative to the API's origin.","required":["key","status"]},"SlideshowStyles":{"type":"object","properties":{"version":{"type":"integer","description":"The slideshow schema version this server reads and writes."},"renderer":{"type":"string","description":"The renderer's version. It's part of every document hash, so a new one means rendering again."},"aspects":{"type":"object","description":"Each aspect's size in pixels: { \"9:16\": { width, height }, … }.","additionalProperties":{"type":"object","properties":{"width":{"type":"integer"},"height":{"type":"integer"}},"required":["width","height"],"additionalProperties":false}},"limits":{"type":"object","properties":{"slides":{"type":"integer"},"texts_per_slide":{"type":"integer"},"text_length":{"type":"integer"}},"required":[],"additionalProperties":false},"fonts":{"type":"array","items":{"type":"object","properties":{"family":{"type":"string","description":"What a text's font field takes."},"name":{"type":"string"},"weights":{"type":"array","items":{"type":"integer"}},"default_weight":{"type":"integer"},"italic_weights":{"type":"array","items":{"type":"integer"}},"license":{"type":"string"}},"required":["family","name","weights"],"additionalProperties":false}},"emoji":{"type":"object","properties":{"name":{"type":"string"},"license":{"type":"string"}},"required":[],"additionalProperties":false},"presets":{"type":"object","description":"Each text style (a text's preset) at the default size: { color, stroke, shadow, box }.","additionalProperties":{"type":"object"}},"preset_aliases":{"type":"object","description":"ss-content's style names, and the preset each means.","additionalProperties":{"type":"string"}},"text_defaults":{"type":"object","description":"What a new text gets for the fields it leaves out."}},"required":["version","renderer","fonts","presets"],"additionalProperties":false,"description":"What slides can use: shapes, fonts and text styles. Fonts are bundled with the renderer, so the editor, agents and TikTok see the same letters."},"SlideshowDraft":{"type":"object","properties":{"id":{"type":"string","pattern":"^drf_[a-z0-9]{16}$"},"workspace_id":{"type":"string","pattern":"^ws_[a-z0-9]{16}$"},"connection_id":{"type":["string","null"]},"kind":{"type":"string","enum":["x_post","x_thread","linkedin_post","tiktok_photo","tiktok_video"]},"title":{"type":["string","null"]},"labels":{"$ref":"#/components/schemas/DraftLabels"},"body":{"type":"object","description":"Shaped by kind.","oneOf":[{"$ref":"#/components/schemas/XPostBody"},{"$ref":"#/components/schemas/XThreadBody"},{"$ref":"#/components/schemas/LinkedInPostBody"},{"$ref":"#/components/schemas/TikTokPhotoBody"},{"$ref":"#/components/schemas/TikTokVideoBody"}]},"post_at":{"type":["string","null"],"format":"date-time"},"rev":{"type":"integer","description":"Send it back with every change."},"status":{"type":"string","enum":["draft","approval_requested","changes_requested","approved"]},"request_note":{"type":["string","null"]},"requested_at":{"type":["string","null"],"format":"date-time"},"changes_requested_note":{"type":["string","null"],"description":"The owner's latest send-back note. Read it, edit the draft, check and request approval again. Retained after edits and renewed requests; earlier notes remain in draft.changes_requested events."},"changes_requested_at":{"type":["string","null"],"format":"date-time","description":"When the owner last sent this draft back."},"created_by":{"$ref":"#/components/schemas/Actor"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"archived":{"type":"boolean"},"meta":{"type":["object","null"],"properties":{"notes":{"type":["array","null"],"items":{"type":"string","maxLength":4000},"maxItems":50,"description":"Source notes for whoever reviews it."},"warnings":{"type":["array","null"],"items":{"type":"string","maxLength":2000},"maxItems":50,"description":"Things to check before posting."},"when":{"type":["string","null"],"maxLength":300,"description":"A note on timing, like \"Tuesday morning\"."},"note":{"type":["string","null"],"maxLength":2000,"description":"The owner's private note."},"original":{"type":["object","null"],"description":"The body as first written, for Reset draft."},"quote_draft_id":{"type":["string","null"],"pattern":"^drf_[a-z0-9]{16}$"}},"required":[],"additionalProperties":false,"description":"Never posted and never approved: notes for the desk."},"legacy_id":{"type":["string","null"],"description":"The local desk's id, for a draft an import moved in."},"approval":{"type":["object","null"],"properties":{"id":{"type":"string","pattern":"^apr_[a-z0-9]{16}$"},"draft_id":{"type":"string","pattern":"^drf_[a-z0-9]{16}$"},"draft_rev":{"type":"integer"},"mode":{"type":"string","enum":["approve","schedule","post_now"],"description":"approve: the decision only. schedule: post at post_at (or, with by_hand \"first\", as soon as post 1 is live). post_now: post right away (the only way TikTok posts go out)."},"snapshot_hash":{"type":"string"},"approved_by":{"type":"string","pattern":"^usr_[a-z0-9]{16}$"},"passkey_id":{"type":"string"},"approved_at":{"type":"string","format":"date-time"},"voided_at":{"type":["string","null"],"format":"date-time"},"void_reason":{"type":["string","null"]},"source":{"type":"string","enum":["passkey","local"],"description":"local: approved in the local desk before passkeys and moved in by an import. It records the decision only; approving again with a passkey is needed to schedule or post."}},"required":["id","draft_id","snapshot_hash"],"additionalProperties":false},"job":{"type":["object","null"],"properties":{"id":{"type":"string"},"draft_id":{"type":"string"},"platform":{"type":"string","enum":["x","linkedin","tiktok"]},"connection_id":{"type":["string","null"]},"approval_id":{"type":["string","null"]},"state":{"type":"string","enum":["waiting","scheduled","held","posting","processing","posted","inbox","failed","canceled"],"description":"held: X credits exhausted or the connection needs a new login; retains approval and posted ids, consumes no retry budget, owner retries after top-up or reconnect. processing: LinkedIn is processing an uploaded video or document, or TikTok is working on the post. inbox: TikTok put it in the creator's TikTok inbox as a draft (finished here, not published: they publish it in TikTok)."},"run_at":{"type":["string","null"],"format":"date-time"},"wait_until":{"type":["string","null"],"format":"date-time"},"result":{"type":["object","null"],"properties":{"ids":{"type":"array","items":{"type":"string"}},"urls":{"type":"array","items":{"type":"string"}},"check":{"type":["object","null"],"description":"The live read-back: { at, ok, issues } or { at, error }. LinkedIn: { available: false, error_code: linkedin_readback_unavailable, error }; no post read-back is attempted with the app's write-only member grant."},"first_by_hand":{"type":"boolean"},"by_hand":{"type":"boolean","description":"The owner posted it themselves; this records its link."},"tiktok":{"$ref":"#/components/schemas/TikTokAttempt"}},"required":[],"additionalProperties":false},"take_down":{"type":["object","null"],"properties":{"complete":{"type":"boolean","description":"Every published id was deleted or already absent. The job stays posted as a historical record; its original result and approval are retained."},"requested_at":{"type":"string","format":"date-time"},"requested_by":{"type":"string"},"passkey_id":{"type":"string"},"posts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"status":{"type":"string","enum":["deleted","already_deleted","failed"]},"at":{"type":"string","format":"date-time"},"error":{"type":"string"}},"required":["id","status","at"],"additionalProperties":false}}},"required":["complete","posts"],"additionalProperties":false},"repost":{"type":["object","null"],"properties":{"state":{"type":"string","enum":["scheduled","posting","posted","canceled","failed"]},"after_hours":{"type":"number"},"run_at":{"type":["string","null"],"format":"date-time"},"posted_at":{"type":["string","null"],"format":"date-time"},"error":{"type":["string","null"]}},"required":["state","after_hours"],"additionalProperties":false,"description":"The approved delayed repost of an X post or thread opening. Separate from the original posting state and result. Failures never resend automatically."},"error":{"type":["string","null"]},"error_code":{"type":["string","null"],"description":"x_credits_exhausted when held for Flaghand's prepaid X credits; connection_login_expired when held until the owner reconnects in Settings and retries; workspace_inactive when the plan wasn't active; duplicate_post when X or LinkedIn refused it as a repeat of a recent post (failed: change the text and approve it again)."},"attempts":{"type":"integer"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"posted_at":{"type":["string","null"],"format":"date-time"}},"required":["id","state"],"additionalProperties":false},"x_thread_live":{"type":["object","null"],"description":"The draft's live X prefix, independent of edits and job ids: { connection_id, ids, parts, root, first_by_hand }. ids and approved posting expectations are in thread order (a hand-first opening may differ on X). null before any thread post is live. The next approval continues after these posts; X cannot edit them."},"render":{"type":["object","null"],"properties":{"id":{"type":"string","pattern":"^rnd_[a-z0-9]{16}$"},"draft_id":{"type":"string","pattern":"^drf_[a-z0-9]{16}$"},"draft_rev":{"type":"integer","description":"The draft's rev when it was rendered."},"hash":{"type":"string","description":"The render's content hash: SHA-256 over the document's hash and every slide's PNG SHA-256, in order. A TikTok approval's snapshot holds it (content.render.hash)."},"document_hash":{"type":"string","description":"SHA-256 of the slideshow document (with the renderer's version) it drew."},"renderer":{"type":"string"},"current":{"type":"boolean","description":"It's of the slideshow as it is now. Only the current render can be approved and posted; after any change to the slides, render again."},"images":{"type":"array","items":{"type":"object","properties":{"slide_id":{"type":"string"},"media_id":{"type":"string","pattern":"^med_[a-z0-9]{16}$"},"url":{"type":"string","description":"The PNG. Fetch it with the same API token or session."},"width":{"type":"integer"},"height":{"type":"integer"},"size":{"type":"integer","description":"Bytes."},"sha256":{"type":"string","description":"SHA-256 of the PNG."}},"required":["slide_id","media_id","url","sha256"],"additionalProperties":false},"description":"One PNG per slide, in order: the photos that go to TikTok (as lossless WebP copies with the same pixels)."},"warnings":{"type":"array","items":{"type":"object","properties":{"slide_id":{"type":"string"},"text_id":{"type":"string"},"text":{"type":"string","description":"One plain sentence."}},"required":["slide_id","text"],"additionalProperties":false},"description":"Characters no bundled font has, text taller than its box, text running off the image."},"created_by":{"$ref":"#/components/schemas/Actor"},"created_at":{"type":"string","format":"date-time"}},"required":["id","hash","document_hash","current","images"],"additionalProperties":false,"description":"The latest render, current or not."},"slide_id":{"type":"string","description":"The id of the slide this call added."},"text_id":{"type":"string","description":"The id of the text this call added."}},"required":["id","kind","body","rev","status"],"additionalProperties":false,"description":"A tiktok_photo draft whose body holds a slideshow, with its latest render."},"SlideshowPdfResult":{"type":"object","properties":{"media":{"$ref":"#/components/schemas/Media"},"url":{"type":"string","description":"The PDF; fetch it with the same API token or session."},"render_hash":{"type":"string","description":"The source PNG render's content hash."},"document_hash":{"type":"string","description":"The source slideshow document's hash, including its renderer version."},"pages":{"type":"integer"},"drawn":{"type":"integer","description":"Slides drawn by this call; unchanged PNGs are reused."}},"required":["media","url","render_hash","document_hash","pages","drawn"],"additionalProperties":false},"SlideshowRenderResult":{"type":"object","properties":{"id":{"type":"string","pattern":"^rnd_[a-z0-9]{16}$"},"draft_id":{"type":"string","pattern":"^drf_[a-z0-9]{16}$"},"draft_rev":{"type":"integer","description":"The draft's rev when it was rendered."},"hash":{"type":"string","description":"The render's content hash: SHA-256 over the document's hash and every slide's PNG SHA-256, in order. A TikTok approval's snapshot holds it (content.render.hash)."},"document_hash":{"type":"string","description":"SHA-256 of the slideshow document (with the renderer's version) it drew."},"renderer":{"type":"string"},"current":{"type":"boolean","description":"It's of the slideshow as it is now. Only the current render can be approved and posted; after any change to the slides, render again."},"images":{"type":"array","items":{"type":"object","properties":{"slide_id":{"type":"string"},"media_id":{"type":"string","pattern":"^med_[a-z0-9]{16}$"},"url":{"type":"string","description":"The PNG. Fetch it with the same API token or session."},"width":{"type":"integer"},"height":{"type":"integer"},"size":{"type":"integer","description":"Bytes."},"sha256":{"type":"string","description":"SHA-256 of the PNG."}},"required":["slide_id","media_id","url","sha256"],"additionalProperties":false},"description":"One PNG per slide, in order: the photos that go to TikTok (as lossless WebP copies with the same pixels)."},"warnings":{"type":"array","items":{"type":"object","properties":{"slide_id":{"type":"string"},"text_id":{"type":"string"},"text":{"type":"string","description":"One plain sentence."}},"required":["slide_id","text"],"additionalProperties":false},"description":"Characters no bundled font has, text taller than its box, text running off the image."},"created_by":{"$ref":"#/components/schemas/Actor"},"created_at":{"type":"string","format":"date-time"},"drawn":{"type":"integer","description":"Slides drawn by this call. Slides that didn't change since an earlier render are reused, not drawn again."}},"required":["id","hash","images"],"additionalProperties":false},"Plan":{"type":"object","properties":{"id":{"type":"string","enum":["monthly","yearly","founding"]},"name":{"type":"string"},"price_id":{"type":"string"},"amount":{"type":"integer","description":"In the currency's smallest unit (cents)."},"price":{"type":"string","description":"The amount as a decimal string, like \"19.00\"."},"currency":{"type":"string"},"interval":{"type":"string","enum":["month","year"]},"trial_days":{"type":"integer"},"coupon":{"type":"string","description":"founding only."},"first_amount":{"type":"integer","description":"founding only: what the first year costs."},"first_price":{"type":"string"},"ends_at":{"type":["string","null"],"format":"date-time","description":"founding only: when the offer stops."}},"required":["id","name","amount","currency","interval"],"additionalProperties":false},"Plans":{"type":"object","properties":{"signup_open":{"type":"boolean"},"trial_days":{"type":"integer"},"livemode":{"type":"boolean"},"plans":{"type":"array","items":{"$ref":"#/components/schemas/Plan"}}},"required":["plans"],"additionalProperties":false},"Billing":{"type":"object","properties":{"state":{"type":"string","enum":["active","paused","lapsed"],"description":"active posts; paused (a payment failed and Stripe is retrying) and lapsed (no plan) never post."},"stripe_status":{"type":["string","null"],"description":"Stripe's subscription status: trialing, active, past_due, canceled, unpaid, incomplete, incomplete_expired or paused."},"plan":{"type":["string","null"],"enum":["monthly","yearly","founding"]},"amount":{"type":["integer","null"]},"currency":{"type":["string","null"]},"trial_ends_at":{"type":["string","null"],"format":"date-time"},"renews_at":{"type":["string","null"],"format":"date-time"},"ends_at":{"type":["string","null"],"format":"date-time","description":"When a canceled plan ends, or ended."},"cancel_requested_at":{"type":["string","null"],"format":"date-time"},"posts_after_end":{"type":"integer","description":"Scheduled posts after ends_at, which won't post."},"can_post":{"type":"boolean"},"comped":{"type":"boolean"},"livemode":{"type":"boolean","description":"false while billing runs in Stripe's test mode."},"trial_available":{"type":"boolean"},"card":{"type":["object","null"],"properties":{"brand":{"type":"string"},"last4":{"type":"string"}},"required":["brand","last4"],"additionalProperties":false},"message":{"type":["string","null"],"description":"Why it can't post, and where to fix it."},"invoices":{"type":"array","items":{"type":"object","properties":{"created_at":{"type":"string","format":"date-time"},"amount":{"type":"integer","description":"Paid, or still owed (status open), in cents."},"currency":{"type":"string"},"status":{"type":"string","enum":["paid","open"]},"url":{"type":["string","null"],"description":"Stripe's page for the invoice and its receipt."}},"required":["created_at","amount","currency","status"],"additionalProperties":false},"description":"Newest first; empty during a trial. Missing when Stripe couldn't be reached: the portal lists them."}},"required":["state","can_post"],"additionalProperties":false},"SetupConnection":{"type":"object","properties":{"id":{"type":"string"},"platform":{"type":"string","enum":["x","linkedin","tiktok"]},"external_id":{"type":["string","null"]},"handle":{"type":["string","null"]},"display_name":{"type":["string","null"]},"status":{"type":"string","description":"active, expired (connect it again) or removed."},"scopes":{"type":"array","items":{"type":"string"}},"expires_at":{"type":["string","null"],"format":"date-time","description":"Last usable login instant when known: the non-renewing access expiry or the later of access and refresh expiry. Null for a renewable login with no known end."},"renews":{"type":"boolean"},"connected_at":{"type":["string","null"],"format":"date-time"},"subscription_type":{"type":["string","null"],"description":"X only: subscription_type from the authenticated user object, not the editable preview profile. Basic, Premium and PremiumPlus allow a single longer post; null means unknown. Long-post checks refresh this before approval."},"creator":{"type":["object","null"],"properties":{"username":{"type":["string","null"]},"nickname":{"type":["string","null"]},"avatar_url":{"type":["string","null"]},"privacy_level_options":{"type":"array","items":{"type":"string"}},"comment_disabled":{"type":["boolean","null"]},"duet_disabled":{"type":["boolean","null"]},"stitch_disabled":{"type":["boolean","null"]},"max_video_post_duration_sec":{"type":["integer","null"]},"can_post":{"type":["boolean","null"]},"fetched_at":{"type":"string","format":"date-time"}},"required":[],"additionalProperties":false,"description":"What TikTok says about the account (creator info), as of fetched_at."},"audit_posture":{"type":["string","null"],"enum":["audited","unaudited","unknown"],"description":"TikTok only. unaudited: posting directly works only for a private account, seen only by its owner. unknown: no posting directly. Drafts sent to the inbox work either way."},"profile":{"$ref":"#/components/schemas/Profile"},"missing_scopes":{"type":["array","null"],"items":{"type":"string"},"description":"Permissions not granted from the existing connection's OAuth request; null when its saved grant is unknown. Does not impose a new permission requirement."},"configured":{"type":"boolean","description":"The server has this platform's app credentials for renewing or reconnecting the login."}},"required":["id","platform","status","scopes","expires_at","renews","missing_scopes","configured"],"additionalProperties":false},"SetupIssue":{"type":"object","properties":{"code":{"type":"string"},"severity":{"type":"string","enum":["error","warning"],"description":"error: a saved posting check fails. warning: an upcoming or conditional problem, agent capability or notification failure; not a new posting gate."},"resource":{"type":"string","enum":["workspace","connection","token","webhook"]},"id":{"type":"string"},"message":{"type":"string"},"next_step":{"type":"string"},"url":{"type":"string"}},"required":["code","severity","resource","id","message","next_step","url"],"additionalProperties":false},"WorkspaceSetup":{"type":"object","properties":{"workspace_id":{"type":"string"},"checked_at":{"type":"string","format":"date-time"},"ok":{"type":"boolean","description":"No reported errors or warnings. Never approval, proof of platform availability or a guarantee a draft can post."},"connections":{"type":"array","items":{"$ref":"#/components/schemas/SetupConnection"}},"billing":{"type":"object","properties":{"state":{"type":"string","enum":["active","paused","lapsed"]},"stripe_status":{"type":["string","null"]},"can_post":{"type":"boolean"},"comped":{"type":"boolean"},"trial_ends_at":{"type":["string","null"],"format":"date-time"},"renews_at":{"type":["string","null"],"format":"date-time"},"ends_at":{"type":["string","null"],"format":"date-time"}},"required":["state","stripe_status","can_post","comped","trial_ends_at","renews_at","ends_at"],"additionalProperties":false},"token":{"type":["object","null"],"properties":{"id":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"}},"missing_drafting_scopes":{"type":"array","items":{"type":"string","description":"Missing from the existing first-run scopes; not all work needs every scope."}},"expires_at":{"type":["string","null"],"format":"date-time"}},"required":["id","scopes","missing_drafting_scopes","expires_at"],"additionalProperties":false,"description":"The calling token only; null for a signed-in member. No credential value or hash."},"webhooks":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"pending_deliveries":{"type":"integer"},"failed_deliveries":{"type":"integer"},"last_attempt_at":{"type":["string","null"],"format":"date-time"}},"required":["id","pending_deliveries","failed_deliveries","last_attempt_at"],"additionalProperties":false,"description":"Active receivers, without private endpoint URLs, secrets or event payloads. A missing receiver is not a problem."}},"x_credits_exhausted":{"type":"boolean"},"issues":{"type":"array","items":{"$ref":"#/components/schemas/SetupIssue"}}},"required":["workspace_id","checked_at","ok","connections","billing","token","webhooks","x_credits_exhausted","issues"],"additionalProperties":false}},"securitySchemes":{"token":{"type":"http","scheme":"bearer","description":"A workspace API token (tok_…). Scopes: read, drafts, media, request-approval, cancel, connections, billing, webhooks, calendar. No scope can approve, schedule, post or connect accounts."},"session":{"type":"apiKey","in":"cookie","name":"__Host-session","description":"Set by passkey sign-in. Requests that change something must come from the app's origin."}}}}