Simple Socials API v1.0.0

API-first publishing to Facebook Pages and Instagram. Simple Socials is operated by GAU Web Solutions LLC. Limits are per Workspace and can be raised by the operator: requests per minute (default 60; over it you get `429 rate_limited` with `Retry-After`), connected Social Accounts (default 10; see Connect Links) and max file size (default 250 MB; `media_too_large`). The current limits are in `GET /v1/workspace`. A disabled Workspace (by the operator, or by its Agency) gets `403 workspace_disabled` on every endpoint (the message says who disabled it), its Connect Links stop working and its scheduled work (publishing, media copies, webhook deliveries) is paused until it is re-enabled.

Base URL https://api.simplesocials.dev. Machine-readable spec: /openapi.json.

Authentication

Send your API Key as a bearer token on every /v1 request:

Authorization: Bearer ssk_xxxxxxxx_…

A missing, invalid or revoked key gets 401 with code unauthorized.

Agencies, which own many Workspaces, get an Agency Key (ssa_xxxxxxxx_…) for the /v1/agency endpoints, sent the same way. A Workspace's API Key gets 403 agency_key_required there. With it, an Agency creates, lists, renames, disables and enables its Workspaces and manages their API Keys under /v1/agency/workspaces (up to its max_workspaces; limits stay with the operator, and Workspaces can't be deleted). Other Agencies' Workspaces are 404.

An Agency Key also works on every other /v1 endpoint on behalf of one of the Agency's Workspaces, named in a header:

Authorization: Bearer ssa_xxxxxxxx_…
Simple-Socials-Workspace: ws_…

The request then behaves exactly as if that Workspace's own API Key had been used: its data, its rate limit, its workspace_disabled check and its Idempotency-Key scope. Without the header you get 400 validation_failed; a Workspace that doesn't exist or isn't yours gets 404 not_found either way. A disabled Agency's key gets 403 agency_disabled, while the Workspaces' own API Keys keep working. A Workspace's API Key may send the header only with its own ID (another ID gets 400).

Errors

Every error uses the same JSON shape:

{"code": "validation_failed", "message": "Human-readable explanation.", "details": {}}

Stable codes:

Webhooks

Register an endpoint with POST /v1/webhook-endpoints to be notified instead of polling. Events:

Each body is {"id": "evt_…", "type": "…", "created_at": "…", "data": {…}}.

Verifying deliveries

Every delivery is a `POST` with a JSON body, signed with the endpoint's secret. To verify it: take the `Simple-Socials-Timestamp` header (Unix seconds) and the raw request body, compute HMAC-SHA256 with the secret (the whole `whsec_…` string as the key) over `timestamp + "." + body`, hex-encode it, and compare it in constant time with the `v1=` value in `Simple-Socials-Signature`. Reject deliveries whose timestamp is more than 5 minutes from your clock, to stop replays. Answer with any 2xx status within 10 seconds; anything else (including redirects and timeouts) is retried with exponential backoff for about 24 hours. Delivery is at least once and unordered: deduplicate by event `id`.

// Node.js
const crypto = require("crypto");
function verify(secret, headers, rawBody) {
  const ts = headers["simple-socials-timestamp"];
  if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;
  const want = crypto.createHmac("sha256", secret).update(ts + "." + rawBody).digest("hex");
  return headers["simple-socials-signature"].split(",").some((part) => {
    const got = part.trim().replace(/^v1=/, "");
    return got.length === want.length && crypto.timingSafeEqual(Buffer.from(got), Buffer.from(want));
  });
}

Endpoints

GET /v1/workspace

Get the current Workspace

Returns the Workspace that owns the API Key used to authenticate the request.

StatusMeaning
200The Workspace.
401The API Key (or Agency Key) is missing, invalid or revoked (code `unauthorized`).
403This Workspace is disabled (code `workspace_disabled`): by the operator (contact support) or by its Agency; the message says which. With an Agency Key, a disabled Agency gets code `agency_disabled`.
429The Workspace's (or, on `/v1/agency` endpoints, the Agency's) requests-per-minute limit was reached (code `rate_limited`). Wait `Retry-After` seconds; `details.limit` is the limit and `details.retry_after_seconds` the wait.

POST /v1/webhook-endpoints/{id}/rotate-secret

Rotate a Webhook Endpoint's secret

Replaces the signing secret immediately and returns the new one (shown only here). Every delivery attempt from now on, including retries of earlier events, is signed with the new secret.

