{"openapi":"3.1.0","info":{"title":"ProductByte API","version":"1.0.0","description":"Create and schedule social posts from your own systems.\n\nAuthenticate with a workspace API key as a bearer token. Keys are created under Workspace settings → API keys, and each key carries a set of scopes and an optional project allowlist.\n\nAlmost everything belongs to a project, so those routes are listed **relative to `/projects/{project_id}`** rather than repeating that prefix on every line: a route shown as `/posts` is `/projects/{project_id}/posts`. Each operation shows its full path and its ready-to-run example. Only `/me`, `/projects` and `/projects/{project_id}` sit outside a project.\n\nEvery error response carries a stable `error_code`. Branch on that, never on the HTTP status alone.\n\n**Rate limits.** Limits are per API key, over a rolling minute: 600 requests in total, of which at most 120 may be writes — a write spends from both, so 120 writes and 480 reads is the most a single key gets through in a minute. Over the limit you get `429` with `API_RATE_LIMITED` and a `Retry-After` header giving the window length.\n\nTreat the figures as approximate rather than a quota: they are counted per data centre, so a client spread across regions will get further than the numbers suggest, and one running from a single place may hit them slightly early. Size a bulk import against the numbers above and back off on `429` rather than pacing to the exact ceiling."},"tags":[{"name":"Keys","description":"The key you are using. Call `/me` first when wiring up an integration: it reports the workspace, the scopes the key carries, and the projects it may act on, which is enough to tell a misconfigured key from a wrong request."},{"name":"Projects","description":"Workspaces are divided into projects, and almost everything else belongs to one. A key may be restricted to a subset of them; `/projects` returns only the ones this key can reach, so it is the right place to discover a `project_id` rather than copying one out of a dashboard URL."},{"name":"Channels","description":"The social accounts connected to a project — a specific Instagram account or LinkedIn page, not the network in general. A post targets exactly one channel, so a `channel_id` from here is what makes a post publishable. Connecting an account is done in the app; this API only reads them."},{"name":"Products","description":"The project’s product catalogue, as imported from its shop. Read-only here. Products matter mainly as input to generation: passing `product_ids` to `/creatives/generate` builds the ad around those products’ images and fields."},{"name":"Creatives","description":"The visual half of a post — the rendered image or video, in one or more formats. Make one by uploading your own media or by describing what you want and letting the composer build it, which runs for minutes and is polled rather than awaited. One creative can be reused across as many posts as you like."},{"name":"Campaigns","description":"A named group of posts, for organising a launch or a month of content. Optional: a post created without one is held in a hidden per-creative group and simply reports no `campaign_id`. Deleting a campaign deletes the posts in it."},{"name":"Posts","description":"A creative published to one channel, either immediately, on a schedule, or saved as a draft. This is where the API does something irreversible, so writes here take an `Idempotency-Key`. Publishing is asynchronous — poll with `updated_since` to learn what actually went out."}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"A workspace API key, for example `pb_live_…`."}},"schemas":{},"parameters":{}},"paths":{"/me":{"get":{"tags":["Keys"],"summary":"/me","description":"Returns the workspace, scopes, and project allowlist of the key making the request. Use it to verify a key during integration.","operationId":"get_MeEndpoint","responses":{"200":{"description":"The key and what it may do.","content":{"application/json":{"schema":{"type":"object","properties":{"organization_id":{"type":"string","description":"Workspace the key belongs to."},"scopes":{"type":"array","items":{"type":"string","enum":["channels:read","posts:read","posts:write","creatives:read","creatives:write","products:read","analytics:read"]},"description":"Permissions granted to this key."},"project_ids":{"type":"array","items":{"type":"string"},"description":"Projects this key may act on. Empty means every project in the workspace."},"expires_at":{"type":"string","format":"date-time","description":"When the key stops working. Absent if it never expires."}},"required":["organization_id","scopes","project_ids"]}}}},"401":{"description":"Missing, unknown, revoked, or expired key (`API_KEY_UNAUTHORIZED`).","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"429":{"description":"Too many requests for this key (`API_RATE_LIMITED`). Back off and retry; `Retry-After` gives the window length.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}}}}},"/projects":{"get":{"tags":["Projects"],"summary":"/projects","description":"Lists the projects this key may act on, by name. Start here: every other path is scoped to a project, and this is the only way to learn its id.\n\nA key with no project allowlist sees every project in the workspace; a restricted key sees only the ones it was issued for.\n\nPaginate with `cursor`: pass the `next_cursor` from the previous page. It is absent on the last page.","operationId":"get_ListProjectsEndpoint","parameters":[{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50,"description":"Page size, 1–100."},"required":false,"description":"Page size, 1–100.","name":"limit","in":"query"},{"schema":{"type":"string","description":"A `next_cursor` from a previous page."},"required":false,"description":"A `next_cursor` from a previous page.","name":"cursor","in":"query"}],"responses":{"200":{"description":"A page of projects.","content":{"application/json":{"schema":{"type":"object","properties":{"projects":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Identifier for this project. Use it in every project-scoped path."},"name":{"type":"string","description":"The project’s display name."},"website":{"type":"string","description":"The website the project publishes for, when one is set."}},"required":["id","name"]},"description":"Projects this key may act on, by name."},"next_cursor":{"type":"string","description":"Pass as `cursor` to fetch the next page. Absent when there are no more."}},"required":["projects"]}}}},"401":{"description":"Missing, unknown, revoked, or expired key (`API_KEY_UNAUTHORIZED`).","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"429":{"description":"Too many requests for this key (`API_RATE_LIMITED`). Back off and retry; `Retry-After` gives the window length.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}}}}},"/projects/{project_id}":{"get":{"tags":["Projects"],"summary":"/projects/{project_id}","description":"Returns one project. Answers 404 for a project outside this workspace and 403 for one outside the key’s allowlist.","operationId":"get_GetProjectEndpoint","parameters":[{"schema":{"type":"string","description":"The project to read."},"required":true,"description":"The project to read.","name":"project_id","in":"path"}],"responses":{"200":{"description":"The project.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Identifier for this project. Use it in every project-scoped path."},"name":{"type":"string","description":"The project’s display name."},"website":{"type":"string","description":"The website the project publishes for, when one is set."}},"required":["id","name"]}}}},"401":{"description":"Missing, unknown, revoked, or expired key (`API_KEY_UNAUTHORIZED`).","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"403":{"description":"The project is outside the key's allowlist (`API_KEY_PROJECT_FORBIDDEN`).","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"404":{"description":"No such project in this workspace (`API_PROJECT_NOT_FOUND`).","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"429":{"description":"Too many requests for this key (`API_RATE_LIMITED`). Back off and retry; `Retry-After` gives the window length.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}}}}},"/projects/{project_id}/campaigns":{"get":{"tags":["Campaigns"],"summary":"/campaigns","description":"Lists the project’s campaigns.\n\nOnly campaigns someone made are listed. Posts created without a `campaign_id` are grouped internally so the product has somewhere to put them; those groupings are not campaigns you can act on and never appear here.\n\nPaginate with `cursor`: pass the `next_cursor` from the previous page. It is absent on the last page.\n\nRequires the `posts:read` scope.","operationId":"get_ListCampaignsEndpoint","parameters":[{"schema":{"type":"string","description":"The project to list campaigns for."},"required":true,"description":"The project to list campaigns for.","name":"project_id","in":"path"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50,"description":"Page size, 1–100."},"required":false,"description":"Page size, 1–100.","name":"limit","in":"query"},{"schema":{"type":"string","description":"A `next_cursor` from a previous page."},"required":false,"description":"A `next_cursor` from a previous page.","name":"cursor","in":"query"}],"responses":{"200":{"description":"A page of campaigns.","content":{"application/json":{"schema":{"type":"object","properties":{"campaigns":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Pass this as `campaign_id` when creating a post."},"name":{"type":"string","description":"The campaign’s name."},"start_date":{"type":"string","format":"date-time","description":"When the campaign starts, when a start day is set. Normalized to midnight UTC on that day — it is a date the product shows, not a scheduling instruction."},"post_count":{"type":"integer","description":"How many posts belong to it."},"created_at":{"type":"string","format":"date-time","description":"When the campaign was created."},"updated_at":{"type":"string","format":"date-time","description":"When it last changed."}},"required":["id","name","post_count","created_at","updated_at"]},"description":"Campaigns, by name."},"next_cursor":{"type":"string","description":"Pass as `cursor` to fetch the next page. Absent when there are no more."}},"required":["campaigns"]}}}},"401":{"description":"Missing, unknown, revoked, or expired key (`API_KEY_UNAUTHORIZED`).","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"403":{"description":"The key lacks `posts:read`, or the project is outside its allowlist.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"429":{"description":"Too many requests for this key (`API_RATE_LIMITED`). Back off and retry; `Retry-After` gives the window length.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}}}},"post":{"tags":["Campaigns"],"summary":"/campaigns","description":"Creates a campaign to group posts under.\n\nMake one before planning a batch, then pass its `id` as `campaign_id` on each `POST /posts` — that is what makes a month of posts one thing the customer can see and manage in ProductByte, rather than a pile of unassigned posts.\n\nRequires the `posts:write` scope.","operationId":"post_CreateCampaignEndpoint","parameters":[{"schema":{"type":"string","description":"The project to create the campaign in."},"required":true,"description":"The project to create the campaign in.","name":"project_id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"description":"What to call it. Shown to the customer in ProductByte."},"start_date":{"type":"string","format":"date","description":"The day the campaign starts, as `YYYY-MM-DD`. Presentational only."}},"required":["name"]}}}},"responses":{"201":{"description":"The campaign.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Pass this as `campaign_id` when creating a post."},"name":{"type":"string","description":"The campaign’s name."},"start_date":{"type":"string","format":"date-time","description":"When the campaign starts, when a start day is set. Normalized to midnight UTC on that day — it is a date the product shows, not a scheduling instruction."},"post_count":{"type":"integer","description":"How many posts belong to it."},"created_at":{"type":"string","format":"date-time","description":"When the campaign was created."},"updated_at":{"type":"string","format":"date-time","description":"When it last changed."}},"required":["id","name","post_count","created_at","updated_at"]}}}},"401":{"description":"Missing, unknown, revoked, or expired key (`API_KEY_UNAUTHORIZED`).","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"403":{"description":"The key lacks `posts:write`, or the project is outside its allowlist.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"429":{"description":"Too many requests for this key (`API_RATE_LIMITED`). Back off and retry; `Retry-After` gives the window length.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}}}}},"/projects/{project_id}/campaigns/{campaign_id}":{"get":{"tags":["Campaigns"],"summary":"/campaigns/{campaign_id}","description":"Returns one campaign.\n\nRequires the `posts:read` scope.","operationId":"get_GetCampaignEndpoint","parameters":[{"schema":{"type":"string","description":"The project the campaign belongs to."},"required":true,"description":"The project the campaign belongs to.","name":"project_id","in":"path"},{"schema":{"type":"string","description":"The campaign to read."},"required":true,"description":"The campaign to read.","name":"campaign_id","in":"path"}],"responses":{"200":{"description":"The campaign.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Pass this as `campaign_id` when creating a post."},"name":{"type":"string","description":"The campaign’s name."},"start_date":{"type":"string","format":"date-time","description":"When the campaign starts, when a start day is set. Normalized to midnight UTC on that day — it is a date the product shows, not a scheduling instruction."},"post_count":{"type":"integer","description":"How many posts belong to it."},"created_at":{"type":"string","format":"date-time","description":"When the campaign was created."},"updated_at":{"type":"string","format":"date-time","description":"When it last changed."}},"required":["id","name","post_count","created_at","updated_at"]}}}},"401":{"description":"Missing, unknown, revoked, or expired key (`API_KEY_UNAUTHORIZED`).","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"403":{"description":"The key lacks `posts:read`, or the project is outside its allowlist.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"404":{"description":"No such campaign in this project (`API_CAMPAIGN_NOT_FOUND`).","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"429":{"description":"Too many requests for this key (`API_RATE_LIMITED`). Back off and retry; `Retry-After` gives the window length.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}}}},"patch":{"tags":["Campaigns"],"summary":"/campaigns/{campaign_id}","description":"Edits a campaign. Omitted fields are left unchanged.\n\nRequires the `posts:write` scope.","operationId":"patch_UpdateCampaignEndpoint","parameters":[{"schema":{"type":"string","description":"The project the campaign belongs to."},"required":true,"description":"The project the campaign belongs to.","name":"project_id","in":"path"},{"schema":{"type":"string","description":"The campaign to edit."},"required":true,"description":"The campaign to edit.","name":"campaign_id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"description":"Replacement name."},"start_date":{"type":"string","format":"date","description":"Replacement start day, as `YYYY-MM-DD`."}}}}}},"responses":{"200":{"description":"The campaign.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Pass this as `campaign_id` when creating a post."},"name":{"type":"string","description":"The campaign’s name."},"start_date":{"type":"string","format":"date-time","description":"When the campaign starts, when a start day is set. Normalized to midnight UTC on that day — it is a date the product shows, not a scheduling instruction."},"post_count":{"type":"integer","description":"How many posts belong to it."},"created_at":{"type":"string","format":"date-time","description":"When the campaign was created."},"updated_at":{"type":"string","format":"date-time","description":"When it last changed."}},"required":["id","name","post_count","created_at","updated_at"]}}}},"401":{"description":"Missing, unknown, revoked, or expired key (`API_KEY_UNAUTHORIZED`).","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"403":{"description":"The key lacks `posts:write`, or the project is outside its allowlist.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"404":{"description":"No such campaign in this project (`API_CAMPAIGN_NOT_FOUND`).","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"429":{"description":"Too many requests for this key (`API_RATE_LIMITED`). Back off and retry; `Retry-After` gives the window length.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}}}},"delete":{"tags":["Campaigns"],"summary":"/campaigns/{campaign_id}","description":"Removes the campaign **and every post in it** from ProductByte. This is what the same action does in the app, and it cannot be undone.\n\n**Nothing is removed from the channels.** A published post in this campaign loses its record here and stays up on the channel — retract those first if the point is to take content down.\n\nRequires the `posts:write` scope.","operationId":"delete_DeleteCampaignEndpoint","parameters":[{"schema":{"type":"string","description":"The project the campaign belongs to."},"required":true,"description":"The project the campaign belongs to.","name":"project_id","in":"path"},{"schema":{"type":"string","description":"The campaign to remove."},"required":true,"description":"The campaign to remove.","name":"campaign_id","in":"path"}],"responses":{"204":{"description":"The campaign and its posts were removed."},"401":{"description":"Missing, unknown, revoked, or expired key (`API_KEY_UNAUTHORIZED`).","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"403":{"description":"The key lacks `posts:write`, or the project is outside its allowlist.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"404":{"description":"No such campaign in this project (`API_CAMPAIGN_NOT_FOUND`).","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"409":{"description":"The campaign holds a paid ad that is running or launching (`API_CAMPAIGN_HAS_RUNNING_ADS`). Pause its ads first.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"429":{"description":"Too many requests for this key (`API_RATE_LIMITED`). Back off and retry; `Retry-After` gives the window length.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}}}}},"/projects/{project_id}/channels":{"get":{"tags":["Channels"],"summary":"/channels","description":"Lists the social accounts and ad accounts connected to a project, grouped by provider. Channel ids from this endpoint are what you pass when creating a post.\n\n### What each channel accepts\n\n| Channel | Stills | Video | Comments | Can be taken down |\n| --- | --- | --- | --- | --- |\n| Instagram | 1:1, 4:5, 16:9 | 9:16 only — a feed video publishes as a Reel | yes | no — remove it in Instagram |\n| Facebook Page | 1:1, 4:5, 16:9 | 1:1, 4:5, 9:16, 16:9 | yes | yes |\n| TikTok | 9:16 | 9:16 | no | no — remove it in TikTok |\n| X | 1:1, 4:5 | 1:1, 4:5, 9:16, 16:9 | yes | yes |\n| Threads | 1:1, 4:5, 9:16, 16:9 | 1:1, 4:5, 9:16, 16:9 | yes | yes |\n| Bluesky | 1:1, 4:5, 9:16, 16:9 | 1:1, 4:5, 9:16, 16:9 | yes | yes |\n| Pinterest | 1:1, 4:5 — up to 5 as a carousel | 1:1, 4:5, 9:16 — one per Pin | no | yes |\n\nA format outside the accepted ratios is refused when the post is created, not when it publishes, so a bad plan fails immediately rather than a month later. `auto_delete_after_seconds` and `POST /posts/{post_id}/retract` both need a channel in the last column.\n\nPaginate with `cursor`: pass the `next_cursor` from the previous page. It is absent on the last page.\n\nRequires the `channels:read` scope.","operationId":"get_ListProjectChannelsEndpoint","parameters":[{"schema":{"type":"string","description":"The project to list channels for."},"required":true,"description":"The project to list channels for.","name":"project_id","in":"path"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50,"description":"Page size, 1–100."},"required":false,"description":"Page size, 1–100.","name":"limit","in":"query"},{"schema":{"type":"string","description":"A `next_cursor` from a previous page."},"required":false,"description":"A `next_cursor` from a previous page.","name":"cursor","in":"query"}],"responses":{"200":{"description":"A page of channels.","content":{"application/json":{"schema":{"type":"object","properties":{"channels":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Stable identifier for this channel. Pass it when creating a post."},"type":{"type":"string","enum":["x_profile","tiktok_creator","facebook_page","instagram","threads_profile","bluesky_profile","pinterest_profile"],"description":"What kind of destination this is."},"platform":{"type":"string","enum":["x","tiktok","meta","threads","bluesky","pinterest"],"description":"The provider behind the channel."},"name":{"type":"string","description":"Display name, as shown in ProductByte."},"secondary_name":{"type":"string","description":"Owning account or business, when the provider reports one."},"handle":{"type":"string","description":"Public handle, without the leading @, when the provider has one."}},"required":["id","type","platform","name"]},"description":"Channels connected to this project."},"next_cursor":{"type":"string","description":"Pass as `cursor` to fetch the next page. Absent when there are no more."}},"required":["channels"]}}}},"401":{"description":"Missing, unknown, revoked, or expired key (`API_KEY_UNAUTHORIZED`).","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"403":{"description":"The key lacks `channels:read` (`API_KEY_SCOPE_FORBIDDEN`), or the project is outside its allowlist (`API_KEY_PROJECT_FORBIDDEN`).","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"404":{"description":"No such project in this workspace (`API_PROJECT_NOT_FOUND`).","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"429":{"description":"Too many requests for this key (`API_RATE_LIMITED`). Back off and retry; `Retry-After` gives the window length.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}}}}},"/projects/{project_id}/channels/{channel_id}":{"get":{"tags":["Channels"],"summary":"/channels/{channel_id}","description":"Returns one connected channel. Useful for confirming a stored channel id still resolves before publishing to it — a disconnected account answers 404 here.\n\nRequires the `channels:read` scope.","operationId":"get_GetProjectChannelEndpoint","parameters":[{"schema":{"type":"string","description":"The project the channel belongs to."},"required":true,"description":"The project the channel belongs to.","name":"project_id","in":"path"},{"schema":{"type":"string","description":"The channel to read."},"required":true,"description":"The channel to read.","name":"channel_id","in":"path"}],"responses":{"200":{"description":"The channel.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Stable identifier for this channel. Pass it when creating a post."},"type":{"type":"string","enum":["x_profile","tiktok_creator","facebook_page","instagram","threads_profile","bluesky_profile","pinterest_profile"],"description":"What kind of destination this is."},"platform":{"type":"string","enum":["x","tiktok","meta","threads","bluesky","pinterest"],"description":"The provider behind the channel."},"name":{"type":"string","description":"Display name, as shown in ProductByte."},"secondary_name":{"type":"string","description":"Owning account or business, when the provider reports one."},"handle":{"type":"string","description":"Public handle, without the leading @, when the provider has one."}},"required":["id","type","platform","name"]}}}},"401":{"description":"Missing, unknown, revoked, or expired key (`API_KEY_UNAUTHORIZED`).","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"403":{"description":"The key lacks `channels:read`, or the project is outside its allowlist.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"404":{"description":"No such channel connected to this project (`API_CHANNEL_NOT_FOUND`).","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"429":{"description":"Too many requests for this key (`API_RATE_LIMITED`). Back off and retry; `Retry-After` gives the window length.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}}}}},"/projects/{project_id}/creatives":{"get":{"tags":["Creatives"],"summary":"/creatives","description":"Lists the project’s creatives, newest first.\n\nEntries carry `format_count` rather than the formats themselves; read a single creative to get the `format_ids` a post needs.\n\nPaginate with `cursor`: pass the `next_cursor` from the previous page. It is absent on the last page.\n\nRequires the `creatives:read` scope.","operationId":"get_ListCreativesEndpoint","parameters":[{"schema":{"type":"string","description":"The project to list creatives for."},"required":true,"description":"The project to list creatives for.","name":"project_id","in":"path"},{"schema":{"type":"string","enum":["image","carousel","video"],"description":"Only creatives built from this kind of media."},"required":false,"description":"Only creatives built from this kind of media.","name":"kind","in":"query"},{"schema":{"type":"string","description":"Match against the creative name."},"required":false,"description":"Match against the creative name.","name":"query","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50,"description":"Page size, 1–100."},"required":false,"description":"Page size, 1–100.","name":"limit","in":"query"},{"schema":{"type":"string","description":"A `next_cursor` from a previous page."},"required":false,"description":"A `next_cursor` from a previous page.","name":"cursor","in":"query"}],"responses":{"200":{"description":"A page of creatives.","content":{"application/json":{"schema":{"type":"object","properties":{"creatives":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Pass this as `creative_id` when creating a post."},"name":{"type":"string","description":"The creative’s name."},"kind":{"type":"string","enum":["image","carousel","video"],"description":"What the creative is built from."},"format_count":{"type":"integer","description":"How many formats it holds. Read the creative to list them."},"created_at":{"type":"string","format":"date-time","description":"When the creative was created."},"updated_at":{"type":"string","format":"date-time","description":"When it last changed."}},"required":["id","name","kind","format_count","created_at","updated_at"]},"description":"Creatives, newest first."},"next_cursor":{"type":"string","description":"Pass as `cursor` to fetch the next page. Absent when there are no more."}},"required":["creatives"]}}}},"401":{"description":"Missing, unknown, revoked, or expired key (`API_KEY_UNAUTHORIZED`).","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"403":{"description":"The key lacks `creatives:read`, or the project is outside its allowlist.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"429":{"description":"Too many requests for this key (`API_RATE_LIMITED`). Back off and retry; `Retry-After` gives the window length.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}}}}},"/projects/{project_id}/creatives/upload":{"post":{"tags":["Creatives"],"summary":"/creatives/upload","description":"Creates a creative from media you supply. Send `multipart/form-data` with a `file` field: a JPEG, PNG, WebP, HEIC, or HEIF image, or an MP4 video.\n\nFor a video, also send `width` and `height` in pixels, and optionally `duration_seconds`. Images are measured for you; a video cannot be, and the frame size decides how the creative renders — so an MP4 without them is rejected rather than guessed at.\n\nThis is one of the ways to create a creative — see `/creatives/generate` for the other. Either way the returned `id` is a creative id, reusable as `creative_id` on as many posts as you like.\n\nSend an `Idempotency-Key` header so a retry after a timeout collects the original creative instead of uploading the file a second time.\n\nRequires the `creatives:write` scope.","operationId":"post_UploadCreativeEndpoint","parameters":[{"schema":{"type":"string","description":"The project to create the creative in."},"required":true,"description":"The project to create the creative in.","name":"project_id","in":"path"}],"responses":{"201":{"description":"The creative was created.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Pass this as `creative_id` when creating a post."},"name":{"type":"string","description":"What the creative is called in ProductByte."},"kind":{"type":"string","enum":["image","carousel","video"],"description":"What the creative is built from. It decides what `formats` means — see below."},"formats":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Pass this in `format_ids` when creating a post."},"kind":{"type":"string","enum":["image","video"],"description":"What kind of media backs this format."},"width":{"type":"number","description":"Frame width in pixels."},"height":{"type":"number","description":"Frame height in pixels."},"language":{"type":"string","enum":["en-GB","en-US","nl-NL","fr-FR","de-DE","es-ES","it-IT","pt-PT","pl-PL","sv-SE","da-DK","fi-FI","no-NO"],"description":"Language of any copy rendered into it."}},"required":["id","kind","width","height","language"]},"description":"For an **image** creative these are alternatives: pick the ones whose aspect ratio suits each channel.\n\nFor a **carousel** they are the deck’s slides, in order, and are published together — pass all of them, not a selection."},"created_at":{"type":"string","format":"date-time","description":"When the creative was created."}},"required":["id","name","kind","formats","created_at"]}}}},"400":{"description":"The file is missing, too large, or not a supported type (`API_REQUEST_INVALID`).","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"401":{"description":"Missing, unknown, revoked, or expired key (`API_KEY_UNAUTHORIZED`).","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"403":{"description":"The key lacks `creatives:write`, or the project is outside its allowlist.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"409":{"description":"The `Idempotency-Key` was already used with a different file (`API_IDEMPOTENCY_KEY_REUSED`), or the first request using it is still running (`API_IDEMPOTENCY_KEY_IN_FLIGHT`) — retry in a moment to collect its response.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"429":{"description":"Too many requests for this key (`API_RATE_LIMITED`). Back off and retry; `Retry-After` gives the window length.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}}}}},"/projects/{project_id}/creatives/generate":{"post":{"tags":["Creatives"],"summary":"/creatives/generate","description":"Generates a creative from a brief — a still image, or a multi-slide carousel with `type: carousel`.\n\nThis is the other way to make a creative — `/creatives/upload` is the one where you supply the media.\n\n**It answers before the work is done.** Composing a creative is an LLM pipeline that runs for minutes, far longer than a request should wait, so you get a generation to poll at `/creatives/generations/{generation_id}`. The `creative_id` is usable immediately: the creative exists from the start, empty, and fills in when the generation succeeds.\n\nNaming `product_ids` builds the creative around those catalogue products, using their images and fields. Without them the composer works from the brief alone.\n\nGeneration consumes the workspace's credits, so this can fail on billing as well as on input.\n\nRequires the `creatives:write` scope.","operationId":"post_GenerateCreativeEndpoint","parameters":[{"schema":{"type":"string","description":"The project to create the creative in."},"required":true,"description":"The project to create the creative in.","name":"project_id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"type":{"type":"string","enum":["image","carousel"],"default":"image","description":"What to compose. `carousel` produces a multi-slide deck."},"prompt":{"type":"string","minLength":1,"maxLength":4000,"description":"The brief, in your own words — what the creative is for, and any direction to follow. Up to 4000 characters."},"name":{"type":"string","minLength":1,"maxLength":200,"description":"What to call the creative. Worth sending: an upload takes its name from the filename, but a generated creative has nothing to take one from and is otherwise unnamed in ProductByte."},"format":{"type":"string","enum":["1:1","4:5","16:9","9:16","2:3"],"description":"Aspect ratio to compose at."},"language":{"type":"string","enum":["en-GB","en-US","nl-NL","fr-FR","de-DE","es-ES","it-IT","pt-PT","pl-PL","sv-SE","da-DK","fi-FI","no-NO"],"description":"Language of the copy rendered into the creative. Defaults to the project’s."},"product_ids":{"type":"array","items":{"type":"string"},"maxItems":10,"description":"Catalogue products to build the creative around, from `/products`. Up to 10."},"variant_count":{"type":"integer","minimum":1,"maximum":3,"description":"How many options to produce. Image only — a carousel composes one deck."},"slide_count":{"type":"integer","minimum":3,"maximum":6,"description":"Slides in the deck, from 3 to 6. Carousel only; defaults to five."}},"required":["prompt","format"]}}}},"responses":{"202":{"description":"The generation was queued. Poll it to learn the outcome.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Pass this to `/creatives/generations/{generation_id}` to follow it."},"creative_id":{"type":"string","description":"The creative being generated into. It exists from the moment the generation starts, empty until the first result lands, so you can reference it before the work finishes."},"status":{"type":"string","enum":["running","succeeded","failed"],"description":"`running` until it settles. On `succeeded`, read the creative for its formats."},"error_code":{"type":"string","description":"Why it failed. A stable code, never prose."},"created_at":{"type":"string","format":"date-time","description":"When the generation was started."},"updated_at":{"type":"string","format":"date-time","description":"When it last changed."}},"required":["id","creative_id","status","created_at","updated_at"]}}}},"401":{"description":"Missing, unknown, revoked, or expired key (`API_KEY_UNAUTHORIZED`).","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"403":{"description":"The key lacks `creatives:write` or the project is outside its allowlist.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"409":{"description":"The `Idempotency-Key` was already used with a different body (`API_IDEMPOTENCY_KEY_REUSED`), or the first request using it is still running (`API_IDEMPOTENCY_KEY_IN_FLIGHT`) — retry in a moment to collect its response.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"429":{"description":"Too many requests for this key (`API_RATE_LIMITED`). Back off and retry; `Retry-After` gives the window length.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}}}}},"/projects/{project_id}/creatives/generations/{generation_id}":{"get":{"tags":["Creatives"],"summary":"/creatives/generations/{generation_id}","description":"Reports on a generation started by `/creatives/generate`.\n\nPoll until `status` stops being `running`; on `succeeded`, read the creative at `creative_id` for its formats. There is no progress percentage — the pipeline has no meaningful one to give.\n\nRequires the `creatives:read` scope.","operationId":"get_GetCreativeGenerationEndpoint","parameters":[{"schema":{"type":"string","description":"The project the generation belongs to."},"required":true,"description":"The project the generation belongs to.","name":"project_id","in":"path"},{"schema":{"type":"string","description":"The generation to read."},"required":true,"description":"The generation to read.","name":"generation_id","in":"path"}],"responses":{"200":{"description":"The generation.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Pass this to `/creatives/generations/{generation_id}` to follow it."},"creative_id":{"type":"string","description":"The creative being generated into. It exists from the moment the generation starts, empty until the first result lands, so you can reference it before the work finishes."},"status":{"type":"string","enum":["running","succeeded","failed"],"description":"`running` until it settles. On `succeeded`, read the creative for its formats."},"error_code":{"type":"string","description":"Why it failed. A stable code, never prose."},"created_at":{"type":"string","format":"date-time","description":"When the generation was started."},"updated_at":{"type":"string","format":"date-time","description":"When it last changed."}},"required":["id","creative_id","status","created_at","updated_at"]}}}},"401":{"description":"Missing, unknown, revoked, or expired key (`API_KEY_UNAUTHORIZED`).","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"403":{"description":"The key lacks `creatives:read`, or the project is outside its allowlist.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"404":{"description":"No such generation in this project (`API_GENERATION_NOT_FOUND`).","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"429":{"description":"Too many requests for this key (`API_RATE_LIMITED`). Back off and retry; `Retry-After` gives the window length.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}}}}},"/projects/{project_id}/creatives/{creative_id}":{"get":{"tags":["Creatives"],"summary":"/creatives/{creative_id}","description":"Returns one creative with every format it can be published in.\n\nOnly finished formats are listed: a format still being rendered has no media to publish and would be refused by `POST /posts`.\n\nRequires the `creatives:read` scope.","operationId":"get_GetCreativeEndpoint","parameters":[{"schema":{"type":"string","description":"The project the creative belongs to."},"required":true,"description":"The project the creative belongs to.","name":"project_id","in":"path"},{"schema":{"type":"string","description":"The creative to read."},"required":true,"description":"The creative to read.","name":"creative_id","in":"path"}],"responses":{"200":{"description":"The creative.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Pass this as `creative_id` when creating a post."},"name":{"type":"string","description":"What the creative is called in ProductByte."},"kind":{"type":"string","enum":["image","carousel","video"],"description":"What the creative is built from. It decides what `formats` means — see below."},"formats":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Pass this in `format_ids` when creating a post."},"kind":{"type":"string","enum":["image","video"],"description":"What kind of media backs this format."},"width":{"type":"number","description":"Frame width in pixels."},"height":{"type":"number","description":"Frame height in pixels."},"language":{"type":"string","enum":["en-GB","en-US","nl-NL","fr-FR","de-DE","es-ES","it-IT","pt-PT","pl-PL","sv-SE","da-DK","fi-FI","no-NO"],"description":"Language of any copy rendered into it."}},"required":["id","kind","width","height","language"]},"description":"For an **image** creative these are alternatives: pick the ones whose aspect ratio suits each channel.\n\nFor a **carousel** they are the deck’s slides, in order, and are published together — pass all of them, not a selection."},"created_at":{"type":"string","format":"date-time","description":"When the creative was created."}},"required":["id","name","kind","formats","created_at"]}}}},"401":{"description":"Missing, unknown, revoked, or expired key (`API_KEY_UNAUTHORIZED`).","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"403":{"description":"The key lacks `creatives:read`, or the project is outside its allowlist.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"404":{"description":"No such creative in this project (`API_CREATIVE_NOT_FOUND`).","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"429":{"description":"Too many requests for this key (`API_RATE_LIMITED`). Back off and retry; `Retry-After` gives the window length.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}}}},"patch":{"tags":["Creatives"],"summary":"/creatives/{creative_id}","description":"Renames a creative. The name is what identifies it in ProductByte's library, so it is worth setting on anything generated.\n\nRequires the `creatives:write` scope.","operationId":"patch_UpdateCreativeEndpoint","parameters":[{"schema":{"type":"string","description":"The project the creative belongs to."},"required":true,"description":"The project the creative belongs to.","name":"project_id","in":"path"},{"schema":{"type":"string","description":"The creative to rename."},"required":true,"description":"The creative to rename.","name":"creative_id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"description":"The new name."}},"required":["name"]}}}},"responses":{"200":{"description":"The creative.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Pass this as `creative_id` when creating a post."},"name":{"type":"string","description":"What the creative is called in ProductByte."},"kind":{"type":"string","enum":["image","carousel","video"],"description":"What the creative is built from. It decides what `formats` means — see below."},"formats":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Pass this in `format_ids` when creating a post."},"kind":{"type":"string","enum":["image","video"],"description":"What kind of media backs this format."},"width":{"type":"number","description":"Frame width in pixels."},"height":{"type":"number","description":"Frame height in pixels."},"language":{"type":"string","enum":["en-GB","en-US","nl-NL","fr-FR","de-DE","es-ES","it-IT","pt-PT","pl-PL","sv-SE","da-DK","fi-FI","no-NO"],"description":"Language of any copy rendered into it."}},"required":["id","kind","width","height","language"]},"description":"For an **image** creative these are alternatives: pick the ones whose aspect ratio suits each channel.\n\nFor a **carousel** they are the deck’s slides, in order, and are published together — pass all of them, not a selection."},"created_at":{"type":"string","format":"date-time","description":"When the creative was created."}},"required":["id","name","kind","formats","created_at"]}}}},"401":{"description":"Missing, unknown, revoked, or expired key (`API_KEY_UNAUTHORIZED`).","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"403":{"description":"The key lacks `creatives:write`, or the project is outside its allowlist.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"404":{"description":"No such creative in this project (`API_CREATIVE_NOT_FOUND`).","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"429":{"description":"Too many requests for this key (`API_RATE_LIMITED`). Back off and retry; `Retry-After` gives the window length.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}}}},"delete":{"tags":["Creatives"],"summary":"/creatives/{creative_id}","description":"Removes the creative.\n\n**This also removes the draft and scheduled posts that use it**, because a post without its creative has nothing to publish. `deleted_post_count` reports how many went. Published posts keep their record and stay up on the channel — use `POST /posts/{post_id}/retract` for those.\n\nRequires the `creatives:write` scope.","operationId":"delete_DeleteCreativeEndpoint","parameters":[{"schema":{"type":"string","description":"The project the creative belongs to."},"required":true,"description":"The project the creative belongs to.","name":"project_id","in":"path"},{"schema":{"type":"string","description":"The creative to remove."},"required":true,"description":"The creative to remove.","name":"creative_id","in":"path"}],"responses":{"200":{"description":"The creative was removed.","content":{"application/json":{"schema":{"type":"object","properties":{"deleted_post_count":{"type":"integer","description":"Draft and scheduled posts removed because they used this creative. Published posts are untouched."}},"required":["deleted_post_count"]}}}},"401":{"description":"Missing, unknown, revoked, or expired key (`API_KEY_UNAUTHORIZED`).","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"403":{"description":"The key lacks `creatives:write`, or the project is outside its allowlist.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"404":{"description":"No such creative in this project (`API_CREATIVE_NOT_FOUND`).","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"429":{"description":"Too many requests for this key (`API_RATE_LIMITED`). Back off and retry; `Retry-After` gives the window length.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}}}}},"/projects/{project_id}/creatives/{creative_id}/formats":{"post":{"tags":["Creatives"],"summary":"/creatives/{creative_id}/formats","description":"Adds another format to an existing creative — a second aspect ratio, or the same one in another language. Send `multipart/form-data` with a `file` field, exactly as for `/creatives/upload`.\n\nReturns the creative with all of its formats. Their ids are what you pass in `format_ids` when creating a post.\n\nOnly image creatives accept new formats: a scene added to a carousel is a slide, and one added to a video is a segment. Neither is a format.\n\nRequires the `creatives:write` scope.","operationId":"post_AddCreativeFormatEndpoint","parameters":[{"schema":{"type":"string","description":"The project the creative belongs to."},"required":true,"description":"The project the creative belongs to.","name":"project_id","in":"path"},{"schema":{"type":"string","description":"The creative to add a format to."},"required":true,"description":"The creative to add a format to.","name":"creative_id","in":"path"}],"responses":{"200":{"description":"The creative, with the new format included.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Pass this as `creative_id` when creating a post."},"name":{"type":"string","description":"What the creative is called in ProductByte."},"kind":{"type":"string","enum":["image","carousel","video"],"description":"What the creative is built from. It decides what `formats` means — see below."},"formats":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Pass this in `format_ids` when creating a post."},"kind":{"type":"string","enum":["image","video"],"description":"What kind of media backs this format."},"width":{"type":"number","description":"Frame width in pixels."},"height":{"type":"number","description":"Frame height in pixels."},"language":{"type":"string","enum":["en-GB","en-US","nl-NL","fr-FR","de-DE","es-ES","it-IT","pt-PT","pl-PL","sv-SE","da-DK","fi-FI","no-NO"],"description":"Language of any copy rendered into it."}},"required":["id","kind","width","height","language"]},"description":"For an **image** creative these are alternatives: pick the ones whose aspect ratio suits each channel.\n\nFor a **carousel** they are the deck’s slides, in order, and are published together — pass all of them, not a selection."},"created_at":{"type":"string","format":"date-time","description":"When the creative was created."}},"required":["id","name","kind","formats","created_at"]}}}},"400":{"description":"The file is missing, unsupported, or the creative does not accept formats (`API_REQUEST_INVALID`).","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"401":{"description":"Missing, unknown, revoked, or expired key (`API_KEY_UNAUTHORIZED`).","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"403":{"description":"The key lacks `creatives:write`, or the project is outside its allowlist.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"404":{"description":"No such creative in this project (`API_CREATIVE_NOT_FOUND`).","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"429":{"description":"Too many requests for this key (`API_RATE_LIMITED`). Back off and retry; `Retry-After` gives the window length.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}}}}},"/projects/{project_id}/creatives/{creative_id}/formats/{format_id}":{"delete":{"tags":["Creatives"],"summary":"/creatives/{creative_id}/formats/{format_id}","description":"Removes one format from a creative and returns what is left.\n\nThe last format cannot be removed: a creative with none has nothing to publish. Delete the creative instead (`API_FORMAT_LAST_REMAINING`).\n\nPosts already referencing the format keep their own copy of the media, so this does not change anything already published.\n\nRequires the `creatives:write` scope.","operationId":"delete_DeleteCreativeFormatEndpoint","parameters":[{"schema":{"type":"string","description":"The project the creative belongs to."},"required":true,"description":"The project the creative belongs to.","name":"project_id","in":"path"},{"schema":{"type":"string","description":"The creative to change."},"required":true,"description":"The creative to change.","name":"creative_id","in":"path"},{"schema":{"type":"string","description":"The format to remove."},"required":true,"description":"The format to remove.","name":"format_id","in":"path"}],"responses":{"200":{"description":"The creative without that format.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Pass this as `creative_id` when creating a post."},"name":{"type":"string","description":"What the creative is called in ProductByte."},"kind":{"type":"string","enum":["image","carousel","video"],"description":"What the creative is built from. It decides what `formats` means — see below."},"formats":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Pass this in `format_ids` when creating a post."},"kind":{"type":"string","enum":["image","video"],"description":"What kind of media backs this format."},"width":{"type":"number","description":"Frame width in pixels."},"height":{"type":"number","description":"Frame height in pixels."},"language":{"type":"string","enum":["en-GB","en-US","nl-NL","fr-FR","de-DE","es-ES","it-IT","pt-PT","pl-PL","sv-SE","da-DK","fi-FI","no-NO"],"description":"Language of any copy rendered into it."}},"required":["id","kind","width","height","language"]},"description":"For an **image** creative these are alternatives: pick the ones whose aspect ratio suits each channel.\n\nFor a **carousel** they are the deck’s slides, in order, and are published together — pass all of them, not a selection."},"created_at":{"type":"string","format":"date-time","description":"When the creative was created."}},"required":["id","name","kind","formats","created_at"]}}}},"401":{"description":"Missing, unknown, revoked, or expired key (`API_KEY_UNAUTHORIZED`).","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"403":{"description":"The key lacks `creatives:write`, or the project is outside its allowlist.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"404":{"description":"No such creative or format (`API_CREATIVE_NOT_FOUND`, `API_FORMAT_NOT_FOUND`).","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"409":{"description":"It is the creative's only format (`API_FORMAT_LAST_REMAINING`).","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"429":{"description":"Too many requests for this key (`API_RATE_LIMITED`). Back off and retry; `Retry-After` gives the window length.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}}}}},"/projects/{project_id}/posts":{"post":{"tags":["Posts"],"summary":"/posts","description":"Creates a post from an existing creative. A post targets one channel, so naming several channels creates several posts and the response is always a list.\n\nSet `publish_type` to decide what happens: `draft` saves it, `scheduled` needs a future `scheduled_at`, and `immediate` publishes now and rejects a timestamp. It is required — there is no default, because this endpoint can publish.\n\nPublishing is asynchronous and `scheduled_at` is a *not before* time, not an exact one: posts typically go out within a few seconds of it. Do not build a countdown that assumes the second.\n\nSend an `Idempotency-Key` header on this endpoint. Publishing is not reversible, and a retry after a timeout would otherwise post twice. Reuse the same key when you retry: if the first request finished you get its original response back, and if it is still running you get `409` `API_IDEMPOTENCY_KEY_IN_FLIGHT` — wait a moment and retry again rather than sending a new key. Retrying with the same key is safe in every ordinary case, but it is a best-effort guard rather than a guarantee: two requests fired at the same instant can still both go through, so retry in sequence rather than in parallel.\n\nRequires the `posts:write` scope.","operationId":"post_CreatePostEndpoint","parameters":[{"schema":{"type":"string","description":"The project to create the post in."},"required":true,"description":"The project to create the post in.","name":"project_id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"allOf":[{"type":"object","properties":{"creative_id":{"type":"string","description":"A creative id from `/creatives/upload`."},"format_ids":{"type":"array","items":{"type":"string"},"minItems":1,"description":"Which of the creative’s formats to publish. Ids come from its `formats`."},"channels":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"A channel id from `/channels`."},"settings":{"type":"object","properties":{"link_url":{"type":"string","description":"Destination URL. X shows it as a call to action on media and a Pin opens it; other channels ignore it."},"meta":{"type":"object","properties":{"mode":{"type":"string","enum":["feed_post","story_post","reel_post"],"description":"Which Instagram surface to publish to. A Facebook Page takes `feed_post` only. A single video on `feed_post` publishes as a Reel — Instagram has no other container for it."},"title":{"type":"string","maxLength":255,"description":"Headline for a Facebook Page video. Ignored elsewhere."},"share_to_feed":{"type":"boolean","description":"Reels only. `false` keeps the Reel out of your profile feed, leaving it in the Reels tab."},"trial_reel":{"type":"boolean","description":"Reels only. Publishes to non-followers first. Accounts Instagram finds ineligible publish an ordinary Reel instead, reported back on the post."},"ai_generated":{"type":"boolean","description":"Discloses AI use. Instagram requires it for photorealistic video."},"collaborators":{"type":"array","items":{"type":"string","minLength":1},"maxItems":3,"description":"Up to 3 public Instagram usernames, without the leading @, invited to co-author. Instagram does not allow these on a trial reel or a Story."}},"required":["mode"],"description":"Instagram and Facebook Page settings. A Reel cover and per-image alt text are set in ProductByte rather than here: both are keyed by internal asset ids this API does not expose."},"tiktok":{"type":"object","properties":{"title":{"type":"string","maxLength":90,"description":"Title for the TikTok post. At most 90 characters."},"direct_post":{"type":"object","properties":{"privacy_level":{"type":"string","enum":["PUBLIC_TO_EVERYONE","MUTUAL_FOLLOW_FRIENDS","FOLLOWER_OF_CREATOR","SELF_ONLY"],"description":"Who can see the post. Required, and must be one the creator currently allows."},"allow_comment":{"type":"boolean","description":"Whether viewers may comment."},"allow_duet":{"type":"boolean","description":"Video only."},"allow_stitch":{"type":"boolean","description":"Video only."},"auto_add_music":{"type":"boolean","description":"Photo posts only."},"is_aigc":{"type":"boolean","description":"Discloses AI-generated content."},"your_brand":{"type":"boolean","description":"The post promotes your own brand."},"branded_content":{"type":"boolean","description":"The post promotes a third party. Cannot be combined with `SELF_ONLY`."}},"required":["privacy_level","allow_comment"],"description":"Publishes straight to TikTok instead of sending the video to the creator’s inbox.\n\nOnly on a `draft`. TikTok requires the creator to see its consent terms — the Branded Content Policy and Music Usage Confirmation — and to choose from the privacy options their account currently offers. An API call cannot show them either, so a Direct Post is prepared here and published from ProductByte, where those screens exist. Sending this with `publish_type` of `scheduled` or `immediate` is refused."}},"description":"TikTok settings."},"pinterest":{"type":"object","properties":{"board_id":{"type":"string","minLength":1,"description":"The board the Pin goes on, one of the account’s boards."},"title":{"type":"string","maxLength":100,"description":"The Pin title. At most 100 characters."},"alt_text":{"type":"string","maxLength":500,"description":"What the picture shows, for screen readers. At most 500 characters."}},"required":["board_id"],"description":"Pinterest settings. Required on a Pinterest channel: every Pin goes on a board."}},"description":"How this post publishes on this channel."}},"required":["id"]},"minItems":1,"description":"Channels to publish to, from `/channels`, each with optional settings. One post is created per channel."},"campaign_id":{"type":"string","description":"Group the posts under this campaign, from `/campaigns`. Omit to leave them unassigned — they can be assigned later in ProductByte."},"copy":{"type":"string","description":"The text published alongside the media."},"replies":{"type":"array","items":{"type":"object","properties":{"copy":{"type":"string","minLength":1,"description":"The comment text. Must not be empty."},"delay_seconds":{"type":"integer","minimum":0,"maximum":2592000,"description":"Wait after the previous item publishes, in seconds. At most 30 days."}},"required":["copy"]},"description":"Comments to publish under the post once it is live, in order. Not every channel supports them."},"auto_delete_after_seconds":{"type":"integer","minimum":60,"description":"Remove the post from the channel this long after it publishes. Whole minutes, 60 seconds minimum. Not supported on Instagram or TikTok."}},"required":["creative_id","format_ids","channels","copy"]},{"oneOf":[{"type":"object","properties":{"publish_type":{"type":"string","enum":["draft"]},"scheduled_at":{"type":"string","format":"date-time","description":"An intended time. Nothing publishes."}},"required":["publish_type"]},{"type":"object","properties":{"publish_type":{"type":"string","enum":["scheduled"]},"scheduled_at":{"type":"string","format":"date-time","description":"When to publish. Required, and must be in the future."}},"required":["publish_type","scheduled_at"]},{"type":"object","properties":{"publish_type":{"type":"string","enum":["immediate"]},"scheduled_at":{"type":"string","format":"date-time","description":"Must be omitted. `immediate` publishes now, so a time would be contradictory."}},"required":["publish_type"]}]}]}}}},"responses":{"201":{"description":"One post per channel.","content":{"application/json":{"schema":{"type":"object","properties":{"posts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Identifier for this post."},"channel_id":{"type":"string","description":"The channel it publishes to."},"campaign_id":{"type":"string","description":"The campaign it belongs to. Absent when the post is unassigned — posts still need a home internally, but that grouping is bookkeeping and is never reported as a campaign."},"status":{"type":"string","enum":["draft","scheduled","publishing","published","failed","deleted"],"description":"Where the post actually got to. Poll this to learn the outcome."},"copy":{"type":"string","description":"The text published alongside the media."},"scheduled_at":{"type":"string","format":"date-time","description":"When it publishes, if it is scheduled."},"published_at":{"type":"string","format":"date-time","description":"When it published, once it has."},"permalink":{"type":"string","description":"Link to the published post, when the provider gives one."},"updated_at":{"type":"string","format":"date-time","description":"When the post last changed. Pass the newest one back as `updated_since` to poll for what changed."},"replies":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Identifier for this reply."},"copy":{"type":"string","description":"The comment text."},"delay_seconds":{"type":"integer","description":"Wait after the previous item published."},"status":{"type":"string","enum":["pending","publishing","published","failed","skipped"],"description":"Where this reply got to. `skipped` means the chain stopped before reaching it."},"published_at":{"type":"string","format":"date-time","description":"When it published, once it has."}},"required":["id","copy","status"]},"description":"Comments queued under this post, in the order they publish. Absent when there are none."},"settings":{"type":"object","properties":{"link_url":{"type":"string","description":"Destination URL. X shows it as a call to action on media and a Pin opens it; other channels ignore it."},"meta":{"type":"object","properties":{"mode":{"type":"string","enum":["feed_post","story_post","reel_post"],"description":"Which Instagram surface to publish to. A Facebook Page takes `feed_post` only. A single video on `feed_post` publishes as a Reel — Instagram has no other container for it."},"title":{"type":"string","maxLength":255,"description":"Headline for a Facebook Page video. Ignored elsewhere."},"share_to_feed":{"type":"boolean","description":"Reels only. `false` keeps the Reel out of your profile feed, leaving it in the Reels tab."},"trial_reel":{"type":"boolean","description":"Reels only. Publishes to non-followers first. Accounts Instagram finds ineligible publish an ordinary Reel instead, reported back on the post."},"ai_generated":{"type":"boolean","description":"Discloses AI use. Instagram requires it for photorealistic video."},"collaborators":{"type":"array","items":{"type":"string","minLength":1},"maxItems":3,"description":"Up to 3 public Instagram usernames, without the leading @, invited to co-author. Instagram does not allow these on a trial reel or a Story."}},"required":["mode"],"description":"Instagram and Facebook Page settings. A Reel cover and per-image alt text are set in ProductByte rather than here: both are keyed by internal asset ids this API does not expose."},"tiktok":{"type":"object","properties":{"title":{"type":"string","maxLength":90,"description":"Title for the TikTok post. At most 90 characters."},"direct_post":{"type":"object","properties":{"privacy_level":{"type":"string","enum":["PUBLIC_TO_EVERYONE","MUTUAL_FOLLOW_FRIENDS","FOLLOWER_OF_CREATOR","SELF_ONLY"],"description":"Who can see the post. Required, and must be one the creator currently allows."},"allow_comment":{"type":"boolean","description":"Whether viewers may comment."},"allow_duet":{"type":"boolean","description":"Video only."},"allow_stitch":{"type":"boolean","description":"Video only."},"auto_add_music":{"type":"boolean","description":"Photo posts only."},"is_aigc":{"type":"boolean","description":"Discloses AI-generated content."},"your_brand":{"type":"boolean","description":"The post promotes your own brand."},"branded_content":{"type":"boolean","description":"The post promotes a third party. Cannot be combined with `SELF_ONLY`."}},"required":["privacy_level","allow_comment"],"description":"Publishes straight to TikTok instead of sending the video to the creator’s inbox.\n\nOnly on a `draft`. TikTok requires the creator to see its consent terms — the Branded Content Policy and Music Usage Confirmation — and to choose from the privacy options their account currently offers. An API call cannot show them either, so a Direct Post is prepared here and published from ProductByte, where those screens exist. Sending this with `publish_type` of `scheduled` or `immediate` is refused."}},"description":"TikTok settings."},"pinterest":{"type":"object","properties":{"board_id":{"type":"string","minLength":1,"description":"The board the Pin goes on, one of the account’s boards."},"title":{"type":"string","maxLength":100,"description":"The Pin title. At most 100 characters."},"alt_text":{"type":"string","maxLength":500,"description":"What the picture shows, for screen readers. At most 500 characters."}},"required":["board_id"],"description":"Pinterest settings. Required on a Pinterest channel: every Pin goes on a board."}},"description":"How this post publishes, as stored. Covers only what this API accepts — a post made in ProductByte may carry more, such as a Reel cover. `link_url` is the effective link: a campaign’s UTM tags are applied to it, so it can differ from the one sent."}},"required":["id","channel_id","status","copy","updated_at"]},"description":"One post per channel, in the order the channels were given."}},"required":["posts"]}}}},"400":{"description":"The body is invalid, or the schedule time is in the past (`API_REQUEST_INVALID`).","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"401":{"description":"Missing, unknown, revoked, or expired key (`API_KEY_UNAUTHORIZED`).","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"403":{"description":"The key lacks `posts:write`, the project is outside its allowlist, or the workspace is at its scheduled-post limit.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"404":{"description":"No such creative, format, or channel in this project.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"409":{"description":"The `Idempotency-Key` was already used with a different body (`API_IDEMPOTENCY_KEY_REUSED`), or the first request using it is still running (`API_IDEMPOTENCY_KEY_IN_FLIGHT`) — retry in a moment to collect its response.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"422":{"description":"A chosen format cannot publish to a chosen channel — wrong aspect ratio, or video where only stills are accepted (`API_FORMAT_NOT_PUBLISHABLE`), the channel cannot publish comments (`API_REPLIES_UNSUPPORTED`), or it cannot auto-delete (`API_AUTO_DELETE_UNSUPPORTED`). `details` names what was refused.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"429":{"description":"Too many requests for this key (`API_RATE_LIMITED`). Back off and retry; `Retry-After` gives the window length.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}}}},"get":{"tags":["Posts"],"summary":"/posts","description":"Lists the project’s posts, newest first. Filter by `status` to reconcile a batch — for example `published` after scheduling a month of content.\n\nTo follow outcomes over time, poll with `updated_since` rather than re-reading the list: it returns only what changed, oldest change first, so a quiet minute costs one empty response. There are no webhooks; this is the intended way to learn that a post published or failed.\n\nPaginate with `cursor`: pass the `next_cursor` from the previous page. It is absent on the last page.\n\nRequires the `posts:read` scope.","operationId":"get_ListPostsEndpoint","parameters":[{"schema":{"type":"string","description":"The project to list posts for."},"required":true,"description":"The project to list posts for.","name":"project_id","in":"path"},{"schema":{"type":"string","enum":["draft","scheduled","publishing","published","failed","deleted"],"description":"Only posts in this state."},"required":false,"description":"Only posts in this state.","name":"status","in":"query"},{"schema":{"type":"string","format":"date-time","description":"Only posts changed at or after this moment, **oldest change first** — the reverse of the default order. Poll with this instead of re-reading everything: keep the last post’s `updated_at` and pass it back next time. The bound is inclusive, so you will see that post again; deduplicate by `id`."},"required":false,"description":"Only posts changed at or after this moment, **oldest change first** — the reverse of the default order. Poll with this instead of re-reading everything: keep the last post’s `updated_at` and pass it back next time. The bound is inclusive, so you will see that post again; deduplicate by `id`.","name":"updated_since","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50,"description":"Page size, 1–100."},"required":false,"description":"Page size, 1–100.","name":"limit","in":"query"},{"schema":{"type":"string","description":"A `next_cursor` from a previous page. Treat it as opaque, and keep the same `status` and `updated_since` when continuing that list."},"required":false,"description":"A `next_cursor` from a previous page. Treat it as opaque, and keep the same `status` and `updated_since` when continuing that list.","name":"cursor","in":"query"}],"responses":{"200":{"description":"A page of posts.","content":{"application/json":{"schema":{"type":"object","properties":{"posts":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Identifier for this post."},"channel_id":{"type":"string","description":"The channel it publishes to."},"campaign_id":{"type":"string","description":"The campaign it belongs to. Absent when the post is unassigned — posts still need a home internally, but that grouping is bookkeeping and is never reported as a campaign."},"status":{"type":"string","enum":["draft","scheduled","publishing","published","failed","deleted"],"description":"Where the post actually got to. Poll this to learn the outcome."},"copy":{"type":"string","description":"The text published alongside the media."},"scheduled_at":{"type":"string","format":"date-time","description":"When it publishes, if it is scheduled."},"published_at":{"type":"string","format":"date-time","description":"When it published, once it has."},"permalink":{"type":"string","description":"Link to the published post, when the provider gives one."},"updated_at":{"type":"string","format":"date-time","description":"When the post last changed. Pass the newest one back as `updated_since` to poll for what changed."},"replies":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Identifier for this reply."},"copy":{"type":"string","description":"The comment text."},"delay_seconds":{"type":"integer","description":"Wait after the previous item published."},"status":{"type":"string","enum":["pending","publishing","published","failed","skipped"],"description":"Where this reply got to. `skipped` means the chain stopped before reaching it."},"published_at":{"type":"string","format":"date-time","description":"When it published, once it has."}},"required":["id","copy","status"]},"description":"Comments queued under this post, in the order they publish. Absent when there are none."},"settings":{"type":"object","properties":{"link_url":{"type":"string","description":"Destination URL. X shows it as a call to action on media and a Pin opens it; other channels ignore it."},"meta":{"type":"object","properties":{"mode":{"type":"string","enum":["feed_post","story_post","reel_post"],"description":"Which Instagram surface to publish to. A Facebook Page takes `feed_post` only. A single video on `feed_post` publishes as a Reel — Instagram has no other container for it."},"title":{"type":"string","maxLength":255,"description":"Headline for a Facebook Page video. Ignored elsewhere."},"share_to_feed":{"type":"boolean","description":"Reels only. `false` keeps the Reel out of your profile feed, leaving it in the Reels tab."},"trial_reel":{"type":"boolean","description":"Reels only. Publishes to non-followers first. Accounts Instagram finds ineligible publish an ordinary Reel instead, reported back on the post."},"ai_generated":{"type":"boolean","description":"Discloses AI use. Instagram requires it for photorealistic video."},"collaborators":{"type":"array","items":{"type":"string","minLength":1},"maxItems":3,"description":"Up to 3 public Instagram usernames, without the leading @, invited to co-author. Instagram does not allow these on a trial reel or a Story."}},"required":["mode"],"description":"Instagram and Facebook Page settings. A Reel cover and per-image alt text are set in ProductByte rather than here: both are keyed by internal asset ids this API does not expose."},"tiktok":{"type":"object","properties":{"title":{"type":"string","maxLength":90,"description":"Title for the TikTok post. At most 90 characters."},"direct_post":{"type":"object","properties":{"privacy_level":{"type":"string","enum":["PUBLIC_TO_EVERYONE","MUTUAL_FOLLOW_FRIENDS","FOLLOWER_OF_CREATOR","SELF_ONLY"],"description":"Who can see the post. Required, and must be one the creator currently allows."},"allow_comment":{"type":"boolean","description":"Whether viewers may comment."},"allow_duet":{"type":"boolean","description":"Video only."},"allow_stitch":{"type":"boolean","description":"Video only."},"auto_add_music":{"type":"boolean","description":"Photo posts only."},"is_aigc":{"type":"boolean","description":"Discloses AI-generated content."},"your_brand":{"type":"boolean","description":"The post promotes your own brand."},"branded_content":{"type":"boolean","description":"The post promotes a third party. Cannot be combined with `SELF_ONLY`."}},"required":["privacy_level","allow_comment"],"description":"Publishes straight to TikTok instead of sending the video to the creator’s inbox.\n\nOnly on a `draft`. TikTok requires the creator to see its consent terms — the Branded Content Policy and Music Usage Confirmation — and to choose from the privacy options their account currently offers. An API call cannot show them either, so a Direct Post is prepared here and published from ProductByte, where those screens exist. Sending this with `publish_type` of `scheduled` or `immediate` is refused."}},"description":"TikTok settings."},"pinterest":{"type":"object","properties":{"board_id":{"type":"string","minLength":1,"description":"The board the Pin goes on, one of the account’s boards."},"title":{"type":"string","maxLength":100,"description":"The Pin title. At most 100 characters."},"alt_text":{"type":"string","maxLength":500,"description":"What the picture shows, for screen readers. At most 500 characters."}},"required":["board_id"],"description":"Pinterest settings. Required on a Pinterest channel: every Pin goes on a board."}},"description":"How this post publishes, as stored. Covers only what this API accepts — a post made in ProductByte may carry more, such as a Reel cover. `link_url` is the effective link: a campaign’s UTM tags are applied to it, so it can differ from the one sent."}},"required":["id","channel_id","status","copy","updated_at"]},"description":"Posts, newest first."},"next_cursor":{"type":"string","description":"Pass as `cursor` to fetch the next page. Absent when there are no more."}},"required":["posts"]}}}},"401":{"description":"Missing, unknown, revoked, or expired key (`API_KEY_UNAUTHORIZED`).","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"403":{"description":"The key lacks `posts:read`, or the project is outside its allowlist.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"429":{"description":"Too many requests for this key (`API_RATE_LIMITED`). Back off and retry; `Retry-After` gives the window length.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}}}}},"/projects/{project_id}/posts/{post_id}":{"get":{"tags":["Posts"],"summary":"/posts/{post_id}","description":"Returns one post and its current status.\n\nPublishing is asynchronous, so a post you created as `immediate` or `scheduled` will move through `scheduled` → `publishing` → `published`, or to `failed`. Poll this to learn the outcome; `permalink` appears once the provider returns one.\n\nRequires the `posts:read` scope.","operationId":"get_GetPostEndpoint","parameters":[{"schema":{"type":"string","description":"The project the post belongs to."},"required":true,"description":"The project the post belongs to.","name":"project_id","in":"path"},{"schema":{"type":"string","description":"The post to read."},"required":true,"description":"The post to read.","name":"post_id","in":"path"}],"responses":{"200":{"description":"The post.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Identifier for this post."},"channel_id":{"type":"string","description":"The channel it publishes to."},"campaign_id":{"type":"string","description":"The campaign it belongs to. Absent when the post is unassigned — posts still need a home internally, but that grouping is bookkeeping and is never reported as a campaign."},"status":{"type":"string","enum":["draft","scheduled","publishing","published","failed","deleted"],"description":"Where the post actually got to. Poll this to learn the outcome."},"copy":{"type":"string","description":"The text published alongside the media."},"scheduled_at":{"type":"string","format":"date-time","description":"When it publishes, if it is scheduled."},"published_at":{"type":"string","format":"date-time","description":"When it published, once it has."},"permalink":{"type":"string","description":"Link to the published post, when the provider gives one."},"updated_at":{"type":"string","format":"date-time","description":"When the post last changed. Pass the newest one back as `updated_since` to poll for what changed."},"replies":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Identifier for this reply."},"copy":{"type":"string","description":"The comment text."},"delay_seconds":{"type":"integer","description":"Wait after the previous item published."},"status":{"type":"string","enum":["pending","publishing","published","failed","skipped"],"description":"Where this reply got to. `skipped` means the chain stopped before reaching it."},"published_at":{"type":"string","format":"date-time","description":"When it published, once it has."}},"required":["id","copy","status"]},"description":"Comments queued under this post, in the order they publish. Absent when there are none."},"settings":{"type":"object","properties":{"link_url":{"type":"string","description":"Destination URL. X shows it as a call to action on media and a Pin opens it; other channels ignore it."},"meta":{"type":"object","properties":{"mode":{"type":"string","enum":["feed_post","story_post","reel_post"],"description":"Which Instagram surface to publish to. A Facebook Page takes `feed_post` only. A single video on `feed_post` publishes as a Reel — Instagram has no other container for it."},"title":{"type":"string","maxLength":255,"description":"Headline for a Facebook Page video. Ignored elsewhere."},"share_to_feed":{"type":"boolean","description":"Reels only. `false` keeps the Reel out of your profile feed, leaving it in the Reels tab."},"trial_reel":{"type":"boolean","description":"Reels only. Publishes to non-followers first. Accounts Instagram finds ineligible publish an ordinary Reel instead, reported back on the post."},"ai_generated":{"type":"boolean","description":"Discloses AI use. Instagram requires it for photorealistic video."},"collaborators":{"type":"array","items":{"type":"string","minLength":1},"maxItems":3,"description":"Up to 3 public Instagram usernames, without the leading @, invited to co-author. Instagram does not allow these on a trial reel or a Story."}},"required":["mode"],"description":"Instagram and Facebook Page settings. A Reel cover and per-image alt text are set in ProductByte rather than here: both are keyed by internal asset ids this API does not expose."},"tiktok":{"type":"object","properties":{"title":{"type":"string","maxLength":90,"description":"Title for the TikTok post. At most 90 characters."},"direct_post":{"type":"object","properties":{"privacy_level":{"type":"string","enum":["PUBLIC_TO_EVERYONE","MUTUAL_FOLLOW_FRIENDS","FOLLOWER_OF_CREATOR","SELF_ONLY"],"description":"Who can see the post. Required, and must be one the creator currently allows."},"allow_comment":{"type":"boolean","description":"Whether viewers may comment."},"allow_duet":{"type":"boolean","description":"Video only."},"allow_stitch":{"type":"boolean","description":"Video only."},"auto_add_music":{"type":"boolean","description":"Photo posts only."},"is_aigc":{"type":"boolean","description":"Discloses AI-generated content."},"your_brand":{"type":"boolean","description":"The post promotes your own brand."},"branded_content":{"type":"boolean","description":"The post promotes a third party. Cannot be combined with `SELF_ONLY`."}},"required":["privacy_level","allow_comment"],"description":"Publishes straight to TikTok instead of sending the video to the creator’s inbox.\n\nOnly on a `draft`. TikTok requires the creator to see its consent terms — the Branded Content Policy and Music Usage Confirmation — and to choose from the privacy options their account currently offers. An API call cannot show them either, so a Direct Post is prepared here and published from ProductByte, where those screens exist. Sending this with `publish_type` of `scheduled` or `immediate` is refused."}},"description":"TikTok settings."},"pinterest":{"type":"object","properties":{"board_id":{"type":"string","minLength":1,"description":"The board the Pin goes on, one of the account’s boards."},"title":{"type":"string","maxLength":100,"description":"The Pin title. At most 100 characters."},"alt_text":{"type":"string","maxLength":500,"description":"What the picture shows, for screen readers. At most 500 characters."}},"required":["board_id"],"description":"Pinterest settings. Required on a Pinterest channel: every Pin goes on a board."}},"description":"How this post publishes, as stored. Covers only what this API accepts — a post made in ProductByte may carry more, such as a Reel cover. `link_url` is the effective link: a campaign’s UTM tags are applied to it, so it can differ from the one sent."}},"required":["id","channel_id","status","copy","updated_at"]}}}},"401":{"description":"Missing, unknown, revoked, or expired key (`API_KEY_UNAUTHORIZED`).","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"403":{"description":"The key lacks `posts:read`, or the project is outside its allowlist.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"404":{"description":"No such post in this project, or it has never published and so has no analytics (`API_POST_NOT_FOUND`).","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"429":{"description":"Too many requests for this key (`API_RATE_LIMITED`). Back off and retry; `Retry-After` gives the window length.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}}}},"patch":{"tags":["Posts"],"summary":"/posts/{post_id}","description":"Edits a post that has not published yet — a draft, a failed post, or a scheduled one.\n\nA scheduled post can also be **moved**: send `scheduled_at` to change when it goes out, which re-arms it. Content edits work the same either way. A post that has published or is publishing can no longer be edited; retract or delete it instead.\n\nSend `campaign_id` to reassign the post, or `null` to unassign it. Omitted fields are left unchanged.\n\nRequires the `posts:write` scope.","operationId":"patch_UpdatePostEndpoint","parameters":[{"schema":{"type":"string","description":"The project the post belongs to."},"required":true,"description":"The project the post belongs to.","name":"project_id","in":"path"},{"schema":{"type":"string","description":"The post to edit."},"required":true,"description":"The post to edit.","name":"post_id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"copy":{"type":"string","description":"Replacement text."},"replies":{"type":"array","items":{"type":"object","properties":{"copy":{"type":"string","minLength":1,"description":"The comment text. Must not be empty."},"delay_seconds":{"type":"integer","minimum":0,"maximum":2592000,"description":"Wait after the previous item publishes, in seconds. At most 30 days."}},"required":["copy"]},"description":"Replaces the whole comment list. Send `[]` to remove every comment."},"scheduled_at":{"type":"string","format":"date-time","description":"Move when a scheduled post publishes. Must be in the future. Only meaningful for a post that is already scheduled — a draft carries its intended time instead."},"campaign_id":{"type":["string","null"],"description":"Reassign the post. Pass a campaign id from `/campaigns`, or `null` to unassign it. Omit to leave the assignment alone."},"auto_delete_after_seconds":{"type":"integer","minimum":60,"description":"Replacement auto-delete delay. Whole minutes, 60 seconds minimum."},"settings":{"type":"object","properties":{"link_url":{"type":"string","description":"Destination URL. X shows it as a call to action on media and a Pin opens it; other channels ignore it."},"meta":{"type":"object","properties":{"mode":{"type":"string","enum":["feed_post","story_post","reel_post"],"description":"Which Instagram surface to publish to. A Facebook Page takes `feed_post` only. A single video on `feed_post` publishes as a Reel — Instagram has no other container for it."},"title":{"type":"string","maxLength":255,"description":"Headline for a Facebook Page video. Ignored elsewhere."},"share_to_feed":{"type":"boolean","description":"Reels only. `false` keeps the Reel out of your profile feed, leaving it in the Reels tab."},"trial_reel":{"type":"boolean","description":"Reels only. Publishes to non-followers first. Accounts Instagram finds ineligible publish an ordinary Reel instead, reported back on the post."},"ai_generated":{"type":"boolean","description":"Discloses AI use. Instagram requires it for photorealistic video."},"collaborators":{"type":"array","items":{"type":"string","minLength":1},"maxItems":3,"description":"Up to 3 public Instagram usernames, without the leading @, invited to co-author. Instagram does not allow these on a trial reel or a Story."}},"required":["mode"],"description":"Instagram and Facebook Page settings. A Reel cover and per-image alt text are set in ProductByte rather than here: both are keyed by internal asset ids this API does not expose."},"tiktok":{"type":"object","properties":{"title":{"type":"string","maxLength":90,"description":"Title for the TikTok post. At most 90 characters."},"direct_post":{"type":"object","properties":{"privacy_level":{"type":"string","enum":["PUBLIC_TO_EVERYONE","MUTUAL_FOLLOW_FRIENDS","FOLLOWER_OF_CREATOR","SELF_ONLY"],"description":"Who can see the post. Required, and must be one the creator currently allows."},"allow_comment":{"type":"boolean","description":"Whether viewers may comment."},"allow_duet":{"type":"boolean","description":"Video only."},"allow_stitch":{"type":"boolean","description":"Video only."},"auto_add_music":{"type":"boolean","description":"Photo posts only."},"is_aigc":{"type":"boolean","description":"Discloses AI-generated content."},"your_brand":{"type":"boolean","description":"The post promotes your own brand."},"branded_content":{"type":"boolean","description":"The post promotes a third party. Cannot be combined with `SELF_ONLY`."}},"required":["privacy_level","allow_comment"],"description":"Publishes straight to TikTok instead of sending the video to the creator’s inbox.\n\nOnly on a `draft`. TikTok requires the creator to see its consent terms — the Branded Content Policy and Music Usage Confirmation — and to choose from the privacy options their account currently offers. An API call cannot show them either, so a Direct Post is prepared here and published from ProductByte, where those screens exist. Sending this with `publish_type` of `scheduled` or `immediate` is refused."}},"description":"TikTok settings."},"pinterest":{"type":"object","properties":{"board_id":{"type":"string","minLength":1,"description":"The board the Pin goes on, one of the account’s boards."},"title":{"type":"string","maxLength":100,"description":"The Pin title. At most 100 characters."},"alt_text":{"type":"string","maxLength":500,"description":"What the picture shows, for screen readers. At most 500 characters."}},"required":["board_id"],"description":"Pinterest settings. Required on a Pinterest channel: every Pin goes on a board."}},"description":"How this post publishes. Merged group by group, like the fields beside it: send `meta` to change the Instagram settings and `tiktok` is left as it was. Send an empty group — `\"meta\": {}` — to clear one."}}}}}},"responses":{"200":{"description":"The updated post.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Identifier for this post."},"channel_id":{"type":"string","description":"The channel it publishes to."},"campaign_id":{"type":"string","description":"The campaign it belongs to. Absent when the post is unassigned — posts still need a home internally, but that grouping is bookkeeping and is never reported as a campaign."},"status":{"type":"string","enum":["draft","scheduled","publishing","published","failed","deleted"],"description":"Where the post actually got to. Poll this to learn the outcome."},"copy":{"type":"string","description":"The text published alongside the media."},"scheduled_at":{"type":"string","format":"date-time","description":"When it publishes, if it is scheduled."},"published_at":{"type":"string","format":"date-time","description":"When it published, once it has."},"permalink":{"type":"string","description":"Link to the published post, when the provider gives one."},"updated_at":{"type":"string","format":"date-time","description":"When the post last changed. Pass the newest one back as `updated_since` to poll for what changed."},"replies":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Identifier for this reply."},"copy":{"type":"string","description":"The comment text."},"delay_seconds":{"type":"integer","description":"Wait after the previous item published."},"status":{"type":"string","enum":["pending","publishing","published","failed","skipped"],"description":"Where this reply got to. `skipped` means the chain stopped before reaching it."},"published_at":{"type":"string","format":"date-time","description":"When it published, once it has."}},"required":["id","copy","status"]},"description":"Comments queued under this post, in the order they publish. Absent when there are none."},"settings":{"type":"object","properties":{"link_url":{"type":"string","description":"Destination URL. X shows it as a call to action on media and a Pin opens it; other channels ignore it."},"meta":{"type":"object","properties":{"mode":{"type":"string","enum":["feed_post","story_post","reel_post"],"description":"Which Instagram surface to publish to. A Facebook Page takes `feed_post` only. A single video on `feed_post` publishes as a Reel — Instagram has no other container for it."},"title":{"type":"string","maxLength":255,"description":"Headline for a Facebook Page video. Ignored elsewhere."},"share_to_feed":{"type":"boolean","description":"Reels only. `false` keeps the Reel out of your profile feed, leaving it in the Reels tab."},"trial_reel":{"type":"boolean","description":"Reels only. Publishes to non-followers first. Accounts Instagram finds ineligible publish an ordinary Reel instead, reported back on the post."},"ai_generated":{"type":"boolean","description":"Discloses AI use. Instagram requires it for photorealistic video."},"collaborators":{"type":"array","items":{"type":"string","minLength":1},"maxItems":3,"description":"Up to 3 public Instagram usernames, without the leading @, invited to co-author. Instagram does not allow these on a trial reel or a Story."}},"required":["mode"],"description":"Instagram and Facebook Page settings. A Reel cover and per-image alt text are set in ProductByte rather than here: both are keyed by internal asset ids this API does not expose."},"tiktok":{"type":"object","properties":{"title":{"type":"string","maxLength":90,"description":"Title for the TikTok post. At most 90 characters."},"direct_post":{"type":"object","properties":{"privacy_level":{"type":"string","enum":["PUBLIC_TO_EVERYONE","MUTUAL_FOLLOW_FRIENDS","FOLLOWER_OF_CREATOR","SELF_ONLY"],"description":"Who can see the post. Required, and must be one the creator currently allows."},"allow_comment":{"type":"boolean","description":"Whether viewers may comment."},"allow_duet":{"type":"boolean","description":"Video only."},"allow_stitch":{"type":"boolean","description":"Video only."},"auto_add_music":{"type":"boolean","description":"Photo posts only."},"is_aigc":{"type":"boolean","description":"Discloses AI-generated content."},"your_brand":{"type":"boolean","description":"The post promotes your own brand."},"branded_content":{"type":"boolean","description":"The post promotes a third party. Cannot be combined with `SELF_ONLY`."}},"required":["privacy_level","allow_comment"],"description":"Publishes straight to TikTok instead of sending the video to the creator’s inbox.\n\nOnly on a `draft`. TikTok requires the creator to see its consent terms — the Branded Content Policy and Music Usage Confirmation — and to choose from the privacy options their account currently offers. An API call cannot show them either, so a Direct Post is prepared here and published from ProductByte, where those screens exist. Sending this with `publish_type` of `scheduled` or `immediate` is refused."}},"description":"TikTok settings."},"pinterest":{"type":"object","properties":{"board_id":{"type":"string","minLength":1,"description":"The board the Pin goes on, one of the account’s boards."},"title":{"type":"string","maxLength":100,"description":"The Pin title. At most 100 characters."},"alt_text":{"type":"string","maxLength":500,"description":"What the picture shows, for screen readers. At most 500 characters."}},"required":["board_id"],"description":"Pinterest settings. Required on a Pinterest channel: every Pin goes on a board."}},"description":"How this post publishes, as stored. Covers only what this API accepts — a post made in ProductByte may carry more, such as a Reel cover. `link_url` is the effective link: a campaign’s UTM tags are applied to it, so it can differ from the one sent."}},"required":["id","channel_id","status","copy","updated_at"]}}}},"400":{"description":"The body is invalid (`API_REQUEST_INVALID`).","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"401":{"description":"Missing, unknown, revoked, or expired key (`API_KEY_UNAUTHORIZED`).","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"403":{"description":"The key lacks `posts:write`, or the project is outside its allowlist.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"404":{"description":"No such post in this project (`API_POST_NOT_FOUND`).","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"409":{"description":"The post has published and can no longer be edited (`API_POST_NOT_EDITABLE`), or `scheduled_at` was sent for a post that is not scheduled (`API_POST_NOT_RESCHEDULABLE`).","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"422":{"description":"The channel cannot publish comments (`API_REPLIES_UNSUPPORTED`) or auto-delete (`API_AUTO_DELETE_UNSUPPORTED`).","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"429":{"description":"Too many requests for this key (`API_RATE_LIMITED`). Back off and retry; `Retry-After` gives the window length.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}}}},"delete":{"tags":["Posts"],"summary":"/posts/{post_id}","description":"Removes the post from ProductByte.\n\n**Nothing is removed from the channel.** Deleting a published post forgets our record of it; the post itself stays up. Deleting a scheduled post cancels it before it publishes.\n\nRequires the `posts:write` scope.","operationId":"delete_DeletePostEndpoint","parameters":[{"schema":{"type":"string","description":"The project the post belongs to."},"required":true,"description":"The project the post belongs to.","name":"project_id","in":"path"},{"schema":{"type":"string","description":"The post to delete."},"required":true,"description":"The post to delete.","name":"post_id","in":"path"}],"responses":{"204":{"description":"The post was removed."},"401":{"description":"Missing, unknown, revoked, or expired key (`API_KEY_UNAUTHORIZED`).","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"403":{"description":"The key lacks `posts:write`, or the project is outside its allowlist.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"404":{"description":"No such post in this project (`API_POST_NOT_FOUND`).","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"409":{"description":"The post is in a state that cannot be deleted, such as mid-publish (`API_POST_NOT_DELETABLE`).","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"429":{"description":"Too many requests for this key (`API_RATE_LIMITED`). Back off and retry; `Retry-After` gives the window length.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}}}}},"/projects/{project_id}/posts/{post_id}/retract":{"post":{"tags":["Posts"],"summary":"/posts/{post_id}/retract","description":"Removes the published copy from the channel and marks the post `deleted`. The ProductByte record stays, so the post keeps its history and analytics — use `DELETE` to forget the record instead.\n\nOnly a `published` post can be retracted, and only on channels that support deletion. Instagram and TikTok do not: they answer `API_CHANNEL_RETRACT_UNSUPPORTED` and the post must be removed in the provider's own app.\n\nThe channel is called synchronously. A `200` means the provider confirmed the removal; if it refuses, the post stays `published` and nothing is changed here.\n\nRequires the `posts:write` scope.","operationId":"post_RetractPostEndpoint","parameters":[{"schema":{"type":"string","description":"The project the post belongs to."},"required":true,"description":"The project the post belongs to.","name":"project_id","in":"path"},{"schema":{"type":"string","description":"The published post to retract."},"required":true,"description":"The published post to retract.","name":"post_id","in":"path"}],"responses":{"200":{"description":"The retracted post, now `deleted`.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Identifier for this post."},"channel_id":{"type":"string","description":"The channel it publishes to."},"campaign_id":{"type":"string","description":"The campaign it belongs to. Absent when the post is unassigned — posts still need a home internally, but that grouping is bookkeeping and is never reported as a campaign."},"status":{"type":"string","enum":["draft","scheduled","publishing","published","failed","deleted"],"description":"Where the post actually got to. Poll this to learn the outcome."},"copy":{"type":"string","description":"The text published alongside the media."},"scheduled_at":{"type":"string","format":"date-time","description":"When it publishes, if it is scheduled."},"published_at":{"type":"string","format":"date-time","description":"When it published, once it has."},"permalink":{"type":"string","description":"Link to the published post, when the provider gives one."},"updated_at":{"type":"string","format":"date-time","description":"When the post last changed. Pass the newest one back as `updated_since` to poll for what changed."},"replies":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Identifier for this reply."},"copy":{"type":"string","description":"The comment text."},"delay_seconds":{"type":"integer","description":"Wait after the previous item published."},"status":{"type":"string","enum":["pending","publishing","published","failed","skipped"],"description":"Where this reply got to. `skipped` means the chain stopped before reaching it."},"published_at":{"type":"string","format":"date-time","description":"When it published, once it has."}},"required":["id","copy","status"]},"description":"Comments queued under this post, in the order they publish. Absent when there are none."},"settings":{"type":"object","properties":{"link_url":{"type":"string","description":"Destination URL. X shows it as a call to action on media and a Pin opens it; other channels ignore it."},"meta":{"type":"object","properties":{"mode":{"type":"string","enum":["feed_post","story_post","reel_post"],"description":"Which Instagram surface to publish to. A Facebook Page takes `feed_post` only. A single video on `feed_post` publishes as a Reel — Instagram has no other container for it."},"title":{"type":"string","maxLength":255,"description":"Headline for a Facebook Page video. Ignored elsewhere."},"share_to_feed":{"type":"boolean","description":"Reels only. `false` keeps the Reel out of your profile feed, leaving it in the Reels tab."},"trial_reel":{"type":"boolean","description":"Reels only. Publishes to non-followers first. Accounts Instagram finds ineligible publish an ordinary Reel instead, reported back on the post."},"ai_generated":{"type":"boolean","description":"Discloses AI use. Instagram requires it for photorealistic video."},"collaborators":{"type":"array","items":{"type":"string","minLength":1},"maxItems":3,"description":"Up to 3 public Instagram usernames, without the leading @, invited to co-author. Instagram does not allow these on a trial reel or a Story."}},"required":["mode"],"description":"Instagram and Facebook Page settings. A Reel cover and per-image alt text are set in ProductByte rather than here: both are keyed by internal asset ids this API does not expose."},"tiktok":{"type":"object","properties":{"title":{"type":"string","maxLength":90,"description":"Title for the TikTok post. At most 90 characters."},"direct_post":{"type":"object","properties":{"privacy_level":{"type":"string","enum":["PUBLIC_TO_EVERYONE","MUTUAL_FOLLOW_FRIENDS","FOLLOWER_OF_CREATOR","SELF_ONLY"],"description":"Who can see the post. Required, and must be one the creator currently allows."},"allow_comment":{"type":"boolean","description":"Whether viewers may comment."},"allow_duet":{"type":"boolean","description":"Video only."},"allow_stitch":{"type":"boolean","description":"Video only."},"auto_add_music":{"type":"boolean","description":"Photo posts only."},"is_aigc":{"type":"boolean","description":"Discloses AI-generated content."},"your_brand":{"type":"boolean","description":"The post promotes your own brand."},"branded_content":{"type":"boolean","description":"The post promotes a third party. Cannot be combined with `SELF_ONLY`."}},"required":["privacy_level","allow_comment"],"description":"Publishes straight to TikTok instead of sending the video to the creator’s inbox.\n\nOnly on a `draft`. TikTok requires the creator to see its consent terms — the Branded Content Policy and Music Usage Confirmation — and to choose from the privacy options their account currently offers. An API call cannot show them either, so a Direct Post is prepared here and published from ProductByte, where those screens exist. Sending this with `publish_type` of `scheduled` or `immediate` is refused."}},"description":"TikTok settings."},"pinterest":{"type":"object","properties":{"board_id":{"type":"string","minLength":1,"description":"The board the Pin goes on, one of the account’s boards."},"title":{"type":"string","maxLength":100,"description":"The Pin title. At most 100 characters."},"alt_text":{"type":"string","maxLength":500,"description":"What the picture shows, for screen readers. At most 500 characters."}},"required":["board_id"],"description":"Pinterest settings. Required on a Pinterest channel: every Pin goes on a board."}},"description":"How this post publishes, as stored. Covers only what this API accepts — a post made in ProductByte may carry more, such as a Reel cover. `link_url` is the effective link: a campaign’s UTM tags are applied to it, so it can differ from the one sent."}},"required":["id","channel_id","status","copy","updated_at"]}}}},"401":{"description":"Missing, unknown, revoked, or expired key (`API_KEY_UNAUTHORIZED`).","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"403":{"description":"The key lacks `posts:write`, or the project is outside its allowlist.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"404":{"description":"No such post in this project (`API_POST_NOT_FOUND`).","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"409":{"description":"The post is not `published`, so there is nothing on the channel to remove (`API_POST_NOT_RETRACTABLE`).","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"422":{"description":"The channel cannot delete its own posts (`API_CHANNEL_RETRACT_UNSUPPORTED`).","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"429":{"description":"Too many requests for this key (`API_RATE_LIMITED`). Back off and retry; `Retry-After` gives the window length.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"502":{"description":"The channel refused the removal (`API_RETRACT_FAILED`). The post is unchanged and still published.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}}}}},"/projects/{project_id}/posts/{post_id}/analytics":{"get":{"tags":["Posts"],"summary":"/posts/{post_id}/analytics","description":"Returns the collected metrics for a published post.\n\nNumbers are collected from the channel on a schedule, so a post published moments ago reports nothing yet. Read `collection_status` before concluding anything from a zero: it separates \"still collecting\" from \"the account needs reconnecting\" and from genuine silence.\n\nA post that never published has no analytics and answers 404.\n\nRequires the `analytics:read` scope.","operationId":"get_GetPostAnalyticsEndpoint","parameters":[{"schema":{"type":"string","description":"The project the post belongs to."},"required":true,"description":"The project the post belongs to.","name":"project_id","in":"path"},{"schema":{"type":"string","description":"The published post to read analytics for."},"required":true,"description":"The published post to read analytics for.","name":"post_id","in":"path"}],"responses":{"200":{"description":"Metrics for the post.","content":{"application/json":{"schema":{"type":"object","properties":{"post_id":{"type":"string","description":"The post these numbers belong to."},"metrics":{"type":"object","properties":{"impressions":{"type":"integer","description":"Times the post was shown."},"likes":{"type":"integer","description":"Likes or reactions."},"comments":{"type":"integer","description":"Comments or replies."},"reposts":{"type":"integer","description":"Shares, reposts, or retweets."},"saves":{"type":"integer","description":"Times the post was saved or bookmarked."},"engagement":{"type":"integer","description":"Provider-reported total engagement."},"link_clicks":{"type":"integer","description":"Clicks on links in the post."}},"description":"Totals so far. Absent keys are metrics this channel does not report."},"collection_status":{"type":"string","enum":["active","completed","auth_required","permission_disabled","unsupported","unavailable","possibly_deleted","deleted","retrying"],"description":"Why the numbers look the way they do. `active` is still being collected and `completed` has finished; anything else explains why collection stopped, so zeroes are not mistaken for genuine silence."},"last_error_at":{"type":"string","format":"date-time","description":"When collection last failed, if it has."}},"required":["post_id","metrics","collection_status"]}}}},"401":{"description":"Missing, unknown, revoked, or expired key (`API_KEY_UNAUTHORIZED`).","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"403":{"description":"The key lacks `analytics:read`, or the project is outside its allowlist.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"404":{"description":"No such post in this project (`API_POST_NOT_FOUND`).","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"429":{"description":"Too many requests for this key (`API_RATE_LIMITED`). Back off and retry; `Retry-After` gives the window length.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}}}}},"/projects/{project_id}/products":{"get":{"tags":["Products"],"summary":"/products","description":"Lists the project’s catalogue, or searches it with `query`.\n\nPaging is by page number, not cursor: the catalogue is a search index whose order changes as it re-indexes, and a cursor would silently skip rows across a re-index. Use `total` to size the walk.\n\nRequires the `products:read` scope.","operationId":"get_ListProductsEndpoint","parameters":[{"schema":{"type":"string","description":"The project whose catalogue to read."},"required":true,"description":"The project whose catalogue to read.","name":"project_id","in":"path"},{"schema":{"type":"string","description":"Match against product name, URL, and other indexed text."},"required":false,"description":"Match against product name, URL, and other indexed text.","name":"query","in":"query"},{"schema":{"type":["integer","null"],"minimum":0,"default":0,"description":"Page to return, counting from 0."},"required":false,"description":"Page to return, counting from 0.","name":"page","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50,"description":"Page size, 1–100."},"required":false,"description":"Page size, 1–100.","name":"limit","in":"query"},{"schema":{"type":["boolean","null"],"default":false,"description":"Include each product’s `raw` fields. Off by default: a full page of catalogue documents is far larger than the named fields, and most callers never read them."},"required":false,"description":"Include each product’s `raw` fields. Off by default: a full page of catalogue documents is far larger than the named fields, and most callers never read them.","name":"include_raw","in":"query"}],"responses":{"200":{"description":"A page of products.","content":{"application/json":{"schema":{"type":"object","properties":{"products":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Identifier for this product."},"name":{"type":"string","description":"Product title."},"sku":{"type":"string","description":"The identifier your catalogue uses, when it has one."},"url":{"type":"string","description":"Link to the product page, when the catalogue provides one."},"price_cents":{"type":"integer","description":"Regular price in minor units — 2930 is €29.30. Minor units avoid rounding a decimal price."},"sale_price_cents":{"type":"integer","description":"Discounted price in minor units, when on offer."},"currency":{"type":"string","description":"Currency of the prices, as an ISO 4217 code."},"images":{"type":"array","items":{"type":"string"},"description":"Product image URLs, primary first."},"raw":{"type":"object","additionalProperties":{},"description":"The fields your catalogue exported, exactly as they arrived — `stock_status`, `on_sale`, whatever your shop sends. Only present when the request asks for it with `include_raw`.\n\n**Not a contract.** The keys are your connector’s, not ProductByte’s: they differ per catalogue, change when your shop changes, and are not versioned with this API. Build on the named fields above; reach for `raw` when you need something they do not cover."}},"required":["id"]},"description":"Products on this page."},"total":{"type":"integer","description":"How many products match, across every page."},"page":{"type":"integer","description":"The page this is, counting from 0."}},"required":["products","total","page"]}}}},"401":{"description":"Missing, unknown, revoked, or expired key (`API_KEY_UNAUTHORIZED`).","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"403":{"description":"The key lacks `products:read`, or the project is outside its allowlist.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"429":{"description":"Too many requests for this key (`API_RATE_LIMITED`). Back off and retry; `Retry-After` gives the window length.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"503":{"description":"The catalogue could not be reached (`API_PRODUCTS_UNAVAILABLE`). Retry; nothing is wrong with the request.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}}}}},"/projects/{project_id}/products/{product_id}":{"get":{"tags":["Products"],"summary":"/products/{product_id}","description":"Returns one catalogue product.\n\nUnlike the list, this always includes `raw`: you asked for a specific product, so the cost of its connector fields is one document rather than a page of them.\n\nRequires the `products:read` scope.","operationId":"get_GetProductEndpoint","parameters":[{"schema":{"type":"string","description":"The project whose catalogue to read."},"required":true,"description":"The project whose catalogue to read.","name":"project_id","in":"path"},{"schema":{"type":"string","description":"The product to read."},"required":true,"description":"The product to read.","name":"product_id","in":"path"}],"responses":{"200":{"description":"The product.","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Identifier for this product."},"name":{"type":"string","description":"Product title."},"sku":{"type":"string","description":"The identifier your catalogue uses, when it has one."},"url":{"type":"string","description":"Link to the product page, when the catalogue provides one."},"price_cents":{"type":"integer","description":"Regular price in minor units — 2930 is €29.30. Minor units avoid rounding a decimal price."},"sale_price_cents":{"type":"integer","description":"Discounted price in minor units, when on offer."},"currency":{"type":"string","description":"Currency of the prices, as an ISO 4217 code."},"images":{"type":"array","items":{"type":"string"},"description":"Product image URLs, primary first."},"raw":{"type":"object","additionalProperties":{},"description":"The fields your catalogue exported, exactly as they arrived — `stock_status`, `on_sale`, whatever your shop sends. Only present when the request asks for it with `include_raw`.\n\n**Not a contract.** The keys are your connector’s, not ProductByte’s: they differ per catalogue, change when your shop changes, and are not versioned with this API. Build on the named fields above; reach for `raw` when you need something they do not cover."}},"required":["id"]}}}},"401":{"description":"Missing, unknown, revoked, or expired key (`API_KEY_UNAUTHORIZED`).","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"403":{"description":"The key lacks `products:read`, or the project is outside its allowlist.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"404":{"description":"No such product in this catalogue (`API_PRODUCT_NOT_FOUND`).","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"429":{"description":"Too many requests for this key (`API_RATE_LIMITED`). Back off and retry; `Retry-After` gives the window length.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}},"503":{"description":"The catalogue could not be reached (`API_PRODUCTS_UNAVAILABLE`). Retry; nothing is wrong with the request.","content":{"application/json":{"schema":{"type":"object","properties":{"error_code":{"type":"string","description":"Stable machine-readable code, for example `API_KEY_SCOPE_FORBIDDEN`. Branch on this."},"details":{"type":"array","items":{"type":"object","properties":{"path":{"type":"array","items":{"type":"string"},"description":"Location of the offending field, for example `[\"copy\"]`."},"message":{"type":"string","description":"What is wrong with it."}},"required":["path","message"]},"description":"Present on validation failures. Never branch on this text; it is for humans."}},"required":["error_code"]}}}}}}}},"webhooks":{},"servers":[{"url":"https://api.productbyte.com/v1"}]}