{"openapi":"3.0.3","info":{"title":"AdaptlyPost Public API","version":"1.0.0","description":"REST API for creating, scheduling, and managing social media posts across Facebook, Instagram, Threads, TikTok, X (Twitter), Bluesky, Mastodon, LinkedIn, Pinterest, YouTube, and Google Business Profile. Register webhooks to receive `post.published`, `post.partially_failed`, `post.failed`, `post.scheduled`, `account.unauthorized`, `image.completed` and `image.failed` events. Read post performance through the `/analytics/*` endpoints: views, likes, comments, shares, followers and engagement rate per workspace, per platform, per day and per post, with a comparison against the previous window of the same length. Connecting an account imports its posts from the last 180 days, and analytics are kept from then on for as long as the account stays connected, so any date range in that history can be queried. A post keeps refreshing for 180 days after it is published and then holds its last captured numbers. Refreshes run on a schedule; `POST /analytics/sync` asks for one now. Webhook deliveries are signed: the `x-adaptly-signature` header contains `sha256=HMAC_SHA256(secret, \"{x-adaptly-timestamp}.{rawBody}\")`. Every response carries the RFC 9331 rate-limit fields so a client can pace itself instead of discovering the limit through a 429: `RateLimit-Policy`, `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset`, the seconds until the window rolls over. A 429 adds `Retry-After` with the same number of seconds. Limits are counted per API key."},"servers":[{"url":"https://post.adaptlypost.com/post/api/v1"}],"security":[{"bearerAuth":[]}],"paths":{"/me":{"get":{"operationId":"getMe","summary":"Describe the calling key","tags":["Account"],"responses":{"200":{"description":"The calling key, its role and its permissions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Me"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"description":"Open to every valid key, whatever its role, so an agent can find out what it may do before it tries.","parameters":[{"name":"X-Workspace-Id","in":"header","required":false,"schema":{"type":"string"},"description":"The workspace to act in, for OAuth sign-ins that reach several workspaces. Take the id from `GET /workspaces`. Without it the call lands in the default workspace. A workspace the caller cannot reach answers 403 with `code: workspace_access_denied`. An API key belongs to one workspace: send its own id or leave the header out"}]}},"/workspaces":{"get":{"operationId":"listWorkspaces","summary":"List the workspaces the caller can act in","description":"An OAuth sign-in reaches every workspace its member belongs to, across organizations; an API key reaches only its own. Send a returned `id` in the `X-Workspace-Id` header to act in that workspace. `current` marks the workspace this call landed in. Open to every valid key, whatever its role, so an agent can find out what it may do before it tries.","tags":["Account"],"responses":{"200":{"description":"The reachable workspaces","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkspaceList"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"X-Workspace-Id","in":"header","required":false,"schema":{"type":"string"},"description":"The workspace to act in, for OAuth sign-ins that reach several workspaces. Take the id from `GET /workspaces`. Without it the call lands in the default workspace. A workspace the caller cannot reach answers 403 with `code: workspace_access_denied`. An API key belongs to one workspace: send its own id or leave the header out"}]}},"/social-posts":{"post":{"operationId":"createPost","summary":"Create a post (immediate, scheduled, or draft)","description":"A draft (`saveAsDraft: true`) needs only `posts.draft`. A post with a future `scheduledAt` also needs `posts.schedule`, and a post that publishes now also needs `posts.publish`; without it the call answers 403 `permission_denied`. Requires the `posts.draft` permission.","tags":["Posts"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSocialPostRequest"}}}},"responses":{"201":{"description":"Post created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePostResponse"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key's role does not hold `posts.draft`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PermissionDeniedError"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"X-Workspace-Id","in":"header","required":false,"schema":{"type":"string"},"description":"The workspace to act in, for OAuth sign-ins that reach several workspaces. Take the id from `GET /workspaces`. Without it the call lands in the default workspace. A workspace the caller cannot reach answers 403 with `code: workspace_access_denied`. An API key belongs to one workspace: send its own id or leave the header out"}]},"get":{"operationId":"listPosts","summary":"List posts","tags":["Posts"],"parameters":[{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":20}},{"name":"offset","in":"query","schema":{"type":"integer","minimum":0,"default":0}},{"name":"sortOrder","in":"query","schema":{"type":"string","enum":["NEWEST","OLDEST"]}},{"name":"statuses","in":"query","schema":{"type":"array","items":{"type":"string","enum":["DRAFT","SCHEDULED","PENDING","PUBLISHING","COMPLETED","PARTIAL_FAILURE","FAILED"]}},"explode":true,"description":"Filter by status"},{"name":"platforms","in":"query","schema":{"type":"array","items":{"type":"string","enum":["FACEBOOK","INSTAGRAM","THREADS","TIKTOK","TWITTER","BLUESKY","LINKEDIN","PINTEREST","YOUTUBE","MASTODON","TELEGRAM","GOOGLE_BUSINESS"]}},"explode":true,"description":"Posts targeting any of these platforms"},{"name":"startDate","in":"query","schema":{"type":"string","format":"date-time"}},{"name":"endDate","in":"query","schema":{"type":"string","format":"date-time"}},{"name":"X-Workspace-Id","in":"header","required":false,"schema":{"type":"string"},"description":"The workspace to act in, for OAuth sign-ins that reach several workspaces. Take the id from `GET /workspaces`. Without it the call lands in the default workspace. A workspace the caller cannot reach answers 403 with `code: workspace_access_denied`. An API key belongs to one workspace: send its own id or leave the header out"}],"responses":{"200":{"description":"Paginated posts","content":{"application/json":{"schema":{"type":"object","properties":{"posts":{"type":"array","items":{"$ref":"#/components/schemas/Post"}},"total":{"type":"integer"},"hasMore":{"type":"boolean"}}}}}},"400":{"description":"Unknown query parameter or invalid filter value. The message names the offending parameter","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key's role does not hold `posts.read`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PermissionDeniedError"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"description":"Requires the `posts.read` permission."}},"/social-posts/bulk":{"post":{"operationId":"bulkSchedulePosts","summary":"Bulk schedule up to 100 posts","tags":["Posts"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkSchedulePostsRequest"}}}},"responses":{"201":{"description":"Bulk scheduling result","content":{"application/json":{"schema":{"type":"object","properties":{"totalScheduled":{"type":"integer"},"totalFailed":{"type":"integer"},"results":{"type":"array","items":{"type":"object","properties":{"postId":{"type":"string"},"success":{"type":"boolean"},"isScheduled":{"type":"boolean"},"scheduledAt":{"type":"string","format":"date-time"},"errorMessage":{"type":"string"}}}}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key's role does not hold `posts.schedule`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PermissionDeniedError"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"description":"Requires the `posts.schedule` permission.","parameters":[{"name":"X-Workspace-Id","in":"header","required":false,"schema":{"type":"string"},"description":"The workspace to act in, for OAuth sign-ins that reach several workspaces. Take the id from `GET /workspaces`. Without it the call lands in the default workspace. A workspace the caller cannot reach answers 403 with `code: workspace_access_denied`. An API key belongs to one workspace: send its own id or leave the header out"}]}},"/social-posts/{id}":{"get":{"operationId":"getPost","summary":"Get a post by ID","tags":["Posts"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Post ID"},{"name":"X-Workspace-Id","in":"header","required":false,"schema":{"type":"string"},"description":"The workspace to act in, for OAuth sign-ins that reach several workspaces. Take the id from `GET /workspaces`. Without it the call lands in the default workspace. A workspace the caller cannot reach answers 403 with `code: workspace_access_denied`. An API key belongs to one workspace: send its own id or leave the header out"}],"responses":{"200":{"description":"The post","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Post"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key's role does not hold `posts.read`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PermissionDeniedError"}}}},"404":{"description":"Post not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"description":"Requires the `posts.read` permission."},"patch":{"operationId":"updatePost","summary":"Update a draft or scheduled post","tags":["Posts"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Post ID"},{"name":"X-Workspace-Id","in":"header","required":false,"schema":{"type":"string"},"description":"The workspace to act in, for OAuth sign-ins that reach several workspaces. Take the id from `GET /workspaces`. Without it the call lands in the default workspace. A workspace the caller cannot reach answers 403 with `code: workspace_access_denied`. An API key belongs to one workspace: send its own id or leave the header out"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateSocialPostRequest"}}}},"responses":{"200":{"description":"Updated post","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Post"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key's role does not hold `posts.draft`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PermissionDeniedError"}}}},"404":{"description":"Post not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"description":"Requires the `posts.draft` permission."},"delete":{"operationId":"deletePost","summary":"Delete a post","tags":["Posts"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Post ID"},{"name":"X-Workspace-Id","in":"header","required":false,"schema":{"type":"string"},"description":"The workspace to act in, for OAuth sign-ins that reach several workspaces. Take the id from `GET /workspaces`. Without it the call lands in the default workspace. A workspace the caller cannot reach answers 403 with `code: workspace_access_denied`. An API key belongs to one workspace: send its own id or leave the header out"}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean"}}}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key's role does not hold `posts.draft`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PermissionDeniedError"}}}},"404":{"description":"Post not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Post is being published and cannot be deleted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"description":"Requires the `posts.draft` permission."}},"/social-posts/{id}/results":{"get":{"operationId":"getPostResults","summary":"Get per-platform publishing results for a post","tags":["Posts"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Post ID"},{"name":"X-Workspace-Id","in":"header","required":false,"schema":{"type":"string"},"description":"The workspace to act in, for OAuth sign-ins that reach several workspaces. Take the id from `GET /workspaces`. Without it the call lands in the default workspace. A workspace the caller cannot reach answers 403 with `code: workspace_access_denied`. An API key belongs to one workspace: send its own id or leave the header out"}],"responses":{"200":{"description":"Per-platform results","content":{"application/json":{"schema":{"type":"object","properties":{"postId":{"type":"string"},"status":{"$ref":"#/components/schemas/PostStatus"},"results":{"type":"array","items":{"type":"object","properties":{"platformId":{"type":"string"},"platform":{"$ref":"#/components/schemas/PlatformType"},"accountName":{"type":"string"},"status":{"$ref":"#/components/schemas/PlatformPostStatus"},"platformPostId":{"type":"string"},"errorMessage":{"type":"string"},"tiktokDraftFallback":{"type":"boolean","description":"TikTok delivered the content to the inbox as a draft because the daily active-user cap was hit"},"publishedAt":{"type":"string","format":"date-time"}}}}}}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key's role does not hold `posts.read`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PermissionDeniedError"}}}},"404":{"description":"Post not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"description":"Requires the `posts.read` permission."}},"/social-posts/{id}/publish":{"post":{"operationId":"publishDraft","summary":"Publish or schedule a draft post","description":"Scheduling the draft for a future `scheduledAt` also needs `posts.schedule`, and publishing it now also needs `posts.publish`, so a key that holds only `posts.draft` cannot publish; the call answers 403 `permission_denied`. Requires the `posts.draft` permission.","tags":["Posts"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Post ID"},{"name":"X-Workspace-Id","in":"header","required":false,"schema":{"type":"string"},"description":"The workspace to act in, for OAuth sign-ins that reach several workspaces. Take the id from `GET /workspaces`. Without it the call lands in the default workspace. A workspace the caller cannot reach answers 403 with `code: workspace_access_denied`. An API key belongs to one workspace: send its own id or leave the header out"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublishDraftRequest"}}}},"responses":{"201":{"description":"Publish result","content":{"application/json":{"schema":{"type":"object","properties":{"postId":{"type":"string"},"queuedPlatforms":{"type":"array","items":{"$ref":"#/components/schemas/PlatformType"}},"isScheduled":{"type":"boolean"},"scheduledAt":{"type":"string","format":"date-time"}}}}}},"400":{"description":"Post is not a draft","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key's role does not hold `posts.draft`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PermissionDeniedError"}}}},"404":{"description":"Post not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/social-posts/{id}/unschedule":{"post":{"operationId":"unschedulePost","summary":"Unschedule a post","description":"Turns a scheduled or dated draft post back into an undated draft. Requires the `posts.draft` permission.","tags":["Posts"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Post ID"},{"name":"X-Workspace-Id","in":"header","required":false,"schema":{"type":"string"},"description":"The workspace to act in, for OAuth sign-ins that reach several workspaces. Take the id from `GET /workspaces`. Without it the call lands in the default workspace. A workspace the caller cannot reach answers 403 with `code: workspace_access_denied`. An API key belongs to one workspace: send its own id or leave the header out"}],"responses":{"200":{"description":"The post as an undated draft","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Post"}}}},"400":{"description":"Post is neither a draft nor scheduled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key's role does not hold `posts.draft`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PermissionDeniedError"}}}},"404":{"description":"Post not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/social-posts/{id}/retry":{"post":{"operationId":"retryFailedPlatforms","summary":"Retry failed platforms of a post","tags":["Posts"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Post ID"},{"name":"X-Workspace-Id","in":"header","required":false,"schema":{"type":"string"},"description":"The workspace to act in, for OAuth sign-ins that reach several workspaces. Take the id from `GET /workspaces`. Without it the call lands in the default workspace. A workspace the caller cannot reach answers 403 with `code: workspace_access_denied`. An API key belongs to one workspace: send its own id or leave the header out"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RetryFailedPlatformsRequest"}}}},"responses":{"201":{"description":"Retry result","content":{"application/json":{"schema":{"type":"object","properties":{"postId":{"type":"string"},"queuedPlatforms":{"type":"array","items":{"$ref":"#/components/schemas/PlatformType"}},"isScheduled":{"type":"boolean"}}}}}},"400":{"description":"A value in `platformIds` matches no platform entry of the post, or none of the matched entries has failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key's role does not hold `posts.publish`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PermissionDeniedError"}}}},"404":{"description":"Post not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"description":"Requires the `posts.publish` permission."}},"/recurring-posts":{"get":{"operationId":"listRecurringPosts","summary":"List recurring posts","description":"Series created by passing `recurrence` to `POST /social-posts`. Only the next occurrence of an active series exists as a scheduled post. Requires the `posts.read` permission.","tags":["Recurring posts"],"parameters":[{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":20}},{"name":"offset","in":"query","schema":{"type":"integer","minimum":0,"default":0}},{"name":"statuses","in":"query","schema":{"type":"array","items":{"type":"string","enum":["ACTIVE","PAUSED","ENDED"]}},"explode":true},{"name":"X-Workspace-Id","in":"header","required":false,"schema":{"type":"string"},"description":"The workspace to act in, for OAuth sign-ins that reach several workspaces. Take the id from `GET /workspaces`. Without it the call lands in the default workspace. A workspace the caller cannot reach answers 403 with `code: workspace_access_denied`. An API key belongs to one workspace: send its own id or leave the header out"}],"responses":{"200":{"description":"Paginated recurring posts","content":{"application/json":{"schema":{"type":"object","properties":{"recurringPosts":{"type":"array","items":{"$ref":"#/components/schemas/RecurringPost"}},"total":{"type":"integer"},"hasMore":{"type":"boolean"}}}}}},"400":{"description":"Unknown query parameter or invalid filter","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key's role does not hold `posts.read`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PermissionDeniedError"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/recurring-posts/{id}":{"get":{"operationId":"getRecurringPost","summary":"Get a recurring post","tags":["Recurring posts"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Recurring post ID"},{"name":"X-Workspace-Id","in":"header","required":false,"schema":{"type":"string"},"description":"The workspace to act in, for OAuth sign-ins that reach several workspaces. Take the id from `GET /workspaces`. Without it the call lands in the default workspace. A workspace the caller cannot reach answers 403 with `code: workspace_access_denied`. An API key belongs to one workspace: send its own id or leave the header out"}],"responses":{"200":{"description":"The recurring post","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecurringPost"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key's role does not hold `posts.read`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PermissionDeniedError"}}}},"404":{"description":"Recurring post not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"description":"Requires the `posts.read` permission."},"delete":{"operationId":"deleteRecurringPost","summary":"Delete a recurring post","description":"Stops the series and deletes its upcoming scheduled post. Posts that already went out are kept. Requires the `posts.draft` permission.","tags":["Recurring posts"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Recurring post ID"},{"name":"X-Workspace-Id","in":"header","required":false,"schema":{"type":"string"},"description":"The workspace to act in, for OAuth sign-ins that reach several workspaces. Take the id from `GET /workspaces`. Without it the call lands in the default workspace. A workspace the caller cannot reach answers 403 with `code: workspace_access_denied`. An API key belongs to one workspace: send its own id or leave the header out"}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean"}}}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key's role does not hold `posts.draft`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PermissionDeniedError"}}}},"404":{"description":"Recurring post not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/recurring-posts/{id}/pause":{"post":{"operationId":"pauseRecurringPost","summary":"Pause a recurring post","description":"Stops creating occurrences and deletes the upcoming scheduled post. Requires the `posts.draft` permission.","tags":["Recurring posts"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Recurring post ID"},{"name":"X-Workspace-Id","in":"header","required":false,"schema":{"type":"string"},"description":"The workspace to act in, for OAuth sign-ins that reach several workspaces. Take the id from `GET /workspaces`. Without it the call lands in the default workspace. A workspace the caller cannot reach answers 403 with `code: workspace_access_denied`. An API key belongs to one workspace: send its own id or leave the header out"}],"responses":{"200":{"description":"The paused recurring post","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecurringPost"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key's role does not hold `posts.draft`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PermissionDeniedError"}}}},"404":{"description":"Recurring post not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/recurring-posts/{id}/resume":{"post":{"operationId":"resumeRecurringPost","summary":"Resume a recurring post","description":"Continues from the next occurrence after now. Occurrences missed while paused are not published. Requires the `posts.draft` permission.","tags":["Recurring posts"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Recurring post ID"},{"name":"X-Workspace-Id","in":"header","required":false,"schema":{"type":"string"},"description":"The workspace to act in, for OAuth sign-ins that reach several workspaces. Take the id from `GET /workspaces`. Without it the call lands in the default workspace. A workspace the caller cannot reach answers 403 with `code: workspace_access_denied`. An API key belongs to one workspace: send its own id or leave the header out"}],"responses":{"200":{"description":"The resumed recurring post","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecurringPost"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key's role does not hold `posts.draft`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PermissionDeniedError"}}}},"404":{"description":"Recurring post not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/social-accounts":{"get":{"operationId":"listSocialAccounts","summary":"List connected social accounts","tags":["Accounts"],"responses":{"200":{"description":"Connected accounts","content":{"application/json":{"schema":{"type":"object","properties":{"accounts":{"type":"array","items":{"$ref":"#/components/schemas/SocialAccount"}}}}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key's role does not hold `accounts.read`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PermissionDeniedError"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"description":"Requires the `accounts.read` permission.","parameters":[{"name":"X-Workspace-Id","in":"header","required":false,"schema":{"type":"string"},"description":"The workspace to act in, for OAuth sign-ins that reach several workspaces. Take the id from `GET /workspaces`. Without it the call lands in the default workspace. A workspace the caller cannot reach answers 403 with `code: workspace_access_denied`. An API key belongs to one workspace: send its own id or leave the header out"}]}},"/social-accounts/{id}/check":{"post":{"operationId":"checkSocialAccount","summary":"Re-check an account with its platform now","description":"Probes the platform with the stored token and returns the resulting `status`. Facebook pages only: a rejected token marks the page `unauthorized` (and fires `account.unauthorized`), a working token clears an earlier `unauthorized` mark. Pages are also checked automatically twice a day. Accepts the account `id` or the Facebook `pageId`.. Requires the `accounts.manage` permission.","tags":["Accounts"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"The account `id` from /social-accounts, or the Facebook `pageId`"},{"name":"X-Workspace-Id","in":"header","required":false,"schema":{"type":"string"},"description":"The workspace to act in, for OAuth sign-ins that reach several workspaces. Take the id from `GET /workspaces`. Without it the call lands in the default workspace. A workspace the caller cannot reach answers 403 with `code: workspace_access_denied`. An API key belongs to one workspace: send its own id or leave the header out"}],"responses":{"200":{"description":"Fresh account status","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SocialAccountCheckResult"}}}},"400":{"description":"The account is not a Facebook page","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key's role does not hold `accounts.manage`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PermissionDeniedError"}}}},"404":{"description":"Account not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/social-accounts/{id}/instagram-audio":{"get":{"operationId":"searchInstagramAudio","summary":"Search Instagram music and original sounds for a Reel","description":"Returns audio Meta has cleared for third-party publishing, which can differ from what the Instagram app shows. Leave out `searchQuery` to get what is trending. Only works for Instagram accounts with `supportsInstagramAudio: true`; pass the chosen `audioId` in `instagramConfigs[].audio`.. Requires the `posts.draft` permission.","tags":["Accounts"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"The Instagram account `id` from /social-accounts"},{"name":"audioType","in":"query","required":true,"schema":{"type":"string","enum":["music","original_sound"]}},{"name":"searchQuery","in":"query","required":false,"schema":{"type":"string","maxLength":100},"description":"Keyword filter. Leave out for trending audio"},{"name":"X-Workspace-Id","in":"header","required":false,"schema":{"type":"string"},"description":"The workspace to act in, for OAuth sign-ins that reach several workspaces. Take the id from `GET /workspaces`. Without it the call lands in the default workspace. A workspace the caller cannot reach answers 403 with `code: workspace_access_denied`. An API key belongs to one workspace: send its own id or leave the header out"}],"responses":{"200":{"description":"Matching audio","content":{"application/json":{"schema":{"type":"object","properties":{"audio":{"type":"array","items":{"$ref":"#/components/schemas/InstagramAudio"}}}}}}},"400":{"description":"The account is not connected through Facebook, or Instagram rejected the search","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key's role does not hold `posts.draft`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PermissionDeniedError"}}}},"404":{"description":"Account not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/social-accounts/{id}/pinterest-boards":{"get":{"operationId":"getPinterestBoards","summary":"List the boards of a Pinterest account","description":"Returns every board the account can pin to, secret boards included. Pass the chosen `id` as `pinterestConfigs[].boardId`. A board belongs to one Pinterest account, so list the boards of each account you post to.. Requires the `posts.draft` permission.","tags":["Accounts"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"The Pinterest account `id` from /social-accounts"},{"name":"X-Workspace-Id","in":"header","required":false,"schema":{"type":"string"},"description":"The workspace to act in, for OAuth sign-ins that reach several workspaces. Take the id from `GET /workspaces`. Without it the call lands in the default workspace. A workspace the caller cannot reach answers 403 with `code: workspace_access_denied`. An API key belongs to one workspace: send its own id or leave the header out"}],"responses":{"200":{"description":"Boards of the account","content":{"application/json":{"schema":{"type":"object","properties":{"boards":{"type":"array","items":{"$ref":"#/components/schemas/PinterestBoard"}}}}}}},"400":{"description":"Pinterest refused the request, usually because the account must be reconnected","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key's role does not hold `posts.draft`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PermissionDeniedError"}}}},"404":{"description":"Account not found, or not a Pinterest account","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/upload-urls":{"post":{"operationId":"getUploadUrls","summary":"Get presigned upload URLs for media files","tags":["Media"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetUploadUrlsRequest"}}}},"responses":{"201":{"description":"Presigned upload URLs","content":{"application/json":{"schema":{"type":"object","properties":{"urls":{"type":"array","items":{"type":"object","properties":{"fileName":{"type":"string"},"uploadUrl":{"type":"string","description":"PUT the file bytes to this URL"},"publicUrl":{"type":"string","description":"Public URL to reference in mediaUrls after upload. Reusable across posts; the file stays until the last post referencing it has published"},"key":{"type":"string"},"expiresAt":{"type":"string","format":"date-time"}}}}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key's role does not hold `media.upload`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PermissionDeniedError"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"description":"Requires the `media.upload` permission.","parameters":[{"name":"X-Workspace-Id","in":"header","required":false,"schema":{"type":"string"},"description":"The workspace to act in, for OAuth sign-ins that reach several workspaces. Take the id from `GET /workspaces`. Without it the call lands in the default workspace. A workspace the caller cannot reach answers 403 with `code: workspace_access_denied`. An API key belongs to one workspace: send its own id or leave the header out"}]}},"/connect-links":{"post":{"operationId":"createConnectLink","summary":"Create a white-label link for connecting social accounts","tags":["Connect Links"],"responses":{"201":{"description":"Connect link","content":{"application/json":{"schema":{"type":"object","properties":{"url":{"type":"string"},"token":{"type":"string"},"expiresAt":{"type":"string"}}}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key's role does not hold `accounts.manage`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PermissionDeniedError"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"description":"Requires the `accounts.manage` permission.","parameters":[{"name":"X-Workspace-Id","in":"header","required":false,"schema":{"type":"string"},"description":"The workspace to act in, for OAuth sign-ins that reach several workspaces. Take the id from `GET /workspaces`. Without it the call lands in the default workspace. A workspace the caller cannot reach answers 403 with `code: workspace_access_denied`. An API key belongs to one workspace: send its own id or leave the header out"}]}},"/connect-links/{token}":{"delete":{"operationId":"revokeConnectLink","summary":"Revoke a connect link","tags":["Connect Links"],"parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string"}},{"name":"X-Workspace-Id","in":"header","required":false,"schema":{"type":"string"},"description":"The workspace to act in, for OAuth sign-ins that reach several workspaces. Take the id from `GET /workspaces`. Without it the call lands in the default workspace. A workspace the caller cannot reach answers 403 with `code: workspace_access_denied`. An API key belongs to one workspace: send its own id or leave the header out"}],"responses":{"200":{"description":"Revoked","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}}}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key's role does not hold `accounts.manage`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PermissionDeniedError"}}}},"404":{"description":"Connect link not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"description":"Requires the `accounts.manage` permission."}},"/webhooks":{"post":{"operationId":"createWebhook","summary":"Register a webhook endpoint","description":"Registers a URL that receives ALL events (post.published, post.partially_failed, post.failed, post.scheduled, account.unauthorized, image.completed, image.failed). Deliveries are HTTP POSTs with a WebhookEventPayload body, retried up to 5 times with exponential backoff. Each request includes `x-adaptly-signature`, `x-adaptly-timestamp`, `x-adaptly-event`, and `x-adaptly-webhook-id` headers. Webhooks are auto-disabled after 20 consecutive failed deliveries.. Requires the `webhooks.manage` permission.","tags":["Webhooks"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWebhookRequest"}}}},"responses":{"201":{"description":"Created webhook (the only response that includes the signing secret)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWebhookResponse"}}}},"400":{"description":"Invalid URL or webhook limit reached","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key's role does not hold `webhooks.manage`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PermissionDeniedError"}}}},"409":{"description":"A webhook with this URL already exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"X-Workspace-Id","in":"header","required":false,"schema":{"type":"string"},"description":"The workspace to act in, for OAuth sign-ins that reach several workspaces. Take the id from `GET /workspaces`. Without it the call lands in the default workspace. A workspace the caller cannot reach answers 403 with `code: workspace_access_denied`. An API key belongs to one workspace: send its own id or leave the header out"}]},"get":{"operationId":"listWebhooks","summary":"List registered webhooks","tags":["Webhooks"],"responses":{"200":{"description":"Registered webhooks","content":{"application/json":{"schema":{"type":"object","properties":{"webhooks":{"type":"array","items":{"$ref":"#/components/schemas/Webhook"}}}}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key's role does not hold `webhooks.read`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PermissionDeniedError"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"description":"Requires the `webhooks.read` permission.","parameters":[{"name":"X-Workspace-Id","in":"header","required":false,"schema":{"type":"string"},"description":"The workspace to act in, for OAuth sign-ins that reach several workspaces. Take the id from `GET /workspaces`. Without it the call lands in the default workspace. A workspace the caller cannot reach answers 403 with `code: workspace_access_denied`. An API key belongs to one workspace: send its own id or leave the header out"}]}},"/webhooks/{id}":{"get":{"operationId":"getWebhook","summary":"Get a webhook by ID","tags":["Webhooks"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Webhook ID"},{"name":"X-Workspace-Id","in":"header","required":false,"schema":{"type":"string"},"description":"The workspace to act in, for OAuth sign-ins that reach several workspaces. Take the id from `GET /workspaces`. Without it the call lands in the default workspace. A workspace the caller cannot reach answers 403 with `code: workspace_access_denied`. An API key belongs to one workspace: send its own id or leave the header out"}],"responses":{"200":{"description":"The webhook","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Webhook"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key's role does not hold `webhooks.read`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PermissionDeniedError"}}}},"404":{"description":"Webhook not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"description":"Requires the `webhooks.read` permission."},"patch":{"operationId":"updateWebhook","summary":"Update a webhook (URL or active state)","tags":["Webhooks"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Webhook ID"},{"name":"X-Workspace-Id","in":"header","required":false,"schema":{"type":"string"},"description":"The workspace to act in, for OAuth sign-ins that reach several workspaces. Take the id from `GET /workspaces`. Without it the call lands in the default workspace. A workspace the caller cannot reach answers 403 with `code: workspace_access_denied`. An API key belongs to one workspace: send its own id or leave the header out"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateWebhookRequest"}}}},"responses":{"200":{"description":"Updated webhook","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Webhook"}}}},"400":{"description":"Invalid URL","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key's role does not hold `webhooks.manage`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PermissionDeniedError"}}}},"404":{"description":"Webhook not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"A webhook with this URL already exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"description":"Requires the `webhooks.manage` permission."},"delete":{"operationId":"deleteWebhook","summary":"Delete a webhook","tags":["Webhooks"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Webhook ID"},{"name":"X-Workspace-Id","in":"header","required":false,"schema":{"type":"string"},"description":"The workspace to act in, for OAuth sign-ins that reach several workspaces. Take the id from `GET /workspaces`. Without it the call lands in the default workspace. A workspace the caller cannot reach answers 403 with `code: workspace_access_denied`. An API key belongs to one workspace: send its own id or leave the header out"}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean"}}}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key's role does not hold `webhooks.manage`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PermissionDeniedError"}}}},"404":{"description":"Webhook not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"description":"Requires the `webhooks.manage` permission."}},"/webhooks/{id}/test":{"post":{"operationId":"testWebhook","summary":"Send a signed webhook.test event to the endpoint","tags":["Webhooks"],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"Webhook ID"},{"name":"X-Workspace-Id","in":"header","required":false,"schema":{"type":"string"},"description":"The workspace to act in, for OAuth sign-ins that reach several workspaces. Take the id from `GET /workspaces`. Without it the call lands in the default workspace. A workspace the caller cannot reach answers 403 with `code: workspace_access_denied`. An API key belongs to one workspace: send its own id or leave the header out"}],"responses":{"201":{"description":"Delivery attempt result","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"statusCode":{"type":"integer"},"error":{"type":"string"}}}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key's role does not hold `webhooks.manage`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PermissionDeniedError"}}}},"404":{"description":"Webhook not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"description":"Requires the `webhooks.manage` permission."}},"/analytics/overview":{"get":{"operationId":"getAnalyticsOverview","summary":"Workspace-wide totals for a date window, with previous-window comparison","description":"Sums views, likes, comments, shares, posts published and average views per post across the selected platforms for posts published inside the window, plus the latest follower count and engagement rate. Every metric carries `previousValue` and `deltaPercent` against the window of the same length immediately before `from`. `partialMetrics` lists metrics some selected platform cannot report, so their totals cover only the platforms that can. A metric is `null` when no selected platform reports it.. Requires the `analytics.read` permission.","tags":["Analytics"],"parameters":[{"name":"from","in":"query","required":true,"schema":{"type":"string","format":"date-time"},"description":"Start of the reporting window (ISO 8601). Metrics cover posts published between `from` and `to`; comparisons use the window of the same length immediately before it"},{"name":"to","in":"query","required":true,"schema":{"type":"string","format":"date-time"},"description":"End of the reporting window (ISO 8601). Must not be earlier than `from`"},{"name":"platforms","in":"query","schema":{"type":"array","items":{"type":"string","enum":["FACEBOOK","INSTAGRAM","THREADS","TIKTOK","TWITTER","BLUESKY","LINKEDIN","PINTEREST","YOUTUBE","MASTODON","TELEGRAM","GOOGLE_BUSINESS"]}},"explode":true,"description":"Restrict to these platforms. Omit for every platform with analytics. MASTODON and TELEGRAM have no analytics and are ignored"},{"name":"X-Workspace-Id","in":"header","required":false,"schema":{"type":"string"},"description":"The workspace to act in, for OAuth sign-ins that reach several workspaces. Take the id from `GET /workspaces`. Without it the call lands in the default workspace. A workspace the caller cannot reach answers 403 with `code: workspace_access_denied`. An API key belongs to one workspace: send its own id or leave the header out"}],"responses":{"200":{"description":"Overview metrics","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnalyticsOverview"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key's role does not hold `analytics.read`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PermissionDeniedError"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/analytics/timeseries":{"get":{"operationId":"getAnalyticsTimeseries","summary":"Metrics bucketed by day, week or month","description":"One point per bucket inside the window. `followers` is the latest known count at the end of each bucket; the other counters sum posts published inside the bucket. A metric no selected platform reports is `null`.. Requires the `analytics.read` permission.","tags":["Analytics"],"parameters":[{"name":"from","in":"query","required":true,"schema":{"type":"string","format":"date-time"},"description":"Start of the reporting window (ISO 8601). Metrics cover posts published between `from` and `to`; comparisons use the window of the same length immediately before it"},{"name":"to","in":"query","required":true,"schema":{"type":"string","format":"date-time"},"description":"End of the reporting window (ISO 8601). Must not be earlier than `from`"},{"name":"platforms","in":"query","schema":{"type":"array","items":{"type":"string","enum":["FACEBOOK","INSTAGRAM","THREADS","TIKTOK","TWITTER","BLUESKY","LINKEDIN","PINTEREST","YOUTUBE","MASTODON","TELEGRAM","GOOGLE_BUSINESS"]}},"explode":true,"description":"Restrict to these platforms. Omit for every platform with analytics. MASTODON and TELEGRAM have no analytics and are ignored"},{"name":"granularity","in":"query","schema":{"type":"string","enum":["DAILY","WEEKLY","MONTHLY"],"default":"DAILY"}},{"name":"X-Workspace-Id","in":"header","required":false,"schema":{"type":"string"},"description":"The workspace to act in, for OAuth sign-ins that reach several workspaces. Take the id from `GET /workspaces`. Without it the call lands in the default workspace. A workspace the caller cannot reach answers 403 with `code: workspace_access_denied`. An API key belongs to one workspace: send its own id or leave the header out"}],"responses":{"200":{"description":"Timeseries points","content":{"application/json":{"schema":{"type":"object","properties":{"points":{"type":"array","items":{"$ref":"#/components/schemas/AnalyticsTimeseriesPoint"}}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key's role does not hold `analytics.read`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PermissionDeniedError"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/analytics/platform-breakdown":{"get":{"operationId":"getAnalyticsPlatformBreakdown","summary":"The same metrics as the overview, one row per platform","description":"One entry per platform that has analytics data in the workspace, each with the metrics of /analytics/overview scoped to that platform and a `supportedMetrics` list naming the metrics the platform actually reports.. Requires the `analytics.read` permission.","tags":["Analytics"],"parameters":[{"name":"from","in":"query","required":true,"schema":{"type":"string","format":"date-time"},"description":"Start of the reporting window (ISO 8601). Metrics cover posts published between `from` and `to`; comparisons use the window of the same length immediately before it"},{"name":"to","in":"query","required":true,"schema":{"type":"string","format":"date-time"},"description":"End of the reporting window (ISO 8601). Must not be earlier than `from`"},{"name":"X-Workspace-Id","in":"header","required":false,"schema":{"type":"string"},"description":"The workspace to act in, for OAuth sign-ins that reach several workspaces. Take the id from `GET /workspaces`. Without it the call lands in the default workspace. A workspace the caller cannot reach answers 403 with `code: workspace_access_denied`. An API key belongs to one workspace: send its own id or leave the header out"}],"responses":{"200":{"description":"Per-platform metrics","content":{"application/json":{"schema":{"type":"object","properties":{"platforms":{"type":"array","items":{"$ref":"#/components/schemas/AnalyticsPlatformBreakdownItem"}}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key's role does not hold `analytics.read`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PermissionDeniedError"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/analytics/posts":{"get":{"operationId":"listPostAnalytics","summary":"Per-post metrics for every tracked post published in the window, paginated","description":"Covers posts published through AdaptlyPost and posts discovered on the connected accounts; discovered posts have `postId` and `postPlatformId` set to null. Sort by any metric or by `PUBLISHED_AT` (the default). A metric the platform does not report is `null`.. Requires the `analytics.read` permission.","tags":["Analytics"],"parameters":[{"name":"from","in":"query","required":true,"schema":{"type":"string","format":"date-time"},"description":"Start of the reporting window (ISO 8601). Metrics cover posts published between `from` and `to`; comparisons use the window of the same length immediately before it"},{"name":"to","in":"query","required":true,"schema":{"type":"string","format":"date-time"},"description":"End of the reporting window (ISO 8601). Must not be earlier than `from`"},{"name":"platforms","in":"query","schema":{"type":"array","items":{"type":"string","enum":["FACEBOOK","INSTAGRAM","THREADS","TIKTOK","TWITTER","BLUESKY","LINKEDIN","PINTEREST","YOUTUBE","MASTODON","TELEGRAM","GOOGLE_BUSINESS"]}},"explode":true,"description":"Restrict to these platforms. Omit for every platform with analytics. MASTODON and TELEGRAM have no analytics and are ignored"},{"name":"sortBy","in":"query","schema":{"type":"string","enum":["VIEWS","LIKES","COMMENTS","SHARES","SAVES","CLICKS","IMPRESSIONS","ENGAGEMENT_RATE","PUBLISHED_AT"],"default":"PUBLISHED_AT"}},{"name":"page","in":"query","schema":{"type":"integer","minimum":1,"default":1}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":100,"default":20}},{"name":"X-Workspace-Id","in":"header","required":false,"schema":{"type":"string"},"description":"The workspace to act in, for OAuth sign-ins that reach several workspaces. Take the id from `GET /workspaces`. Without it the call lands in the default workspace. A workspace the caller cannot reach answers 403 with `code: workspace_access_denied`. An API key belongs to one workspace: send its own id or leave the header out"}],"responses":{"200":{"description":"Paginated post metrics","content":{"application/json":{"schema":{"type":"object","properties":{"posts":{"type":"array","items":{"$ref":"#/components/schemas/AnalyticsPost"}},"total":{"type":"integer"},"page":{"type":"integer"},"limit":{"type":"integer"},"hasMore":{"type":"boolean"}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key's role does not hold `analytics.read`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PermissionDeniedError"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/analytics/top-posts":{"get":{"operationId":"listTopPosts","summary":"Best-performing posts in the window by one metric","description":"The top `limit` posts published inside the window, ordered by `sortBy` (default VIEWS). Same rows as /analytics/posts without pagination.. Requires the `analytics.read` permission.","tags":["Analytics"],"parameters":[{"name":"from","in":"query","required":true,"schema":{"type":"string","format":"date-time"},"description":"Start of the reporting window (ISO 8601). Metrics cover posts published between `from` and `to`; comparisons use the window of the same length immediately before it"},{"name":"to","in":"query","required":true,"schema":{"type":"string","format":"date-time"},"description":"End of the reporting window (ISO 8601). Must not be earlier than `from`"},{"name":"platforms","in":"query","schema":{"type":"array","items":{"type":"string","enum":["FACEBOOK","INSTAGRAM","THREADS","TIKTOK","TWITTER","BLUESKY","LINKEDIN","PINTEREST","YOUTUBE","MASTODON","TELEGRAM","GOOGLE_BUSINESS"]}},"explode":true,"description":"Restrict to these platforms. Omit for every platform with analytics. MASTODON and TELEGRAM have no analytics and are ignored"},{"name":"sortBy","in":"query","schema":{"type":"string","enum":["VIEWS","LIKES","COMMENTS","SHARES","SAVES","CLICKS","IMPRESSIONS","ENGAGEMENT_RATE","PUBLISHED_AT"],"default":"VIEWS"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":50,"default":10}},{"name":"X-Workspace-Id","in":"header","required":false,"schema":{"type":"string"},"description":"The workspace to act in, for OAuth sign-ins that reach several workspaces. Take the id from `GET /workspaces`. Without it the call lands in the default workspace. A workspace the caller cannot reach answers 403 with `code: workspace_access_denied`. An API key belongs to one workspace: send its own id or leave the header out"}],"responses":{"200":{"description":"Top posts","content":{"application/json":{"schema":{"type":"object","properties":{"posts":{"type":"array","items":{"$ref":"#/components/schemas/AnalyticsPost"}}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key's role does not hold `analytics.read`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PermissionDeniedError"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/analytics/discovered-posts":{"get":{"operationId":"listDiscoveredPosts","summary":"Posts found on the connected accounts that were not published through AdaptlyPost","description":"Read-only calendar entries for content published elsewhere (natively, or through another tool). Their metrics appear in /analytics/posts; this endpoint returns the post itself: text, thumbnail, permalink and publish time.. Requires the `analytics.read` permission.","tags":["Analytics"],"parameters":[{"name":"from","in":"query","required":true,"schema":{"type":"string","format":"date-time"},"description":"Start of the reporting window (ISO 8601). Metrics cover posts published between `from` and `to`; comparisons use the window of the same length immediately before it"},{"name":"to","in":"query","required":true,"schema":{"type":"string","format":"date-time"},"description":"End of the reporting window (ISO 8601). Must not be earlier than `from`"},{"name":"platforms","in":"query","schema":{"type":"array","items":{"type":"string","enum":["FACEBOOK","INSTAGRAM","THREADS","TIKTOK","TWITTER","BLUESKY","LINKEDIN","PINTEREST","YOUTUBE","MASTODON","TELEGRAM","GOOGLE_BUSINESS"]}},"explode":true,"description":"Restrict to these platforms. Omit for every platform with analytics. MASTODON and TELEGRAM have no analytics and are ignored"},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":1000,"default":200}},{"name":"X-Workspace-Id","in":"header","required":false,"schema":{"type":"string"},"description":"The workspace to act in, for OAuth sign-ins that reach several workspaces. Take the id from `GET /workspaces`. Without it the call lands in the default workspace. A workspace the caller cannot reach answers 403 with `code: workspace_access_denied`. An API key belongs to one workspace: send its own id or leave the header out"}],"responses":{"200":{"description":"Discovered posts","content":{"application/json":{"schema":{"type":"object","properties":{"posts":{"type":"array","items":{"$ref":"#/components/schemas/DiscoveredPost"}}}}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key's role does not hold `analytics.read`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PermissionDeniedError"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/analytics/sync-status":{"get":{"operationId":"getAnalyticsSyncStatus","summary":"Freshness of the analytics data, per connected account","description":"Tells you when each account was last synced, whether a sync is running now, how far back the data goes (`historyHorizonAt`) and whether an account must be reconnected before analytics can be read from it (`needsAnalyticsReconnect`). Accounts on platforms without analytics (Mastodon and Telegram) are not listed.. Requires the `analytics.read` permission.","tags":["Analytics"],"responses":{"200":{"description":"Sync status","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnalyticsSyncStatus"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key's role does not hold `analytics.read`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PermissionDeniedError"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"X-Workspace-Id","in":"header","required":false,"schema":{"type":"string"},"description":"The workspace to act in, for OAuth sign-ins that reach several workspaces. Take the id from `GET /workspaces`. Without it the call lands in the default workspace. A workspace the caller cannot reach answers 403 with `code: workspace_access_denied`. An API key belongs to one workspace: send its own id or leave the header out"}]}},"/analytics/sync":{"post":{"operationId":"triggerAnalyticsSync","summary":"Ask for an analytics refresh now","description":"Queues a sync for every connected account with analytics and re-reads the last 7 days. Analytics refresh on their own every few hours, so this is for \"I just published, show me numbers\" moments. Rate limited to one run per workspace every 10 minutes: inside the cooldown the response is 200 with `queued: false` and `cooldownSecondsRemaining`, never an error. Read /analytics/sync-status to see the run finish.. Requires the `analytics.sync` permission.","tags":["Analytics"],"responses":{"200":{"description":"Sync request result","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TriggerAnalyticsSyncResponse"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key's role does not hold `analytics.sync`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PermissionDeniedError"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"X-Workspace-Id","in":"header","required":false,"schema":{"type":"string"},"description":"The workspace to act in, for OAuth sign-ins that reach several workspaces. Take the id from `GET /workspaces`. Without it the call lands in the default workspace. A workspace the caller cannot reach answers 403 with `code: workspace_access_denied`. An API key belongs to one workspace: send its own id or leave the header out"}]}},"/ai/captions":{"post":{"operationId":"generateCaption","summary":"Generate a caption","description":"Writes a caption from a prompt. Pass `platform` and the caption is held to that platform's character limit. Consumes one caption credit unless the workspace has its own provider key connected.. Requires the `ai.generate` permission.","tags":["AI"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateCaptionRequest"}}}},"responses":{"201":{"description":"Generated caption","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CaptionResponse"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Insufficient credits","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key's role does not hold `ai.generate`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PermissionDeniedError"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"X-Workspace-Id","in":"header","required":false,"schema":{"type":"string"},"description":"The workspace to act in, for OAuth sign-ins that reach several workspaces. Take the id from `GET /workspaces`. Without it the call lands in the default workspace. A workspace the caller cannot reach answers 403 with `code: workspace_access_denied`. An API key belongs to one workspace: send its own id or leave the header out"}]}},"/ai/captions/refine":{"post":{"operationId":"refineCaption","summary":"Rewrite an existing caption","description":"Rewrites `originalText` following the instruction in `prompt`. Send `partialText` to continue from a partly written caption. Costs the same as generating one.. Requires the `ai.generate` permission.","tags":["AI"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefineCaptionRequest"}}}},"responses":{"201":{"description":"Rewritten caption","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CaptionResponse"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Insufficient credits","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key's role does not hold `ai.generate`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PermissionDeniedError"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"X-Workspace-Id","in":"header","required":false,"schema":{"type":"string"},"description":"The workspace to act in, for OAuth sign-ins that reach several workspaces. Take the id from `GET /workspaces`. Without it the call lands in the default workspace. A workspace the caller cannot reach answers 403 with `code: workspace_access_denied`. An API key belongs to one workspace: send its own id or leave the header out"}]}},"/ai/images":{"post":{"operationId":"generateImage","summary":"Start an image generation","description":"Queues an image and returns a `jobId` right away. Poll GET /ai/images/{jobId} until `status` is `completed` or `failed`; a completed job carries the `imageUrl`. Images take roughly 10 to 40 seconds. Credits are charged when generation starts and refunded if it fails.. Requires the `ai.generate` permission.","tags":["AI"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateImageRequest"}}}},"responses":{"201":{"description":"Image generation queued","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateImageResponse"}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key's role does not hold `ai.generate`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PermissionDeniedError"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"parameters":[{"name":"X-Workspace-Id","in":"header","required":false,"schema":{"type":"string"},"description":"The workspace to act in, for OAuth sign-ins that reach several workspaces. Take the id from `GET /workspaces`. Without it the call lands in the default workspace. A workspace the caller cannot reach answers 403 with `code: workspace_access_denied`. An API key belongs to one workspace: send its own id or leave the header out"}]}},"/ai/images/{jobId}":{"get":{"operationId":"getImageJob","summary":"Read an image generation job","tags":["AI"],"parameters":[{"name":"jobId","in":"path","required":true,"schema":{"type":"string"},"description":"Job ID returned by POST /ai/images"},{"name":"X-Workspace-Id","in":"header","required":false,"schema":{"type":"string"},"description":"The workspace to act in, for OAuth sign-ins that reach several workspaces. Take the id from `GET /workspaces`. Without it the call lands in the default workspace. A workspace the caller cannot reach answers 403 with `code: workspace_access_denied`. An API key belongs to one workspace: send its own id or leave the header out"}],"responses":{"200":{"description":"Image job state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ImageJobResponse"}}}},"401":{"description":"Invalid or missing API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"The key's role does not hold `ai.generate`","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PermissionDeniedError"}}}},"404":{"description":"Job not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}},"description":"Requires the `ai.generate` permission."}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"API token from the AdaptlyPost dashboard, sent as 'Authorization: Bearer adaptly_...'. A key carries the workspace role it was created with, and can never do more than the member who created it: Admin runs the workspace and manages connected accounts and connect links, Editor creates, schedules and publishes, and manages webhooks and the post signature, Contributor creates and edits its own drafts and cannot schedule or publish, Viewer reads posts and analytics. Call `GET /me` to read the role and the exact permission list of the key in hand. An OAuth access token reaches every workspace its member belongs to: list them with `GET /workspaces` and pick one per call with the `X-Workspace-Id` header, where each workspace applies the member's own role there. An operation the role does not cover answers 403 with `code: permission_denied` and the missing `requiredPermission`; retrying or swapping keys will not change that. A key whose creator has since lost access to the workspace answers 401 with `code: token_issuer_lost_access`. An OAuth access token whose login has no AdaptlyPost account answers 401 with `code: oauth_account_not_found`. Every call answers 403 with `code: subscription_required` while the organization's plan is not active."}},"schemas":{"CaptionPlatform":{"type":"string","enum":["TWITTER","BLUESKY","MASTODON","THREADS","PINTEREST","INSTAGRAM","TIKTOK","LINKEDIN","YOUTUBE","FACEBOOK","GOOGLE_BUSINESS"],"description":"Holds the caption to this platform's character limit"},"ImageAspectRatio":{"type":"string","enum":["1:1","16:9","9:16","3:2","2:3","4:5","5:4","21:9","9:21"],"default":"1:1","description":"The image comes back at exactly this ratio: 1:1 is 1024x1024, 3:2 is 1536x1024, 2:3 is 1024x1536, 5:4 is 1280x1024, 4:5 is 1024x1280, 16:9 is 1536x864, 9:16 is 864x1536, 21:9 is 1792x768 and 9:21 is 768x1792. With your own Google key Google picks the pixel size, and 9:21 comes back as 9:16"},"ImageModel":{"type":"string","enum":["standard","premium"],"default":"standard"},"ImageQuality":{"type":"string","enum":["LOW","MEDIUM","HIGH"],"description":"Used only when you generate with your own OpenAI key. On AdaptlyPost credits `model` sets the quality and this field is ignored"},"ImageJobStatus":{"type":"string","enum":["queued","processing","generating","completed","failed"]},"GenerateCaptionRequest":{"type":"object","required":["prompt"],"properties":{"prompt":{"type":"string","maxLength":2000,"description":"What the caption should say or be about"},"platform":{"$ref":"#/components/schemas/CaptionPlatform"}}},"RefineCaptionRequest":{"type":"object","required":["prompt","originalText"],"properties":{"prompt":{"type":"string","maxLength":2000,"description":"How the caption should change"},"platform":{"$ref":"#/components/schemas/CaptionPlatform"},"originalText":{"type":"string","maxLength":10000,"description":"The caption to rewrite"},"partialText":{"type":"string","maxLength":10000,"description":"A partly written caption to continue from"}}},"CaptionResponse":{"type":"object","properties":{"caption":{"type":"string"}}},"GenerateImageRequest":{"type":"object","required":["prompt"],"properties":{"prompt":{"type":"string","maxLength":2000},"aspectRatio":{"$ref":"#/components/schemas/ImageAspectRatio"},"model":{"$ref":"#/components/schemas/ImageModel"},"quality":{"$ref":"#/components/schemas/ImageQuality"},"sessionId":{"type":"string","format":"uuid","description":"Groups images together. Omit it and a new session is created, then reuse the returned sessionId to keep related images in one place."},"referenceImages":{"type":"array","maxItems":5,"items":{"type":"string"},"description":"Public image URLs to steer the result"}}},"GenerateImageResponse":{"type":"object","properties":{"jobId":{"type":"string"},"sessionId":{"type":"string","format":"uuid"},"status":{"$ref":"#/components/schemas/ImageJobStatus"}}},"ImageJobResponse":{"type":"object","properties":{"jobId":{"type":"string"},"sessionId":{"type":"string","format":"uuid"},"status":{"$ref":"#/components/schemas/ImageJobStatus"},"imageUrl":{"type":"string","nullable":true},"imageId":{"type":"string","format":"uuid","nullable":true},"error":{"type":"string","nullable":true}}},"PlatformType":{"type":"string","enum":["FACEBOOK","INSTAGRAM","THREADS","TIKTOK","TWITTER","BLUESKY","LINKEDIN","PINTEREST","YOUTUBE","MASTODON","TELEGRAM","GOOGLE_BUSINESS"]},"ContentType":{"type":"string","enum":["TEXT","IMAGE","VIDEO","CAROUSEL","DOCUMENT"],"description":"`CAROUSEL` takes 2 or more items in `mediaUrls`, up to 4 on TWITTER, BLUESKY and MASTODON, 5 on PINTEREST, 10 on INSTAGRAM, FACEBOOK and THREADS, 20 on LINKEDIN and 35 on TIKTOK. A TIKTOK carousel takes JPEG or WebP photos only. INSTAGRAM and THREADS accept images and videos in the same carousel; every other platform takes images only, and YOUTUBE has no carousel. `DOCUMENT` publishes one PDF, PPT, PPTX, DOC or DOCX file (max 100 MB, 300 pages) as a LinkedIn document post, and is LinkedIn only: any other platform in the same request is rejected with a 400"},"PostStatus":{"type":"string","enum":["DRAFT","SCHEDULED","PENDING","PUBLISHING","COMPLETED","PARTIAL_FAILURE","FAILED"]},"PlatformPostStatus":{"type":"string","enum":["PENDING","PUBLISHING","PUBLISHED","FAILED"]},"WebhookEventName":{"type":"string","enum":["post.published","post.partially_failed","post.failed","post.scheduled","account.unauthorized","image.completed","image.failed"]},"Error":{"type":"object","properties":{"statusCode":{"type":"integer"},"message":{"oneOf":[{"type":"string"},{"type":"array","items":{"type":"string"}}]},"error":{"type":"string"},"code":{"type":"string","description":"Machine-readable reason. `permission_denied` (403) means the role is short of a permission, `subscription_required` (403) means the organization's plan is not active, `token_issuer_lost_access` (401) means the key is dead because its creator left the workspace, `oauth_account_not_found` (401) means the login behind an OAuth access token has no AdaptlyPost account, `insufficient_credits` (402) means the AI endpoints are out of credits for this user, `workspace_access_denied` (403) means the `X-Workspace-Id` header names a workspace the caller cannot act in"}}},"WorkspacePermission":{"type":"string","enum":["workspace.read","workspace.manage","posts.read","posts.draft","posts.others","posts.schedule","posts.publish","posts.delete","media.upload","accounts.read","accounts.manage","analytics.read","analytics.sync","ai.generate","members.read","members.manage","tokens.own","tokens.manage","webhooks.read","webhooks.manage","signature.manage","flowsery.write","redreplier.write","claw.use"]},"PermissionDeniedError":{"type":"object","required":["statusCode","error","code","requiredPermission","role","keyRole","issuerRole","tokenType","message"],"properties":{"statusCode":{"type":"integer","enum":[403]},"error":{"type":"string","enum":["Forbidden"]},"code":{"type":"string","enum":["permission_denied"]},"requiredPermission":{"$ref":"#/components/schemas/WorkspacePermission"},"role":{"type":"string","description":"The role the message names: the power actually in force. It never holds requiredPermission. When the key was issued by a member who has since been demoted, this is the issuer’s role, not the key’s"},"keyRole":{"type":"string","nullable":true,"description":"The workspace role recorded on the key. Null when the caller is a session"},"issuerRole":{"type":"string","nullable":true,"description":"The current workspace role of the member who issued the key, which caps what the key can do. Null when the caller is a session"},"tokenType":{"type":"string","enum":["api_token","oauth"]},"message":{"type":"string","description":"What the role cannot do, and what to do instead. Stop on this error: another key or a retry will not help"}}},"WorkspaceList":{"type":"object","required":["workspaces"],"properties":{"workspaces":{"type":"array","items":{"$ref":"#/components/schemas/Workspace"}}}},"Workspace":{"type":"object","required":["id","name","organization","role","permissions","isDefault","current","can"],"properties":{"id":{"type":"string","description":"Send it in the `X-Workspace-Id` header"},"name":{"type":"string"},"organization":{"type":"object","required":["id","name"],"properties":{"id":{"type":"string"},"name":{"type":"string"}}},"role":{"type":"object","required":["key","name"],"properties":{"key":{"type":"string"},"name":{"type":"string"}}},"permissions":{"type":"array","items":{"type":"string","enum":["workspace.read","workspace.manage","posts.read","posts.draft","posts.others","posts.schedule","posts.publish","posts.delete","media.upload","accounts.read","accounts.manage","analytics.read","analytics.sync","ai.generate","members.read","members.manage","tokens.own","tokens.manage","webhooks.read","webhooks.manage","signature.manage","flowsery.write","redreplier.write","claw.use"]}},"isDefault":{"type":"boolean"},"current":{"type":"boolean","description":"True for the workspace this call landed in"},"can":{"type":"object","required":["draft","schedule","publish"],"properties":{"draft":{"type":"boolean"},"schedule":{"type":"boolean"},"publish":{"type":"boolean"}}}}},"Me":{"type":"object","required":["tokenType","workspace","organizationId","role","permissions","can","summary"],"properties":{"tokenType":{"type":"string","enum":["api_token","oauth"]},"tokenId":{"type":"string","nullable":true},"tokenName":{"type":"string","nullable":true},"workspace":{"type":"object","required":["id"],"properties":{"id":{"type":"string"}}},"organizationId":{"type":"string"},"role":{"type":"object","required":["key","name"],"properties":{"key":{"type":"string"},"name":{"type":"string"}}},"issuerRole":{"type":"string","nullable":true,"description":"The workspace role of the member who created the key. The key's own permissions never exceed it"},"permissions":{"type":"array","items":{"$ref":"#/components/schemas/WorkspacePermission"}},"can":{"type":"object","required":["draft","schedule","publish"],"properties":{"draft":{"type":"boolean"},"schedule":{"type":"boolean"},"publish":{"type":"boolean"}}},"summary":{"type":"string","description":"One sentence naming the role and what it can do"},"expiresAt":{"type":"string","format":"date-time","nullable":true,"description":"When the key stops working. Null means it never expires"}}},"PlatformText":{"type":"object","required":["platform","text"],"properties":{"platform":{"$ref":"#/components/schemas/PlatformType"},"text":{"type":"string","description":"On LinkedIn, mention a company page with @[Page Name](urn:li:organization:<id>)"}}},"PlatformAspectRatio":{"type":"object","required":["platform","aspectRatio"],"properties":{"platform":{"$ref":"#/components/schemas/PlatformType"},"aspectRatio":{"type":"string","enum":["SQUARE","PORTRAIT","VERTICAL","LANDSCAPE"],"description":"`SQUARE` is 1:1, `PORTRAIT` 4:5, `VERTICAL` 9:16 and `LANDSCAPE` 1.91:1"},"fit":{"type":"string","enum":["CROP","PAD"],"description":"How an image of another shape reaches the ratio. `CROP` (the default) cuts it from the center; `PAD` keeps the whole image and fills the rest with a blurred copy of it"}}},"PlatformThumbnail":{"type":"object","required":["platform"],"properties":{"platform":{"$ref":"#/components/schemas/PlatformType"},"thumbnailUrl":{"type":"string","description":"The image to use as this platform's thumbnail, an https URL or an image data URI"},"thumbnailTimestampMs":{"type":"integer","minimum":0,"description":"The video frame (ms from the start) to use as this platform's thumbnail"}}},"PlatformFirstComment":{"type":"object","required":["platform","text"],"properties":{"platform":{"$ref":"#/components/schemas/PlatformType"},"text":{"type":"string","maxLength":10000,"description":"The comment's text, trimmed. The limit depends on the platform, see `firstComments`"}}},"FirstCommentStatus":{"type":"string","enum":["PENDING","PUBLISHED","FAILED"]},"PinterestPostConfig":{"type":"object","required":["connectionId"],"properties":{"connectionId":{"type":"string"},"boardId":{"type":"string","description":"A board `id` from `GET /social-accounts/{id}/pinterest-boards`. Leave it out to pin to a public board named AdaptlyPost, created on the account the first time it is needed"},"title":{"type":"string","maxLength":100},"link":{"type":"string","format":"uri"}}},"PinterestBoard":{"type":"object","properties":{"id":{"type":"string","description":"Pass as `pinterestConfigs[].boardId`"},"name":{"type":"string"},"description":{"type":"string"},"privacy":{"type":"string","description":"`PUBLIC`, `PROTECTED` or `SECRET`, as Pinterest reports it"},"pinCount":{"type":"integer"}}},"TikTokPostConfig":{"type":"object","required":["connectionId","privacyLevel"],"properties":{"connectionId":{"type":"string"},"title":{"type":"string","maxLength":90},"caption":{"type":"string","maxLength":2200},"privacyLevel":{"type":"string","enum":["PUBLIC_TO_EVERYONE","MUTUAL_FOLLOW_FRIENDS","FOLLOWER_OF_CREATOR","SELF_ONLY"]},"allowComments":{"type":"boolean"},"allowDuet":{"type":"boolean"},"allowStitch":{"type":"boolean"},"sendAsDraft":{"type":"boolean"},"aiGenerated":{"type":"boolean"},"brandedContent":{"type":"boolean"},"brandedContentOwnBrand":{"type":"boolean"},"autoAddMusic":{"type":"boolean"}}},"InstagramPostConfig":{"type":"object","required":["connectionId"],"properties":{"connectionId":{"type":"string"},"postType":{"type":"string","enum":["FEED","REEL","STORY"],"description":"Where the post is published. `FEED` works with every content type, `REEL` needs `contentType: VIDEO`, and `STORY` needs `IMAGE`, `VIDEO` or `CAROUSEL`. A `CAROUSEL` story publishes each media item as its own story, in order. Any other combination is rejected with a 400."},"shareToStory":{"type":"boolean","description":"Also publish the same media as a story on this account, next to the `FEED` or `REEL` post. The story shows as its own entry in `platforms` with `postType: STORY`, so it succeeds, fails and retries on its own. It skips the caption, first comment, cover, collaborators, trial and audio, needs `IMAGE`, `VIDEO` or `CAROUSEL`, and a video must fit the story length limit (60 seconds). Ignored when `postType` is `STORY`."},"trialGraduation":{"type":"string","enum":["MANUAL","SS_PERFORMANCE"],"description":"Publish the reel as a trial reel: Instagram shows it to non-followers first, and it stays off your followers' feeds and your profile grid until it is shared to them. `MANUAL` means you share it to followers yourself in the Instagram app; `SS_PERFORMANCE` means Instagram shares it automatically if it performs well. Omit it for a normal reel. Needs `contentType: VIDEO` and a `postType` of `REEL`, `FEED` or none; images, carousels and stories are rejected with a 400. Instagram decides which professional accounts can post trial reels and caps how many go out through the API; a refused trial fails that platform with an error saying so."},"collaborators":{"type":"array","maxItems":3,"items":{"type":"string","pattern":"^@?[A-Za-z0-9._]{1,30}$"},"description":"Instagram usernames invited as collaborators (Instagram Collab), up to 3. A leading `@` is stripped and duplicates are dropped. Works on feed images, carousels and reels; a story or a trial reel with collaborators is rejected with a 400. Each collaborator gets an invite in Instagram and must accept it before the post shows on their profile."},"audio":{"$ref":"#/components/schemas/InstagramAudioConfig"}}},"InstagramAudioConfig":{"type":"object","required":["audioId"],"description":"Music or an original sound from Instagram's audio library, added to the Reel when it is published. Needs `contentType: VIDEO` and a `postType` other than `STORY`, and only works for accounts with `supportsInstagramAudio: true` (connected through Facebook). Find ids with `GET /social-accounts/{id}/instagram-audio`.","properties":{"audioId":{"type":"string"},"title":{"type":"string","maxLength":255,"description":"Track title, stored for display only"},"artist":{"type":"string","maxLength":255,"description":"Artist or creator, stored for display only"},"audioVolume":{"type":"integer","minimum":1,"maximum":100,"description":"Volume of the added audio. Defaults to 100"},"videoVolume":{"type":"integer","minimum":1,"maximum":100,"description":"Volume of the video's own sound. Defaults to 100"}}},"InstagramAudio":{"type":"object","properties":{"audioId":{"type":"string","description":"Pass as `instagramConfigs[].audio.audioId`"},"audioType":{"type":"string","enum":["music","original_sound"]},"title":{"type":"string"},"displayArtist":{"type":"string","description":"Music only"},"igUsername":{"type":"string","description":"Original sounds only"},"durationMs":{"type":"integer"},"coverArtworkUrl":{"type":"string"},"profilePictureUrl":{"type":"string"},"previewUrl":{"type":"string","description":"Temporary audio file URL, expires after about 1.5 days"},"instagramUrl":{"type":"string"},"isAdsEligible":{"type":"boolean"}}},"FacebookPostConfig":{"type":"object","required":["pageId"],"properties":{"pageId":{"type":"string"},"postType":{"type":"string","enum":["FEED","REEL","STORY"],"description":"Where the post is published. `FEED` works with every content type, `REEL` needs `contentType: VIDEO`, and `STORY` needs `IMAGE`, `VIDEO` or `CAROUSEL`. A `CAROUSEL` story publishes each media item as its own story, in order. Any other combination is rejected with a 400."},"shareToStory":{"type":"boolean","description":"Also publish the same media as a story on this account, next to the `FEED` or `REEL` post. The story shows as its own entry in `platforms` with `postType: STORY`, so it succeeds, fails and retries on its own. It skips the caption, video title and text background, needs `IMAGE`, `VIDEO` or `CAROUSEL`, and a video must fit the story length limit (60 seconds). Ignored when `postType` is `STORY`."},"videoTitle":{"type":"string","maxLength":255},"textFormatPresetId":{"type":"string","pattern":"^\\d{1,20}$","example":"106018623298955","description":"Facebook's id for a colored text background (for example `106018623298955` is solid purple, `249307305544279` is a red to blue gradient). Only for `contentType: TEXT` posts with no media and text up to 130 characters; anything else is rejected with a 400. Links in the text show without a preview. If Facebook refuses the background, the post goes out as plain text."}}},"LinkedInPostConfig":{"type":"object","required":["connectionId"],"properties":{"connectionId":{"type":"string"},"documentTitle":{"type":"string","maxLength":100,"description":"Title LinkedIn shows on a `DOCUMENT` post. Defaults to the uploaded file name. Ignored for every other content type"}}},"YouTubePostConfig":{"type":"object","required":["connectionId"],"properties":{"connectionId":{"type":"string"},"postType":{"type":"string","enum":["VIDEO","SHORTS"]},"videoTitle":{"type":"string","maxLength":100},"tags":{"type":"array","items":{"type":"string"},"maxItems":20},"privacyStatus":{"type":"string","enum":["public","private","unlisted"]},"license":{"type":"string","enum":["youtube","creativeCommon"]},"notifySubscribers":{"type":"boolean"},"allowEmbedding":{"type":"boolean"},"madeForKids":{"type":"boolean"},"categoryId":{"type":"string"},"playlistId":{"type":"string"}}},"GoogleBusinessPostConfig":{"type":"object","required":["connectionId","topicType"],"description":"Options for one Google Business Profile location. Google Business Profile takes `contentType` `TEXT` or `IMAGE` with one JPEG or PNG image up to 5 MB, and text up to 1500 characters","properties":{"connectionId":{"type":"string"},"topicType":{"type":"string","enum":["STANDARD","EVENT","OFFER"],"description":"`STANDARD` is an update. `EVENT` and `OFFER` need `eventTitle`, `eventStart` and `eventEnd`"},"callToActionType":{"type":"string","enum":["BOOK","ORDER","SHOP","LEARN_MORE","SIGN_UP","CALL"],"description":"Button shown on the post. Every type except `CALL` needs `callToActionUrl`; `CALL` dials the phone number on the Business Profile"},"callToActionUrl":{"type":"string","format":"uri","description":"Ignored when `callToActionType` is `CALL`"},"eventTitle":{"type":"string"},"eventStart":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2})?$","example":"2026-10-01T18:00","description":"`YYYY-MM-DD` or `YYYY-MM-DDTHH:mm` in the business's local time, with no offset. Google reads event times in the location's time zone"},"eventEnd":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2})?$","example":"2026-10-01T21:00","description":"`YYYY-MM-DD` or `YYYY-MM-DDTHH:mm` in the business's local time, with no offset"},"offerCouponCode":{"type":"string"},"offerRedeemUrl":{"type":"string","format":"uri"},"offerTerms":{"type":"string"}}},"CreateSocialPostRequest":{"type":"object","required":["platforms","contentType","timezone"],"properties":{"platforms":{"type":"array","minItems":1,"items":{"$ref":"#/components/schemas/PlatformType"},"description":"Platforms to post to. Each one also needs its connection ids. TELEGRAM has no connection ids in the API, so Telegram posts can only be created in the app"},"contentType":{"$ref":"#/components/schemas/ContentType"},"text":{"type":"string","description":"Post text/caption for all platforms. On LinkedIn, mention a company page with @[Page Name](urn:li:organization:<id>), using the page name exactly as LinkedIn shows it"},"platformTexts":{"type":"array","items":{"$ref":"#/components/schemas/PlatformText"},"description":"Platform-specific text overrides"},"platformAspectRatios":{"type":"array","items":{"$ref":"#/components/schemas/PlatformAspectRatio"},"description":"Reframe every image of an `IMAGE` or `CAROUSEL` post to one shape on a platform, one entry per platform. INSTAGRAM takes `SQUARE`, `PORTRAIT` or `LANDSCAPE` (Instagram refuses 9:16 feed and carousel images); TIKTOK takes all four. Other platforms, video posts, Instagram stories and a second entry for the same platform are rejected with a 400. Omit a platform to keep its images as uploaded. On an update it applies to the `mediaUrls` sent in the same request"},"mediaUrls":{"type":"array","items":{"type":"string"},"description":"Public HTTPS URLs of media to attach. Use POST /upload-urls to upload files first. A `DOCUMENT` post takes exactly one PDF, PPT, PPTX, DOC or DOCX URL, and a document file is only accepted on a `DOCUMENT` post. One uploaded URL may be reused across any number of posts; the file is kept until the last post referencing it has published. After publishing, the post's `mediaUrls` may hold expiring platform CDN links, so reuse the `publicUrl` you uploaded, not a URL read back from a published post"},"mediaAltTexts":{"type":"array","items":{"type":"string"},"description":"Alt text for each image, in the same order as mediaUrls (max 1000 characters each, empty string to skip one). Sent to X, Bluesky, Mastodon, LinkedIn, Facebook, Instagram, Threads and Pinterest (first image only, max 500 characters). TikTok, YouTube and Google Business Profile have no alt text field"},"thumbnailUrl":{"type":"string","description":"Video thumbnail as an https URL or an image data URI, used on every platform that takes one unless `platformThumbnails` overrides it"},"thumbnailTimestampMs":{"type":"number","description":"Video frame timestamp (ms) to use as the thumbnail, used by INSTAGRAM, PINTEREST and TIKTOK unless `platformThumbnails` overrides it"},"platformThumbnails":{"type":"array","items":{"$ref":"#/components/schemas/PlatformThumbnail"},"description":"A different thumbnail for one platform of a `VIDEO` post, one entry per platform. A platform without an entry uses the post-level `thumbnailUrl` and `thumbnailTimestampMs`. YOUTUBE, FACEBOOK and LINKEDIN take an image (`thumbnailUrl`); INSTAGRAM and PINTEREST take an image or a frame (`thumbnailTimestampMs`), the image winning when both are sent; TIKTOK takes only a frame. Other platforms, posts that are not videos, Facebook and Instagram stories, a second entry for one platform and an entry the platform cannot use are rejected with a 400. On an update, sending the array replaces every override and omitting it keeps the stored ones"},"firstComments":{"type":"array","items":{"$ref":"#/components/schemas/PlatformFirstComment"},"description":"A comment posted from the same account right after the post goes out on a platform, one entry per platform. Available on LINKEDIN (1250 characters), TWITTER (280, as a reply), THREADS (500, as a reply), BLUESKY (300, as a reply), MASTODON (500, as a reply) and YOUTUBE (10000). Other platforms, a platform the post does not go to, an empty or too long comment, a second entry for one platform, a private or made for kids YouTube video and Facebook or Instagram stories are rejected with a 400. The comment is posted about 10 seconds after the platform publishes (60 on YouTube) and retried up to 3 times; on X it is not retried, and a lifetime-deal account pays for it like any X post. A comment that fails never fails the post: read `firstCommentStatus` and `firstCommentError` on the platform. On an update, sending the array replaces every first comment and omitting it keeps the stored ones"},"scheduledAt":{"type":"string","format":"date-time","description":"ISO 8601 date-time. Future date schedules the post; omit to publish immediately"},"timezone":{"type":"string","example":"UTC"},"saveAsDraft":{"type":"boolean"},"recurrence":{"$ref":"#/components/schemas/Recurrence"},"pageIds":{"type":"array","items":{"type":"string"},"description":"Facebook pages to post to. Accepts either the account `id` or the `pageId` returned by /social-accounts"},"tiktokConnectionIds":{"type":"array","items":{"type":"string"}},"threadsConnectionIds":{"type":"array","items":{"type":"string"}},"instagramConnectionIds":{"type":"array","items":{"type":"string"}},"twitterConnectionIds":{"type":"array","items":{"type":"string"}},"blueskyConnectionIds":{"type":"array","items":{"type":"string"}},"mastodonConnectionIds":{"type":"array","items":{"type":"string"}},"googleBusinessConnectionIds":{"type":"array","items":{"type":"string"},"description":"Google Business Profile locations to post to, by the account `id` from /social-accounts. A location without an entry in `googleBusinessConfigs` gets a `STANDARD` update"},"linkedinConnectionIds":{"type":"array","items":{"type":"string"}},"pinterestConnectionIds":{"type":"array","items":{"type":"string"}},"youtubeConnectionIds":{"type":"array","items":{"type":"string"}},"twitterKeepLinks":{"type":"boolean","description":"Keep links in the X text clickable. X charges over 10 times more for a post with a link, so by default we break every link in X text (`example.com` becomes `example. com`). Only AppSumo lifetime deals can turn this on, and a kept link costs 20 credits instead of 2. `true` on any other plan returns a 400; it is ignored when the X text has no link"},"pinterestConfigs":{"type":"array","items":{"$ref":"#/components/schemas/PinterestPostConfig"}},"tiktokConfigs":{"type":"array","items":{"$ref":"#/components/schemas/TikTokPostConfig"}},"instagramConfigs":{"type":"array","items":{"$ref":"#/components/schemas/InstagramPostConfig"}},"facebookConfigs":{"type":"array","items":{"$ref":"#/components/schemas/FacebookPostConfig"}},"youtubeConfigs":{"type":"array","items":{"$ref":"#/components/schemas/YouTubePostConfig"}},"googleBusinessConfigs":{"type":"array","items":{"$ref":"#/components/schemas/GoogleBusinessPostConfig"}},"linkedinConfigs":{"type":"array","items":{"$ref":"#/components/schemas/LinkedInPostConfig"}}}},"Recurrence":{"type":"object","required":["frequency"],"description":"Repeats the post. Requires a future `scheduledAt`, which becomes the first post and sets the time of day. Cannot be combined with `saveAsDraft` or TIKTOK","properties":{"frequency":{"type":"string","enum":["DAILY","WEEKLY","MONTHLY","YEARLY"]},"interval":{"type":"integer","minimum":1,"maximum":30,"default":1,"description":"Repeat every N days, weeks, months or years"},"weekdays":{"type":"array","items":{"type":"string","enum":["MONDAY","TUESDAY","WEDNESDAY","THURSDAY","FRIDAY","SATURDAY","SUNDAY"]},"description":"WEEKLY only. The weekday of `scheduledAt` is always included"},"endsOn":{"type":"string","format":"date","description":"Last day an occurrence may go out on. Cannot be combined with `maxOccurrences`"},"maxOccurrences":{"type":"integer","minimum":2,"maximum":365,"description":"Total number of posts the series publishes"}}},"RecurringPost":{"type":"object","properties":{"id":{"type":"string"},"userId":{"type":"string"},"status":{"type":"string","enum":["ACTIVE","PAUSED","ENDED"]},"pauseReason":{"type":"string","enum":["USER","CONSECUTIVE_FAILURES","SUBSCRIPTION_INACTIVE","ACCESS_LOST","CONNECTION_REMOVED","INVALID_CONTENT"]},"lastError":{"type":"string"},"frequency":{"type":"string","enum":["DAILY","WEEKLY","MONTHLY","YEARLY"]},"interval":{"type":"integer"},"weekdays":{"type":"array","items":{"type":"string","enum":["MONDAY","TUESDAY","WEDNESDAY","THURSDAY","FRIDAY","SATURDAY","SUNDAY"]}},"startsAt":{"type":"string","format":"date-time"},"timezone":{"type":"string"},"endsOn":{"type":"string","format":"date-time"},"maxOccurrences":{"type":"integer"},"nextOccurrenceAt":{"type":"string","format":"date-time","description":"The next occurrence that does not exist as a post yet. Absent once the series has ended"},"occurrenceCount":{"type":"integer","description":"Posts created by the series so far"},"contentType":{"$ref":"#/components/schemas/ContentType"},"text":{"type":"string"},"mediaUrls":{"type":"array","items":{"type":"string"}},"mediaAltTexts":{"type":"array","items":{"type":"string"}},"thumbnailUrl":{"type":"string"},"thumbnailTimestampMs":{"type":"number","description":"Video frame timestamp (ms) used as the thumbnail"},"platformTypes":{"type":"array","items":{"$ref":"#/components/schemas/PlatformType"}},"platforms":{"type":"array","items":{"$ref":"#/components/schemas/PostPlatform"}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}}},"UpdateSocialPostRequest":{"type":"object","properties":{"platforms":{"type":"array","items":{"$ref":"#/components/schemas/PlatformType"}},"contentType":{"$ref":"#/components/schemas/ContentType"},"text":{"type":"string","description":"Post text/caption for all platforms. On LinkedIn, mention a company page with @[Page Name](urn:li:organization:<id>), using the page name exactly as LinkedIn shows it"},"platformTexts":{"type":"array","items":{"$ref":"#/components/schemas/PlatformText"},"description":"Platform-specific text overrides"},"platformAspectRatios":{"type":"array","items":{"$ref":"#/components/schemas/PlatformAspectRatio"},"description":"Reframe every image of an `IMAGE` or `CAROUSEL` post to one shape on a platform, one entry per platform. INSTAGRAM takes `SQUARE`, `PORTRAIT` or `LANDSCAPE` (Instagram refuses 9:16 feed and carousel images); TIKTOK takes all four. Other platforms, video posts, Instagram stories and a second entry for the same platform are rejected with a 400. Omit a platform to keep its images as uploaded. On an update it applies to the `mediaUrls` sent in the same request"},"mediaUrls":{"type":"array","items":{"type":"string"},"description":"Public HTTPS URLs of media to attach. Use POST /upload-urls to upload files first. A `DOCUMENT` post takes exactly one PDF, PPT, PPTX, DOC or DOCX URL, and a document file is only accepted on a `DOCUMENT` post. One uploaded URL may be reused across any number of posts; the file is kept until the last post referencing it has published. After publishing, the post's `mediaUrls` may hold expiring platform CDN links, so reuse the `publicUrl` you uploaded, not a URL read back from a published post"},"mediaAltTexts":{"type":"array","items":{"type":"string"},"description":"Alt text for each image, in the same order as mediaUrls (max 1000 characters each, empty string to skip one). Sent to X, Bluesky, Mastodon, LinkedIn, Facebook, Instagram, Threads and Pinterest (first image only, max 500 characters). TikTok, YouTube and Google Business Profile have no alt text field"},"thumbnailUrl":{"type":"string","description":"Video thumbnail as an https URL or an image data URI, used on every platform that takes one unless `platformThumbnails` overrides it"},"thumbnailTimestampMs":{"type":"number","description":"Video frame timestamp (ms) to use as the thumbnail, used by INSTAGRAM, PINTEREST and TIKTOK unless `platformThumbnails` overrides it"},"platformThumbnails":{"type":"array","items":{"$ref":"#/components/schemas/PlatformThumbnail"},"description":"A different thumbnail for one platform of a `VIDEO` post, one entry per platform. A platform without an entry uses the post-level `thumbnailUrl` and `thumbnailTimestampMs`. YOUTUBE, FACEBOOK and LINKEDIN take an image (`thumbnailUrl`); INSTAGRAM and PINTEREST take an image or a frame (`thumbnailTimestampMs`), the image winning when both are sent; TIKTOK takes only a frame. Other platforms, posts that are not videos, Facebook and Instagram stories, a second entry for one platform and an entry the platform cannot use are rejected with a 400. On an update, sending the array replaces every override and omitting it keeps the stored ones"},"firstComments":{"type":"array","items":{"$ref":"#/components/schemas/PlatformFirstComment"},"description":"A comment posted from the same account right after the post goes out on a platform, one entry per platform. Available on LINKEDIN (1250 characters), TWITTER (280, as a reply), THREADS (500, as a reply), BLUESKY (300, as a reply), MASTODON (500, as a reply) and YOUTUBE (10000). Other platforms, a platform the post does not go to, an empty or too long comment, a second entry for one platform, a private or made for kids YouTube video and Facebook or Instagram stories are rejected with a 400. The comment is posted about 10 seconds after the platform publishes (60 on YouTube) and retried up to 3 times; on X it is not retried, and a lifetime-deal account pays for it like any X post. A comment that fails never fails the post: read `firstCommentStatus` and `firstCommentError` on the platform. On an update, sending the array replaces every first comment and omitting it keeps the stored ones"},"scheduledAt":{"type":"string","format":"date-time","description":"New schedule time (ISO 8601). Moving a SCHEDULED post to a time in the past returns 400; to publish it now, call `POST /social-posts/{id}/publish` without `scheduledAt`"},"timezone":{"type":"string"},"pageIds":{"type":"array","items":{"type":"string"},"description":"Facebook pages to post to. Accepts either the account `id` or the `pageId` returned by /social-accounts"},"tiktokConnectionIds":{"type":"array","items":{"type":"string"}},"threadsConnectionIds":{"type":"array","items":{"type":"string"}},"instagramConnectionIds":{"type":"array","items":{"type":"string"}},"twitterConnectionIds":{"type":"array","items":{"type":"string"}},"blueskyConnectionIds":{"type":"array","items":{"type":"string"}},"mastodonConnectionIds":{"type":"array","items":{"type":"string"}},"googleBusinessConnectionIds":{"type":"array","items":{"type":"string"},"description":"Google Business Profile locations to post to, by the account `id` from /social-accounts. A location without an entry in `googleBusinessConfigs` gets a `STANDARD` update"},"linkedinConnectionIds":{"type":"array","items":{"type":"string"}},"pinterestConnectionIds":{"type":"array","items":{"type":"string"}},"youtubeConnectionIds":{"type":"array","items":{"type":"string"}},"twitterKeepLinks":{"type":"boolean","description":"Keep links in the X text clickable. X charges over 10 times more for a post with a link, so by default we break every link in X text (`example.com` becomes `example. com`). Only AppSumo lifetime deals can turn this on, and a kept link costs 20 credits instead of 2. `true` on any other plan returns a 400; it is ignored when the X text has no link"},"pinterestConfigs":{"type":"array","items":{"$ref":"#/components/schemas/PinterestPostConfig"}},"tiktokConfigs":{"type":"array","items":{"$ref":"#/components/schemas/TikTokPostConfig"}},"instagramConfigs":{"type":"array","items":{"$ref":"#/components/schemas/InstagramPostConfig"}},"facebookConfigs":{"type":"array","items":{"$ref":"#/components/schemas/FacebookPostConfig"}},"youtubeConfigs":{"type":"array","items":{"$ref":"#/components/schemas/YouTubePostConfig"}},"googleBusinessConfigs":{"type":"array","items":{"$ref":"#/components/schemas/GoogleBusinessPostConfig"}},"linkedinConfigs":{"type":"array","items":{"$ref":"#/components/schemas/LinkedInPostConfig"}}}},"BulkPostItem":{"type":"object","required":["contentType","scheduledAt"],"properties":{"contentType":{"type":"string","enum":["TEXT","IMAGE","VIDEO","CAROUSEL"],"description":"`DOCUMENT` posts cannot be bulk scheduled; create them one at a time with `POST /social-posts`"},"text":{"type":"string","description":"Post text/caption for all platforms. On LinkedIn, mention a company page with @[Page Name](urn:li:organization:<id>), using the page name exactly as LinkedIn shows it"},"platformTexts":{"type":"array","items":{"$ref":"#/components/schemas/PlatformText"},"description":"Platform-specific text overrides"},"platformAspectRatios":{"type":"array","items":{"$ref":"#/components/schemas/PlatformAspectRatio"},"description":"Reframe every image of an `IMAGE` or `CAROUSEL` post to one shape on a platform, one entry per platform. INSTAGRAM takes `SQUARE`, `PORTRAIT` or `LANDSCAPE` (Instagram refuses 9:16 feed and carousel images); TIKTOK takes all four. Other platforms, video posts, Instagram stories and a second entry for the same platform are rejected with a 400. Omit a platform to keep its images as uploaded. On an update it applies to the `mediaUrls` sent in the same request"},"mediaUrls":{"type":"array","items":{"type":"string"},"description":"Public HTTPS URLs of media to attach. Use POST /upload-urls to upload files first. A `DOCUMENT` post takes exactly one PDF, PPT, PPTX, DOC or DOCX URL, and a document file is only accepted on a `DOCUMENT` post. One uploaded URL may be reused across any number of posts; the file is kept until the last post referencing it has published. After publishing, the post's `mediaUrls` may hold expiring platform CDN links, so reuse the `publicUrl` you uploaded, not a URL read back from a published post"},"mediaAltTexts":{"type":"array","items":{"type":"string"},"description":"Alt text for each image, in the same order as mediaUrls (max 1000 characters each, empty string to skip one). Sent to X, Bluesky, Mastodon, LinkedIn, Facebook, Instagram, Threads and Pinterest (first image only, max 500 characters). TikTok, YouTube and Google Business Profile have no alt text field"},"thumbnailUrl":{"type":"string","description":"Video thumbnail as an https URL or an image data URI, used on every platform that takes one unless `platformThumbnails` overrides it"},"thumbnailTimestampMs":{"type":"number","description":"Video frame timestamp (ms) to use as the thumbnail, used by INSTAGRAM, PINTEREST and TIKTOK unless `platformThumbnails` overrides it"},"platformThumbnails":{"type":"array","items":{"$ref":"#/components/schemas/PlatformThumbnail"},"description":"A different thumbnail for one platform of a `VIDEO` post, one entry per platform. A platform without an entry uses the post-level `thumbnailUrl` and `thumbnailTimestampMs`. YOUTUBE, FACEBOOK and LINKEDIN take an image (`thumbnailUrl`); INSTAGRAM and PINTEREST take an image or a frame (`thumbnailTimestampMs`), the image winning when both are sent; TIKTOK takes only a frame. Other platforms, posts that are not videos, Facebook and Instagram stories, a second entry for one platform and an entry the platform cannot use are rejected with a 400. On an update, sending the array replaces every override and omitting it keeps the stored ones"},"firstComments":{"type":"array","items":{"$ref":"#/components/schemas/PlatformFirstComment"},"description":"A comment posted from the same account right after the post goes out on a platform, one entry per platform. Available on LINKEDIN (1250 characters), TWITTER (280, as a reply), THREADS (500, as a reply), BLUESKY (300, as a reply), MASTODON (500, as a reply) and YOUTUBE (10000). Other platforms, a platform the post does not go to, an empty or too long comment, a second entry for one platform, a private or made for kids YouTube video and Facebook or Instagram stories are rejected with a 400. The comment is posted about 10 seconds after the platform publishes (60 on YouTube) and retried up to 3 times; on X it is not retried, and a lifetime-deal account pays for it like any X post. A comment that fails never fails the post: read `firstCommentStatus` and `firstCommentError` on the platform. On an update, sending the array replaces every first comment and omitting it keeps the stored ones"},"scheduledAt":{"type":"string","format":"date-time"},"pinterestConfigs":{"type":"array","items":{"$ref":"#/components/schemas/PinterestPostConfig"}},"tiktokConfigs":{"type":"array","items":{"$ref":"#/components/schemas/TikTokPostConfig"}},"instagramConfigs":{"type":"array","items":{"$ref":"#/components/schemas/InstagramPostConfig"}},"facebookConfigs":{"type":"array","items":{"$ref":"#/components/schemas/FacebookPostConfig"}},"youtubeConfigs":{"type":"array","items":{"$ref":"#/components/schemas/YouTubePostConfig"}},"googleBusinessConfigs":{"type":"array","items":{"$ref":"#/components/schemas/GoogleBusinessPostConfig"}}}},"BulkSchedulePostsRequest":{"type":"object","required":["platforms","timezone","posts"],"properties":{"platforms":{"type":"array","minItems":1,"items":{"$ref":"#/components/schemas/PlatformType"},"description":"Platforms to post to. Each one also needs its connection ids. TELEGRAM has no connection ids in the API, so Telegram posts can only be created in the app"},"timezone":{"type":"string","example":"UTC"},"posts":{"type":"array","minItems":1,"maxItems":100,"items":{"$ref":"#/components/schemas/BulkPostItem"}},"pageIds":{"type":"array","items":{"type":"string"},"description":"Facebook pages to post to. Accepts either the account `id` or the `pageId` returned by /social-accounts"},"tiktokConnectionIds":{"type":"array","items":{"type":"string"}},"threadsConnectionIds":{"type":"array","items":{"type":"string"}},"instagramConnectionIds":{"type":"array","items":{"type":"string"}},"twitterConnectionIds":{"type":"array","items":{"type":"string"}},"blueskyConnectionIds":{"type":"array","items":{"type":"string"}},"mastodonConnectionIds":{"type":"array","items":{"type":"string"}},"googleBusinessConnectionIds":{"type":"array","items":{"type":"string"},"description":"Google Business Profile locations to post to, by the account `id` from /social-accounts. A location without an entry in `googleBusinessConfigs` gets a `STANDARD` update"},"linkedinConnectionIds":{"type":"array","items":{"type":"string"}},"pinterestConnectionIds":{"type":"array","items":{"type":"string"}},"youtubeConnectionIds":{"type":"array","items":{"type":"string"}},"pinterestConfigs":{"type":"array","items":{"$ref":"#/components/schemas/PinterestPostConfig"}},"tiktokConfigs":{"type":"array","items":{"$ref":"#/components/schemas/TikTokPostConfig"}},"instagramConfigs":{"type":"array","items":{"$ref":"#/components/schemas/InstagramPostConfig"}},"facebookConfigs":{"type":"array","items":{"$ref":"#/components/schemas/FacebookPostConfig"}},"youtubeConfigs":{"type":"array","items":{"$ref":"#/components/schemas/YouTubePostConfig"}},"googleBusinessConfigs":{"type":"array","items":{"$ref":"#/components/schemas/GoogleBusinessPostConfig"}}}},"PublishDraftRequest":{"type":"object","required":["timezone"],"properties":{"scheduledAt":{"type":"string","format":"date-time"},"timezone":{"type":"string","example":"UTC"}}},"RetryFailedPlatformsRequest":{"type":"object","properties":{"platformIds":{"type":"array","items":{"type":"string"},"description":"PostPlatform ids from GET /social-posts/{id}/results, or platform names such as \"BLUESKY\" to retry every failed entry of that platform. Omit it, or send an empty array, to retry every failed entry. Unknown values are rejected with 400"}}},"GetUploadUrlsRequest":{"type":"object","required":["files"],"properties":{"files":{"type":"array","minItems":1,"maxItems":20,"items":{"type":"object","required":["fileName","mimeType"],"properties":{"fileName":{"type":"string"},"mimeType":{"type":"string","enum":["image/jpeg","image/png","image/webp","video/mp4","video/quicktime","application/pdf","application/vnd.ms-powerpoint","application/vnd.openxmlformats-officedocument.presentationml.presentation","application/msword","application/vnd.openxmlformats-officedocument.wordprocessingml.document"],"description":"Documents (PDF, PPT, PPTX, DOC, DOCX) are for LinkedIn `DOCUMENT` posts. Keep the file extension in `fileName`: the post reads the file type from it"}}}}}},"CreatePostResponse":{"type":"object","properties":{"postId":{"type":"string"},"queuedPlatforms":{"type":"array","items":{"$ref":"#/components/schemas/PlatformType"}},"skippedPlatforms":{"type":"array","items":{"type":"object","properties":{"platform":{"$ref":"#/components/schemas/PlatformType"},"reason":{"type":"string"}}}},"isScheduled":{"type":"boolean"},"scheduledAt":{"type":"string","format":"date-time"},"recurringPostId":{"type":"string","description":"Set when the request carried `recurrence`"}}},"PostPlatform":{"type":"object","properties":{"id":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"platform":{"$ref":"#/components/schemas/PlatformType"},"status":{"$ref":"#/components/schemas/PlatformPostStatus"},"connectionId":{"type":"string"},"pageId":{"type":"string","description":"For FACEBOOK, the Facebook page id — the same value as `pageId` on /social-accounts, and identical to `facebookPageExternalId` below. Posts created before that id was recorded return the account `id` from /social-accounts instead"},"accountName":{"type":"string"},"text":{"type":"string"},"platformPostId":{"type":"string"},"postUrl":{"type":"string","description":"Public link to the published post, set once the post is published. Instagram and Facebook stories expire after 24 hours, so for them it links to the profile or page that posted them"},"errorMessage":{"type":"string"},"publishedAt":{"type":"string","format":"date-time"},"mediaUrls":{"type":"array","items":{"type":"string"},"description":"The media as submitted. After publishing, some platforms replace these with their own CDN links, which expire, and the uploaded source files are removed. Use `previewUrls` for anything you display later"},"mediaAltTexts":{"type":"array","items":{"type":"string"}},"mediaAspectRatio":{"type":"string","enum":["SQUARE","PORTRAIT","VERTICAL","LANDSCAPE"],"description":"The aspect ratio chosen for this platform through `platformAspectRatios`; absent when the images went out as uploaded"},"mediaAspectRatioFit":{"type":"string","enum":["CROP","PAD"]},"thumbnailUrl":{"type":"string","description":"The thumbnail image set for this platform through `platformThumbnails`; absent when the platform uses the post-level thumbnail"},"thumbnailTimestampMs":{"type":"integer","description":"The video frame (ms) set for this platform through `platformThumbnails`"},"previewUrls":{"type":"array","items":{"type":"string"},"description":"Preview images we host (WebP, up to 720px), a still frame for videos, filled in shortly after publishing starts. They stay available after the source files and the platform links in mediaUrls are gone. Normally one entry per media item, where an empty string means that item could not be rendered; a post recovered after its media expired carries a single cover instead"},"firstComment":{"type":"string","description":"The first comment set through `firstComments`"},"firstCommentStatus":{"allOf":[{"$ref":"#/components/schemas/FirstCommentStatus"}],"description":"PENDING until the comment is posted, PUBLISHED once it is, FAILED when it could not be. Absent when the platform has no first comment"},"firstCommentId":{"type":"string","description":"The platform's id of the published comment"},"firstCommentError":{"type":"string","description":"Why the first comment failed"},"firstCommentPublishedAt":{"type":"string","format":"date-time"},"pinterestBoardId":{"type":"string"},"pinterestTitle":{"type":"string"},"pinterestLink":{"type":"string"},"tiktokTitle":{"type":"string"},"tiktokCaption":{"type":"string"},"tiktokPrivacyLevel":{"type":"string"},"tiktokAllowComments":{"type":"boolean"},"tiktokAllowDuet":{"type":"boolean"},"tiktokAllowStitch":{"type":"boolean"},"tiktokSendAsDraft":{"type":"boolean"},"tiktokAiGenerated":{"type":"boolean"},"tiktokBrandedContent":{"type":"boolean"},"tiktokBrandedOwnBrand":{"type":"boolean"},"tiktokAutoAddMusic":{"type":"boolean"},"tiktokDraftFallback":{"type":"boolean","description":"TikTok's daily active-user cap was hit, so the content was delivered to the TikTok inbox as a draft instead of published directly"},"instagramPostType":{"type":"string"},"instagramTrialGraduation":{"type":"string","enum":["MANUAL","SS_PERFORMANCE"],"description":"Set when this Instagram reel publishes as a trial reel; see `InstagramPostConfig.trialGraduation`"},"instagramCollaborators":{"type":"array","items":{"type":"string"},"description":"Instagram usernames invited as collaborators; see `InstagramPostConfig.collaborators`"},"instagramUsername":{"type":"string","description":"The Instagram handle the entry was published from. Set once an INSTAGRAM entry publishes"},"instagramAudioId":{"type":"string"},"instagramAudioTitle":{"type":"string"},"instagramAudioArtist":{"type":"string"},"instagramAudioVolume":{"type":"integer"},"instagramVideoVolume":{"type":"integer"},"facebookPostType":{"type":"string"},"facebookVideoTitle":{"type":"string"},"facebookTextFormatPresetId":{"type":"string","description":"The Facebook text background sent with the post; see `FacebookPostConfig.textFormatPresetId`"},"facebookPageExternalId":{"type":"string","description":"The Facebook page id this platform entry publishes to. Always set for FACEBOOK entries created since the id was recorded"},"youtubePostType":{"type":"string"},"youtubeVideoTitle":{"type":"string"},"youtubeTags":{"type":"array","items":{"type":"string"}},"youtubePrivacyStatus":{"type":"string"},"youtubeLicense":{"type":"string"},"youtubeNotifySubscribers":{"type":"boolean"},"youtubeAllowEmbedding":{"type":"boolean"},"youtubeMadeForKids":{"type":"boolean"},"youtubeCategoryId":{"type":"string"},"youtubePlaylistId":{"type":"string"},"linkedinDocumentTitle":{"type":"string","description":"The title set for a LinkedIn `DOCUMENT` post, when one was given"},"twitterKeepLinks":{"type":"boolean","description":"Whether links in this X post stay clickable. Only honored for AppSumo lifetime deals"},"googleBusinessTopicType":{"type":"string"},"googleBusinessCallToActionType":{"type":"string"},"googleBusinessCallToActionUrl":{"type":"string"},"googleBusinessEventTitle":{"type":"string"},"googleBusinessEventStart":{"type":"string"},"googleBusinessEventEnd":{"type":"string"},"googleBusinessOfferCouponCode":{"type":"string"},"googleBusinessOfferRedeemUrl":{"type":"string"},"googleBusinessOfferTerms":{"type":"string"}}},"Post":{"type":"object","properties":{"id":{"type":"string"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"userId":{"type":"string"},"contentType":{"$ref":"#/components/schemas/ContentType"},"text":{"type":"string"},"scheduledAt":{"type":"string","format":"date-time"},"timezone":{"type":"string"},"status":{"$ref":"#/components/schemas/PostStatus"},"thumbnailUrl":{"type":"string"},"thumbnailTimestampMs":{"type":"number","description":"Video frame timestamp (ms) used as the thumbnail"},"mediaUrls":{"type":"array","items":{"type":"string"},"description":"The media as submitted, shared by every platform entry. See `mediaUrls` on PostPlatform for how these change after publishing"},"mediaAltTexts":{"type":"array","items":{"type":"string"}},"platforms":{"type":"array","items":{"$ref":"#/components/schemas/PostPlatform"}},"recurringPostId":{"type":"string","description":"The recurring post this post is an occurrence of"},"occurrenceAt":{"type":"string","format":"date-time","description":"The slot of the series this post fills. Stays the same when the post is rescheduled"}}},"SocialAccount":{"type":"object","properties":{"id":{"type":"string","description":"The connection id to pass in the platform's `*ConnectionIds` array. For FACEBOOK this identifies the connected page and is not the Facebook page id — see `pageId`"},"platform":{"$ref":"#/components/schemas/PlatformType"},"displayName":{"type":"string"},"username":{"type":"string"},"avatarUrl":{"type":"string"},"status":{"$ref":"#/components/schemas/SocialAccountStatus"},"unauthorizedReason":{"type":"string","description":"The platform's own rejection message. Present only while `status` is `unauthorized`"},"pageId":{"type":"string","description":"The Facebook page id. Present for FACEBOOK page accounts — accepted in `pageIds` (as is this account `id`), and returned as `pageId` and `facebookPageExternalId` on post platforms"},"supportsInstagramAudio":{"type":"boolean","description":"INSTAGRAM only. True when the account was connected through Facebook, which Meta requires for adding Instagram audio to Reels"}}},"SocialAccountStatus":{"type":"string","enum":["active","unauthorized"],"description":"`unauthorized` means the platform rejected the stored token: posts to this account are refused with a 400 until it is reconnected in the dashboard"},"SocialAccountCheckResult":{"type":"object","properties":{"id":{"type":"string"},"platform":{"$ref":"#/components/schemas/PlatformType"},"displayName":{"type":"string"},"pageId":{"type":"string"},"status":{"$ref":"#/components/schemas/SocialAccountStatus"},"unauthorizedReason":{"type":"string","description":"The platform's own rejection message, when `status` is `unauthorized`"},"checkedAt":{"type":"string","format":"date-time"}}},"WebhookAccount":{"type":"object","description":"The account an `account.unauthorized` delivery is about. `id` matches the account `id` on /social-accounts","properties":{"id":{"type":"string"},"platform":{"$ref":"#/components/schemas/PlatformType"},"displayName":{"type":"string"},"pageId":{"type":"string","description":"The Facebook page id, for FACEBOOK accounts"},"status":{"type":"string","enum":["unauthorized"]},"reason":{"type":"string","description":"The platform's own rejection message"}}},"WebhookImage":{"type":"object","description":"The generation job an `image.completed` or `image.failed` delivery is about. `jobId` and `sessionId` match the POST /ai/images response","required":["jobId","sessionId","prompt"],"properties":{"jobId":{"type":"string"},"sessionId":{"type":"string","format":"uuid"},"prompt":{"type":"string"},"imageUrl":{"type":"string","description":"Public URL of the generated image, on image.completed"},"imageId":{"type":"string","description":"ID of the generated image, on image.completed"},"error":{"type":"string","description":"Why generation failed, on image.failed"}}},"CreateWebhookRequest":{"type":"object","required":["url"],"properties":{"url":{"type":"string","format":"uri","description":"Publicly reachable http(s) endpoint. Receives all events"}}},"UpdateWebhookRequest":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"active":{"type":"boolean"}}},"Webhook":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string"},"active":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"},"lastSuccessAt":{"type":"string","format":"date-time","nullable":true},"lastFailureAt":{"type":"string","format":"date-time","nullable":true},"disabledAt":{"type":"string","format":"date-time","nullable":true}}},"CreateWebhookResponse":{"allOf":[{"$ref":"#/components/schemas/Webhook"},{"type":"object","properties":{"secret":{"type":"string","description":"HMAC-SHA256 signing secret (whsec_...) for the x-adaptly-signature header. Returned ONLY at creation, store it securely; if lost, delete and re-create the webhook"}}}]},"WebhookEventPayload":{"type":"object","description":"Body of webhook deliveries POSTed to registered endpoints. Verify authenticity by comparing the x-adaptly-signature header to sha256=HMAC_SHA256(secret, \"{x-adaptly-timestamp}.{rawBody}\")","properties":{"id":{"type":"string","description":"Unique delivery ID"},"event":{"description":"The event name. POST /webhooks/{id}/test sends `webhook.test`, which is never delivered otherwise","anyOf":[{"$ref":"#/components/schemas/WebhookEventName"},{"type":"string","enum":["webhook.test"]}]},"createdAt":{"type":"string","format":"date-time"},"data":{"type":"object","description":"Carries `post` for post.* events, `account` for account.* events and `image` for image.* events. A `webhook.test` delivery carries `webhookId` and `events` instead","properties":{"post":{"$ref":"#/components/schemas/Post"},"account":{"$ref":"#/components/schemas/WebhookAccount"},"image":{"$ref":"#/components/schemas/WebhookImage"},"webhookId":{"type":"string","description":"The webhook being tested, on webhook.test"},"events":{"type":"array","items":{"$ref":"#/components/schemas/WebhookEventName"},"description":"Every event the endpoint will receive, on webhook.test"}}}}},"AnalyticsGranularity":{"type":"string","enum":["DAILY","WEEKLY","MONTHLY"]},"AnalyticsSortMetric":{"type":"string","enum":["VIEWS","LIKES","COMMENTS","SHARES","SAVES","CLICKS","IMPRESSIONS","ENGAGEMENT_RATE","PUBLISHED_AT"]},"AnalyticsSyncJobStatus":{"type":"string","enum":["IDLE","QUEUED","SYNCING","FAILED"]},"AnalyticsMetricValue":{"type":"object","description":"A metric for the requested window next to the same metric for the previous window of equal length. `null` when no selected platform reports it","properties":{"value":{"type":"number","nullable":true},"previousValue":{"type":"number","nullable":true},"deltaPercent":{"type":"number","nullable":true,"description":"Percentage change from previousValue to value. Null when previousValue is 0 or unknown"}}},"AnalyticsOverview":{"type":"object","properties":{"views":{"$ref":"#/components/schemas/AnalyticsMetricValue"},"likes":{"$ref":"#/components/schemas/AnalyticsMetricValue"},"comments":{"$ref":"#/components/schemas/AnalyticsMetricValue"},"shares":{"$ref":"#/components/schemas/AnalyticsMetricValue"},"followers":{"allOf":[{"$ref":"#/components/schemas/AnalyticsMetricValue"}],"description":"Latest follower count as of `to`, compared with the count as of the end of the previous window"},"postsCount":{"$ref":"#/components/schemas/AnalyticsMetricValue"},"avgViewsPerPost":{"$ref":"#/components/schemas/AnalyticsMetricValue"},"engagementRate":{"allOf":[{"$ref":"#/components/schemas/AnalyticsMetricValue"}],"description":"Engagements (likes + comments + shares) divided by views, as a percentage"},"partialMetrics":{"type":"array","items":{"type":"string"},"description":"Metric names that at least one selected platform cannot report; their totals cover only the platforms that can"},"lastSyncedAt":{"type":"string","format":"date-time","nullable":true,"description":"Most recent successful sync across the selected platforms. Null when nothing has synced yet"}}},"AnalyticsTimeseriesPoint":{"type":"object","properties":{"date":{"type":"string","format":"date-time","description":"Start of the bucket (UTC)"},"views":{"type":"number","nullable":true},"likes":{"type":"number","nullable":true},"comments":{"type":"number","nullable":true},"shares":{"type":"number","nullable":true},"followers":{"type":"number","nullable":true,"description":"Latest known follower count at the end of the bucket"},"postsCount":{"type":"number","nullable":true},"engagementRate":{"type":"number","nullable":true}}},"AnalyticsPostMetrics":{"type":"object","description":"Latest captured counters for one post. A metric the platform does not report is null","properties":{"views":{"type":"number","nullable":true},"likes":{"type":"number","nullable":true},"comments":{"type":"number","nullable":true},"shares":{"type":"number","nullable":true},"saves":{"type":"number","nullable":true},"clicks":{"type":"number","nullable":true},"impressions":{"type":"number","nullable":true},"reach":{"type":"number","nullable":true},"engagementRate":{"type":"number","nullable":true}}},"AnalyticsPost":{"type":"object","properties":{"id":{"type":"string","description":"Analytics record id. The only id every tracked post has, including posts discovered on the account"},"postId":{"type":"string","nullable":true,"description":"The AdaptlyPost post id (usable with /social-posts/{id}). Null for a post discovered on the account rather than published through AdaptlyPost"},"postPlatformId":{"type":"string","nullable":true,"description":"The `platformId` from /social-posts/{id}/results. Null for discovered posts"},"platform":{"$ref":"#/components/schemas/PlatformType"},"publishedAt":{"type":"string","format":"date-time"},"title":{"type":"string","nullable":true},"thumbnailUrl":{"type":"string","nullable":true},"permalink":{"type":"string","nullable":true,"description":"Public URL of the post on the platform, when known"},"accountName":{"type":"string","nullable":true},"metrics":{"$ref":"#/components/schemas/AnalyticsPostMetrics"}}},"DiscoveredPost":{"type":"object","properties":{"id":{"type":"string","description":"Analytics record id"},"platform":{"$ref":"#/components/schemas/PlatformType"},"publishedAt":{"type":"string","format":"date-time"},"text":{"type":"string","nullable":true},"thumbnailUrl":{"type":"string","nullable":true},"permalink":{"type":"string","nullable":true},"accountName":{"type":"string","nullable":true}}},"AnalyticsPlatformBreakdownItem":{"type":"object","properties":{"platform":{"$ref":"#/components/schemas/PlatformType"},"followers":{"$ref":"#/components/schemas/AnalyticsMetricValue"},"views":{"$ref":"#/components/schemas/AnalyticsMetricValue"},"likes":{"$ref":"#/components/schemas/AnalyticsMetricValue"},"comments":{"$ref":"#/components/schemas/AnalyticsMetricValue"},"shares":{"$ref":"#/components/schemas/AnalyticsMetricValue"},"postsCount":{"$ref":"#/components/schemas/AnalyticsMetricValue"},"avgViewsPerPost":{"$ref":"#/components/schemas/AnalyticsMetricValue"},"engagementRate":{"$ref":"#/components/schemas/AnalyticsMetricValue"},"supportedMetrics":{"type":"array","items":{"type":"string"},"description":"Metric names this platform reports"}}},"AnalyticsPlatformSyncStatus":{"type":"object","properties":{"platform":{"$ref":"#/components/schemas/PlatformType"},"connectionId":{"type":"string","description":"The account `id` from /social-accounts"},"accountName":{"type":"string","nullable":true},"status":{"$ref":"#/components/schemas/AnalyticsSyncJobStatus"},"lastSyncedAt":{"type":"string","format":"date-time","nullable":true},"lastErrorMessage":{"type":"string","nullable":true},"historyHorizonAt":{"type":"string","format":"date-time","nullable":true,"description":"Oldest publish date this account has data for. Earlier dates have no data rather than zero activity"},"lastDiscoveryAt":{"type":"string","format":"date-time","nullable":true},"needsAnalyticsReconnect":{"type":"boolean","description":"True when the account was connected before analytics permissions existed. It stays parked until the user reconnects it (a connect link works) and approves the extra permissions"}}},"AnalyticsSyncStatus":{"type":"object","properties":{"accountGroupId":{"type":"string"},"syncInProgress":{"type":"boolean","description":"True while any account is queued or syncing"},"lastSyncedAt":{"type":"string","format":"date-time","nullable":true},"historyHorizonAt":{"type":"string","format":"date-time","nullable":true,"description":"Earliest date any connected account has data for"},"platforms":{"type":"array","items":{"$ref":"#/components/schemas/AnalyticsPlatformSyncStatus"}}}},"TriggerAnalyticsSyncResponse":{"type":"object","properties":{"queued":{"type":"boolean","description":"True when a sync was queued. False inside the 10 minute cooldown, or when no connected account supports analytics"},"message":{"type":"string"},"cooldownSecondsRemaining":{"type":"integer","nullable":true,"description":"Seconds until the next sync can be requested; null when queued"}}}}}}