StatusMeaning
200The endpoint with its new `secret`.
401The API Key (or Agency Key) is missing, invalid or revoked (code `unauthorized`).
403This Workspace is disabled (code `workspace_disabled`): by the operator (contact support) or by its Agency; the message says which. With an Agency Key, a disabled Agency gets code `agency_disabled`.
404No such resource in your Workspace (code `not_found`).
429The Workspace's (or, on `/v1/agency` endpoints, the Agency's) requests-per-minute limit was reached (code `rate_limited`). Wait `Retry-After` seconds; `details.limit` is the limit and `details.retry_after_seconds` the wait.

GET /v1/webhook-endpoints/{id}/deliveries

List an endpoint's deliveries

Every event sent (or being sent) to the endpoint, with each HTTP attempt.

StatusMeaning
200A page of deliveries.
401The API Key (or Agency Key) is missing, invalid or revoked (code `unauthorized`).
403This Workspace is disabled (code `workspace_disabled`): by the operator (contact support) or by its Agency; the message says which. With an Agency Key, a disabled Agency gets code `agency_disabled`.
404No such resource in your Workspace (code `not_found`).
422The request is invalid (code `validation_failed`); `details.field` names the offending field when there is one.
429The Workspace's (or, on `/v1/agency` endpoints, the Agency's) requests-per-minute limit was reached (code `rate_limited`). Wait `Retry-After` seconds; `details.limit` is the limit and `details.retry_after_seconds` the wait.

GET /v1/webhook-endpoints/{id}

Get a Webhook Endpoint

StatusMeaning
200The endpoint (without its secret).
401The API Key (or Agency Key) is missing, invalid or revoked (code `unauthorized`).
403This Workspace is disabled (code `workspace_disabled`): by the operator (contact support) or by its Agency; the message says which. With an Agency Key, a disabled Agency gets code `agency_disabled`.
404No such resource in your Workspace (code `not_found`).
429The Workspace's (or, on `/v1/agency` endpoints, the Agency's) requests-per-minute limit was reached (code `rate_limited`). Wait `Retry-After` seconds; `details.limit` is the limit and `details.retry_after_seconds` the wait.

PATCH /v1/webhook-endpoints/{id}

Update a Webhook Endpoint

Changes the fields present in the body; omitted fields stay as they are. `description: null` clears the description.

StatusMeaning
200The updated endpoint.
401The API Key (or Agency Key) is missing, invalid or revoked (code `unauthorized`).
403This Workspace is disabled (code `workspace_disabled`): by the operator (contact support) or by its Agency; the message says which. With an Agency Key, a disabled Agency gets code `agency_disabled`.
404No such resource in your Workspace (code `not_found`).
422The request is invalid (code `validation_failed`); `details.field` names the offending field when there is one.
429The Workspace's (or, on `/v1/agency` endpoints, the Agency's) requests-per-minute limit was reached (code `rate_limited`). Wait `Retry-After` seconds; `details.limit` is the limit and `details.retry_after_seconds` the wait.

DELETE /v1/webhook-endpoints/{id}

Delete a Webhook Endpoint

Removes the endpoint and its delivery history; pending retries stop.

StatusMeaning
204Deleted.
401The API Key (or Agency Key) is missing, invalid or revoked (code `unauthorized`).
403This Workspace is disabled (code `workspace_disabled`): by the operator (contact support) or by its Agency; the message says which. With an Agency Key, a disabled Agency gets code `agency_disabled`.
404No such resource in your Workspace (code `not_found`).
429The Workspace's (or, on `/v1/agency` endpoints, the Agency's) requests-per-minute limit was reached (code `rate_limited`). Wait `Retry-After` seconds; `details.limit` is the limit and `details.retry_after_seconds` the wait.

GET /v1/webhook-endpoints

List Webhook Endpoints

StatusMeaning
200A page of Webhook Endpoints.
401The API Key (or Agency Key) is missing, invalid or revoked (code `unauthorized`).
403This Workspace is disabled (code `workspace_disabled`): by the operator (contact support) or by its Agency; the message says which. With an Agency Key, a disabled Agency gets code `agency_disabled`.
422The request is invalid (code `validation_failed`); `details.field` names the offending field when there is one.
429The Workspace's (or, on `/v1/agency` endpoints, the Agency's) requests-per-minute limit was reached (code `rate_limited`). Wait `Retry-After` seconds; `details.limit` is the limit and `details.retry_after_seconds` the wait.

POST /v1/webhook-endpoints

Register a Webhook Endpoint

Registers a URL to receive signed events. The response includes the endpoint's signing `secret`, shown only here and when rotated; store it to verify deliveries. A Workspace can have at most 10 endpoints.

StatusMeaning
201Registered; includes `secret`.
401The API Key (or Agency Key) is missing, invalid or revoked (code `unauthorized`).
403This Workspace is disabled (code `workspace_disabled`): by the operator (contact support) or by its Agency; the message says which. With an Agency Key, a disabled Agency gets code `agency_disabled`.
422The request is invalid (code `validation_failed`); `details.field` names the offending field when there is one.
429The Workspace's (or, on `/v1/agency` endpoints, the Agency's) requests-per-minute limit was reached (code `rate_limited`). Wait `Retry-After` seconds; `details.limit` is the limit and `details.retry_after_seconds` the wait.

GET /v1/social-accounts/{id}

Get a Social Account

StatusMeaning
200The Social Account.
401The API Key (or Agency Key) is missing, invalid or revoked (code `unauthorized`).
403This Workspace is disabled (code `workspace_disabled`): by the operator (contact support) or by its Agency; the message says which. With an Agency Key, a disabled Agency gets code `agency_disabled`.
404No such resource in your Workspace (code `not_found`).
429The Workspace's (or, on `/v1/agency` endpoints, the Agency's) requests-per-minute limit was reached (code `rate_limited`). Wait `Retry-After` seconds; `details.limit` is the limit and `details.retry_after_seconds` the wait.

DELETE /v1/social-accounts/{id}

Disconnect a Social Account

Destroys the account's stored token and removes it from the Workspace. Connecting the same account again later restores it with the same ID.

StatusMeaning
204Disconnected.
401The API Key (or Agency Key) is missing, invalid or revoked (code `unauthorized`).
403This Workspace is disabled (code `workspace_disabled`): by the operator (contact support) or by its Agency; the message says which. With an Agency Key, a disabled Agency gets code `agency_disabled`.
404No such resource in your Workspace (code `not_found`).
429The Workspace's (or, on `/v1/agency` endpoints, the Agency's) requests-per-minute limit was reached (code `rate_limited`). Wait `Retry-After` seconds; `details.limit` is the limit and `details.retry_after_seconds` the wait.

GET /v1/social-accounts

List Social Accounts

Lists the Workspace's connected Social Accounts. Paginate with `limit` and the opaque `cursor` from the previous page's `next_cursor`; `next_cursor` is null on the last page.

StatusMeaning
200A page of Social Accounts.
401The API Key (or Agency Key) is missing, invalid or revoked (code `unauthorized`).
403This Workspace is disabled (code `workspace_disabled`): by the operator (contact support) or by its Agency; the message says which. With an Agency Key, a disabled Agency gets code `agency_disabled`.
422The request is invalid (code `validation_failed`); `details.field` names the offending field when there is one.
429The Workspace's (or, on `/v1/agency` endpoints, the Agency's) requests-per-minute limit was reached (code `rate_limited`). Wait `Retry-After` seconds; `details.limit` is the limit and `details.retry_after_seconds` the wait.

POST /v1/posts/{id}/cancel

Cancel a Post

Cancels the Post's unpublished targets (`scheduled`, or `publishing` while queued or waiting to retry); cancelled targets never publish. Targets that already published or failed are left as they are. Send `target_ids` to cancel only some targets; omit the body (or the field) to cancel all. A target whose publish request is already in flight at the platform may still publish, and then reports `published`. Returns the Post in its new state.

StatusMeaning
200The Post.
401The API Key (or Agency Key) is missing, invalid or revoked (code `unauthorized`).
403This Workspace is disabled (code `workspace_disabled`): by the operator (contact support) or by its Agency; the message says which. With an Agency Key, a disabled Agency gets code `agency_disabled`.
404No such resource in your Workspace (code `not_found`).
422The request is invalid (code `validation_failed`); `details.field` names the offending field when there is one.
429The Workspace's (or, on `/v1/agency` endpoints, the Agency's) requests-per-minute limit was reached (code `rate_limited`). Wait `Retry-After` seconds; `details.limit` is the limit and `details.retry_after_seconds` the wait.

GET /v1/posts/{id}

Get a Post

Returns the Post with each target's status, platform post ID, permalink and error.

StatusMeaning
200The Post.
401The API Key (or Agency Key) is missing, invalid or revoked (code `unauthorized`).
403This Workspace is disabled (code `workspace_disabled`): by the operator (contact support) or by its Agency; the message says which. With an Agency Key, a disabled Agency gets code `agency_disabled`.
404No such resource in your Workspace (code `not_found`).
429The Workspace's (or, on `/v1/agency` endpoints, the Agency's) requests-per-minute limit was reached (code `rate_limited`). Wait `Retry-After` seconds; `details.limit` is the limit and `details.retry_after_seconds` the wait.

GET /v1/posts

List Posts

Lists your Workspace's Posts, newest first (by `created_at`), with their targets. Filters combine with AND; date ranges include `*_after` and exclude `*_before`, and take RFC 3339 date-times with an offset. An invalid filter fails with `validation_failed`, `details.field` naming it.

StatusMeaning
200A page of Posts.
401The API Key (or Agency Key) is missing, invalid or revoked (code `unauthorized`).
403This Workspace is disabled (code `workspace_disabled`): by the operator (contact support) or by its Agency; the message says which. With an Agency Key, a disabled Agency gets code `agency_disabled`.
422The request is invalid (code `validation_failed`); `details.field` names the offending field when there is one.
429The Workspace's (or, on `/v1/agency` endpoints, the Agency's) requests-per-minute limit was reached (code `rate_limited`). Wait `Retry-After` seconds; `details.limit` is the limit and `details.retry_after_seconds` the wait.

POST /v1/posts

Create a Post

Creates a Post and fans it out into one Post Target per Social Account. The call returns at once; each target is published by a background job. Poll `GET /v1/posts/{id}` for the outcome. Set `publish_at` to schedule the Post: its targets stay `scheduled` until then; without it (or with a time in the past) they publish as soon as possible. Cancel unpublished targets with `POST /v1/posts/{id}/cancel`. Content is validated against every target's platform first, with that platform's `overrides` applied (Facebook: a caption, a link, or both; or one to 10 photos (JPEG, PNG or GIF, at most 10 MB each; several become one multi-photo post); or one video (MP4 or MOV, at most 1 GB and 20 minutes); captions up to 63,206 characters, optional with Media; photos and videos can't be mixed, and posts with Media can't have a `link` (put it in the caption). Instagram: one JPEG image, one MP4/MOV video published as a Reel, or a carousel of 2–20 images and videos; images JPEG, at most 8 MB, aspect ratio 4:5 to 1.91:1; videos 3 s to 15 min, at most 300 MB; captions up to 2,200 characters, 30 hashtags and 20 @mentions; text-only posts are rejected, and so are Posts with a `link` unless `overrides.instagram.link` removes it (`""` or `null`), naming the target). Every Media in `media_ids` must exist in your Workspace and be `ready`; Media that cleanup has deleted fails with 422 `media_deleted` (`details.field` names it), so upload it again. A problem fails the whole request with `validation_failed`, where `details.field` names the field and `details.social_account_id` the offending target. Targeting a Social Account that is `needs_reauth` fails with 422 `account_needs_reauth` (same details); reconnect it first. Send an `Idempotency-Key` header to make retries safe: repeating the request with the same key and body (in the same Workspace) returns the original Post with `Idempotent-Replayed: true` instead of creating another. Reusing a key with a different body (including `media_ids` and `overrides`) returns 422 `idempotency_key_reused`. Keys are kept for at least 24 hours. Facebook publishes a video that fits its Reels specs (portrait, about 9:16, at least 540×960, 3–90 seconds) as a Reel, and other videos as Page videos; set `overrides.facebook.video_type` to `reel` or `video` to choose (`auto` is the default). Facebook fetches the video from its Media URL; the target stays `publishing` while Facebook processes it (checked every 30 seconds, giving up after 2 hours). Facebook allows 30 API-published Reels per Page per rolling 24 hours. Creating a Post checks this first: when the Page already has 30 Reels published in the 24 hours before the Post's publish time, or pending (scheduled or in progress) and due to publish within 24 hours either side of it (an overdue one counts as publishing now), the request fails with 422 `publishing_limit_reached` naming the target (`details.field`, `details.social_account_id`, plus `cap`, `limit`, `window_seconds`, `count`). The check is repeated just before publishing (Reels in progress count): a Reel over the limit then fails its target with `publishing_limit_reached` (same details). Instagram publishes in the background through Instagram's media containers: the job creates the containers, checks once a minute until Instagram has processed them (giving up after 30 minutes), then publishes. Before creating containers it checks the account's 24-hour publishing limit (`content_publishing_limit`, currently 50 posts; a carousel counts as one), counting the account's other Instagram posts still being published through Simple Socials on top of Instagram's own count; when it is used up the target fails with `publishing_limit_reached`.

StatusMeaning
201The Post (or, for a replayed Idempotency-Key, the original Post in its current state).
401The API Key (or Agency Key) is missing, invalid or revoked (code `unauthorized`).
403This Workspace is disabled (code `workspace_disabled`): by the operator (contact support) or by its Agency; the message says which. With an Agency Key, a disabled Agency gets code `agency_disabled`.
422Invalid request (`validation_failed`), an Idempotency-Key reused with a different body (`idempotency_key_reused`), a target that `needs_reauth` (`account_needs_reauth`), deleted Media (`media_deleted`), or a target already at a platform publishing cap (`publishing_limit_reached`; `details.social_account_id` names it).
429The Workspace's (or, on `/v1/agency` endpoints, the Agency's) requests-per-minute limit was reached (code `rate_limited`). Wait `Retry-After` seconds; `details.limit` is the limit and `details.retry_after_seconds` the wait.

POST /v1/media/{id}/complete

Complete a direct upload

Checks the uploaded file and marks the Media `ready`, recording its detected type, size and, where detectable, dimensions and duration. If the file is larger than the Workspace's limit, or its bytes are not the declared type, it is deleted, the Media becomes `failed` and the error is returned. Completing `ready` Media again returns it unchanged.

StatusMeaning
200The ready Media.
401The API Key (or Agency Key) is missing, invalid or revoked (code `unauthorized`).
403This Workspace is disabled (code `workspace_disabled`): by the operator (contact support) or by its Agency; the message says which. With an Agency Key, a disabled Agency gets code `agency_disabled`.
404No such resource in your Workspace (code `not_found`).
409Nothing has been uploaded yet, or the Media is not a pending upload. Codes: `media_not_uploaded`, `media_not_pending`.
422The uploaded file was rejected; the Media is now `failed`. Codes: `media_too_large`, `unsupported_media_type`.
429The Workspace's (or, on `/v1/agency` endpoints, the Agency's) requests-per-minute limit was reached (code `rate_limited`). Wait `Retry-After` seconds; `details.limit` is the limit and `details.retry_after_seconds` the wait.

GET /v1/media/{id}

Get Media

Returns the Media's status, type, size and, once `ready`, its public URL.

StatusMeaning
200The Media.
401The API Key (or Agency Key) is missing, invalid or revoked (code `unauthorized`).
403This Workspace is disabled (code `workspace_disabled`): by the operator (contact support) or by its Agency; the message says which. With an Agency Key, a disabled Agency gets code `agency_disabled`.
404No such resource in your Workspace (code `not_found`).
429The Workspace's (or, on `/v1/agency` endpoints, the Agency's) requests-per-minute limit was reached (code `rate_limited`). Wait `Retry-After` seconds; `details.limit` is the limit and `details.retry_after_seconds` the wait.

POST /v1/media/uploads

Start a direct upload

Creates `pending` Media and returns a presigned URL. Upload the file with an HTTP `PUT` to `upload.url`, sending exactly the headers in `upload.headers` (the Content-Type is part of the signature), before `upload.expires_at` (1 hour). The bytes go straight to storage, not through the API. Then call `POST /v1/media/{id}/complete`. Supported types: `image/jpeg`, `image/png`, `image/gif`, `video/mp4`, `video/quicktime` (each platform accepts a subset when you post). `size` must not exceed the Workspace's max file size (250 MB by default); the actual upload is checked again on complete.

StatusMeaning
201Pending Media with upload instructions.
401The API Key (or Agency Key) is missing, invalid or revoked (code `unauthorized`).
403This Workspace is disabled (code `workspace_disabled`): by the operator (contact support) or by its Agency; the message says which. With an Agency Key, a disabled Agency gets code `agency_disabled`.
422The request is invalid. Codes: `validation_failed`, `unsupported_media_type`, `media_too_large`.
429The Workspace's (or, on `/v1/agency` endpoints, the Agency's) requests-per-minute limit was reached (code `rate_limited`). Wait `Retry-After` seconds; `details.limit` is the limit and `details.retry_after_seconds` the wait.

POST /v1/media

Create Media from a URL

Creates `pending` Media and copies the file at `url` into storage in the background. Poll `GET /v1/media/{id}` until it is `ready` (or `failed`, with an `error`). The URL must be publicly reachable over http(s); URLs pointing at private, loopback or link-local addresses (including through DNS or redirects) are refused with `source_url_not_allowed`. The file's type is detected from its bytes, not the server's Content-Type. Failure codes: `source_fetch_failed` (HTTP error or unreachable after retries), `source_url_not_allowed`, `media_too_large`, `unsupported_media_type`, `storage_failed`.

StatusMeaning
202Pending Media; the copy runs in the background.
401The API Key (or Agency Key) is missing, invalid or revoked (code `unauthorized`).
403This Workspace is disabled (code `workspace_disabled`): by the operator (contact support) or by its Agency; the message says which. With an Agency Key, a disabled Agency gets code `agency_disabled`.
422The URL is malformed or not allowed. Codes: `validation_failed`, `source_url_not_allowed`.
429The Workspace's (or, on `/v1/agency` endpoints, the Agency's) requests-per-minute limit was reached (code `rate_limited`). Wait `Retry-After` seconds; `details.limit` is the limit and `details.retry_after_seconds` the wait.

POST /v1/connect-links

Create a Connect Link

Creates a single-use link to a hosted page where the owner of a social account signs in with the platform and chooses which accounts to attach to your Workspace. Send the returned `url` to them. The link expires after one hour and can complete once. When `redirect_url` is given, the person is sent back to it afterwards with `status=connected`, `connect_link_id` and `social_account_ids` (comma-separated), or with `status=error`, `connect_link_id` and `error` (one of `access_denied`, `missing_permissions`, `no_accounts`, `nothing_selected`, `account_mismatch`, `platform_error`, `link_expired`, `link_used`, `invalid_state`, `account_limit_reached`, `workspace_disabled`). Your Workspace's Social Account limit (`limits.max_social_accounts`) is enforced when the person confirms their selection: accounts that are `active` or `needs_reauth` count, `disconnected` and deleted ones don't. Reconnecting an account that already counts is always allowed; if the new accounts would go over the limit, none are attached, the page answers 422 and the redirect carries `error=account_limit_reached` (the link stays usable, so they can choose fewer). **Reconnecting:** pass `social_account_id` to repair an existing Social Account (for example one that is `needs_reauth` or `disconnected`) in place. `provider` may then be omitted; if given it must match the account's. The person must sign in as that same platform account: the account picker offers only it, and signing in as anyone else fails with `error=account_mismatch` without changing anything (the link stays usable). On success the account is `active` again with the same ID and a new token.

StatusMeaning
201The Connect Link.
401The API Key (or Agency Key) is missing, invalid or revoked (code `unauthorized`).
403This Workspace is disabled (code `workspace_disabled`): by the operator (contact support) or by its Agency; the message says which. With an Agency Key, a disabled Agency gets code `agency_disabled`.
422The request is invalid (code `validation_failed`); `details.field` names the offending field when there is one.
429The Workspace's (or, on `/v1/agency` endpoints, the Agency's) requests-per-minute limit was reached (code `rate_limited`). Wait `Retry-After` seconds; `details.limit` is the limit and `details.retry_after_seconds` the wait.

POST /v1/agency/workspaces/{id}/enable

Enable a Workspace

Re-enables a Workspace your Agency disabled; held work resumes. Enabling an active Workspace changes nothing. A Workspace the operator disabled (`disabled_by` `operator`) can only be enabled by the operator: this returns `403 workspace_disabled` (contact support).

StatusMeaning
200The Workspace, with its new status.
401The Agency Key is missing, invalid or revoked (code `unauthorized`).
403The operator disabled this Workspace, so only the operator can enable it (code `workspace_disabled`; contact support). Also, as on every agency endpoint: a Workspace's API Key instead of an Agency Key (`agency_key_required`) or a disabled Agency (`agency_disabled`).
404No such Workspace (or API Key) owned by your Agency (code `not_found`). Other Agencies' Workspaces and Workspaces without an Agency look the same as unknown IDs.
429The Workspace's (or, on `/v1/agency` endpoints, the Agency's) requests-per-minute limit was reached (code `rate_limited`). Wait `Retry-After` seconds; `details.limit` is the limit and `details.retry_after_seconds` the wait.

POST /v1/agency/workspaces/{id}/disable

Disable a Workspace

Disables the Workspace the way the operator's `workspace disable` does: its API Keys (and requests for it) get `403 workspace_disabled` (saying its agency disabled it), its Connect Links stop working, and its scheduled work (publishing, media copies, webhook deliveries) is held. Data is kept. Use it to pause a client. The Workspace's `disabled_by` becomes `agency`; a Workspace the operator disabled stays disabled by the operator.

StatusMeaning
200The Workspace, with its new status.
401The Agency Key is missing, invalid or revoked (code `unauthorized`).
403A Workspace's API Key was sent instead of an Agency Key (code `agency_key_required`), or the operator disabled the Agency (code `agency_disabled`).
404No such Workspace (or API Key) owned by your Agency (code `not_found`). Other Agencies' Workspaces and Workspaces without an Agency look the same as unknown IDs.
429The Workspace's (or, on `/v1/agency` endpoints, the Agency's) requests-per-minute limit was reached (code `rate_limited`). Wait `Retry-After` seconds; `details.limit` is the limit and `details.retry_after_seconds` the wait.

DELETE /v1/agency/workspaces/{id}/api-keys/{key_id}

Revoke a Workspace's API Key

Revokes the key: it gets `401 unauthorized` from then on. It stays in the list with `revoked_at` set. An unknown, already revoked or other Workspace's key is `404 not_found`.

StatusMeaning
204Revoked.
401The Agency Key is missing, invalid or revoked (code `unauthorized`).
403A Workspace's API Key was sent instead of an Agency Key (code `agency_key_required`), or the operator disabled the Agency (code `agency_disabled`).
404No such Workspace (or API Key) owned by your Agency (code `not_found`). Other Agencies' Workspaces and Workspaces without an Agency look the same as unknown IDs.
429The Workspace's (or, on `/v1/agency` endpoints, the Agency's) requests-per-minute limit was reached (code `rate_limited`). Wait `Retry-After` seconds; `details.limit` is the limit and `details.retry_after_seconds` the wait.

GET /v1/agency/workspaces/{id}/api-keys

List a Workspace's API Keys

Lists the Workspace's API Keys, including revoked ones and keys the operator created, ordered by ID. Secrets are never returned here. Paginate with `limit` and the opaque `cursor` from the previous page's `next_cursor`; `next_cursor` is null on the last page.

StatusMeaning
200A page of keys.
401The Agency Key is missing, invalid or revoked (code `unauthorized`).
403A Workspace's API Key was sent instead of an Agency Key (code `agency_key_required`), or the operator disabled the Agency (code `agency_disabled`).
404No such Workspace (or API Key) owned by your Agency (code `not_found`). Other Agencies' Workspaces and Workspaces without an Agency look the same as unknown IDs.
422The request is invalid (code `validation_failed`); `details.field` names the offending field when there is one.
429The Workspace's (or, on `/v1/agency` endpoints, the Agency's) requests-per-minute limit was reached (code `rate_limited`). Wait `Retry-After` seconds; `details.limit` is the limit and `details.retry_after_seconds` the wait.

POST /v1/agency/workspaces/{id}/api-keys

Create an API Key for a Workspace

Issues a new API Key (`ssk_...`) for the Workspace, for a client or a single site that integrates directly with only that Workspace's access. The `secret` is returned only in this response; store it now. No request body.

StatusMeaning
201The new key, with its secret (shown once).
401The Agency Key is missing, invalid or revoked (code `unauthorized`).
403A Workspace's API Key was sent instead of an Agency Key (code `agency_key_required`), or the operator disabled the Agency (code `agency_disabled`).
404No such Workspace (or API Key) owned by your Agency (code `not_found`). Other Agencies' Workspaces and Workspaces without an Agency look the same as unknown IDs.
429The Workspace's (or, on `/v1/agency` endpoints, the Agency's) requests-per-minute limit was reached (code `rate_limited`). Wait `Retry-After` seconds; `details.limit` is the limit and `details.retry_after_seconds` the wait.

GET /v1/agency/workspaces/{id}

Get a Workspace

Returns one of the Agency's Workspaces with its limits and usage. Workspaces of other Agencies, or with no Agency, are `404 not_found` like unknown IDs. There is no DELETE: an Agency can disable a Workspace but not delete it.

StatusMeaning
200The Workspace.
401The Agency Key is missing, invalid or revoked (code `unauthorized`).
403A Workspace's API Key was sent instead of an Agency Key (code `agency_key_required`), or the operator disabled the Agency (code `agency_disabled`).
404No such Workspace (or API Key) owned by your Agency (code `not_found`). Other Agencies' Workspaces and Workspaces without an Agency look the same as unknown IDs.
429The Workspace's (or, on `/v1/agency` endpoints, the Agency's) requests-per-minute limit was reached (code `rate_limited`). Wait `Retry-After` seconds; `details.limit` is the limit and `details.retry_after_seconds` the wait.

PATCH /v1/agency/workspaces/{id}

Rename a Workspace

Changes the Workspace's name. Only `name` can be changed: limits are set by the operator, and any other field is rejected with `422 validation_failed`.

StatusMeaning
200The renamed Workspace.
401The Agency Key is missing, invalid or revoked (code `unauthorized`).
403A Workspace's API Key was sent instead of an Agency Key (code `agency_key_required`), or the operator disabled the Agency (code `agency_disabled`).
404No such Workspace (or API Key) owned by your Agency (code `not_found`). Other Agencies' Workspaces and Workspaces without an Agency look the same as unknown IDs.
422The request is invalid (code `validation_failed`); `details.field` names the offending field when there is one.
429The Workspace's (or, on `/v1/agency` endpoints, the Agency's) requests-per-minute limit was reached (code `rate_limited`). Wait `Retry-After` seconds; `details.limit` is the limit and `details.retry_after_seconds` the wait.

GET /v1/agency/workspaces

List the Agency's Workspaces

Lists every Workspace the Agency owns (created through the API or attached by the operator), in any status, with limits and usage. Ordered by ID.

StatusMeaning
200A page of Workspaces.
401The Agency Key is missing, invalid or revoked (code `unauthorized`).
403A Workspace's API Key was sent instead of an Agency Key (code `agency_key_required`), or the operator disabled the Agency (code `agency_disabled`).
422The request is invalid (code `validation_failed`); `details.field` names the offending field when there is one.
429The Workspace's (or, on `/v1/agency` endpoints, the Agency's) requests-per-minute limit was reached (code `rate_limited`). Wait `Retry-After` seconds; `details.limit` is the limit and `details.retry_after_seconds` the wait.

POST /v1/agency/workspaces

Create a Workspace

Creates an active Workspace owned by the Agency, with default limits (the operator can raise them). Then create an API Key for it, or act on it with the Agency Key. Fails with `422 limit_exceeded` once the Agency owns `limits.max_workspaces` Workspaces (`details.limit` is the maximum, `details.limit_name` is `max_workspaces`); ask the operator for more. Workspaces attached by the operator count too.

StatusMeaning
201The new Workspace.
401The Agency Key is missing, invalid or revoked (code `unauthorized`).
403A Workspace's API Key was sent instead of an Agency Key (code `agency_key_required`), or the operator disabled the Agency (code `agency_disabled`).
422The name is blank or the body has unknown fields (code `validation_failed`), or the Agency already owns `limits.max_workspaces` Workspaces (code `limit_exceeded`; `details.limit` is the maximum and `details.limit_name` is `max_workspaces`).
429The Workspace's (or, on `/v1/agency` endpoints, the Agency's) requests-per-minute limit was reached (code `rate_limited`). Wait `Retry-After` seconds; `details.limit` is the limit and `details.retry_after_seconds` the wait.

GET /v1/agency

Get the current Agency

Returns the Agency that owns the Agency Key used to authenticate the request, with its limits and how many Workspaces it owns. Endpoints under `/v1/agency` accept only an Agency Key (`ssa_...`): a Workspace's API Key gets `403 agency_key_required`. They have their own rate limit per Agency (`limits.requests_per_minute`, default 60, shared by all its keys and separate from its Workspaces' limits). A disabled Agency gets `403 agency_disabled`; its Workspaces keep working through their own API Keys.

StatusMeaning
200The Agency.
401The Agency Key is missing, invalid or revoked (code `unauthorized`).
403A Workspace's API Key was sent instead of an Agency Key (code `agency_key_required`), or the operator disabled the Agency (code `agency_disabled`).
429The Workspace's (or, on `/v1/agency` endpoints, the Agency's) requests-per-minute limit was reached (code `rate_limited`). Wait `Retry-After` seconds; `details.limit` is the limit and `details.retry_after_seconds` the wait.

GET /healthz no auth

Health check

Returns 200 when the service and its database are reachable, 503 otherwise. No authentication.

StatusMeaning
200Healthy.
503The database is unreachable.