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).
Webhooks
Register an endpoint with POST /v1/webhook-endpoints to be notified instead of polling. Events:
post.published A Post Target was publishedpost.failed A Post Target failedaccount.disconnected A Social Account was disconnected
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.
| Status | Meaning |
200 | The Workspace. |
401 | The API Key (or Agency Key) is missing, invalid or revoked (code `unauthorized`). |
403 | This 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`. |
429 | The 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.
| Status | Meaning |
200 | The endpoint with its new `secret`. |
401 | The API Key (or Agency Key) is missing, invalid or revoked (code `unauthorized`). |
403 | This 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`. |
404 | No such resource in your Workspace (code `not_found`). |
429 | The 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.
| Status | Meaning |
200 | A page of deliveries. |
401 | The API Key (or Agency Key) is missing, invalid or revoked (code `unauthorized`). |
403 | This 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`. |
404 | No such resource in your Workspace (code `not_found`). |
422 | The request is invalid (code `validation_failed`); `details.field` names the offending field when there is one. |
429 | The 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
| Status | Meaning |
200 | The endpoint (without its secret). |
401 | The API Key (or Agency Key) is missing, invalid or revoked (code `unauthorized`). |
403 | This 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`. |
404 | No such resource in your Workspace (code `not_found`). |
429 | The 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.
| Status | Meaning |
200 | The updated endpoint. |
401 | The API Key (or Agency Key) is missing, invalid or revoked (code `unauthorized`). |
403 | This 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`. |
404 | No such resource in your Workspace (code `not_found`). |
422 | The request is invalid (code `validation_failed`); `details.field` names the offending field when there is one. |
429 | The 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.
| Status | Meaning |
204 | Deleted. |
401 | The API Key (or Agency Key) is missing, invalid or revoked (code `unauthorized`). |
403 | This 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`. |
404 | No such resource in your Workspace (code `not_found`). |
429 | The 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
| Status | Meaning |
200 | A page of Webhook Endpoints. |
401 | The API Key (or Agency Key) is missing, invalid or revoked (code `unauthorized`). |
403 | This 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`. |
422 | The request is invalid (code `validation_failed`); `details.field` names the offending field when there is one. |
429 | The 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.
| Status | Meaning |
201 | Registered; includes `secret`. |
401 | The API Key (or Agency Key) is missing, invalid or revoked (code `unauthorized`). |
403 | This 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`. |
422 | The request is invalid (code `validation_failed`); `details.field` names the offending field when there is one. |
429 | The 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
| Status | Meaning |
200 | The Social Account. |
401 | The API Key (or Agency Key) is missing, invalid or revoked (code `unauthorized`). |
403 | This 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`. |
404 | No such resource in your Workspace (code `not_found`). |
429 | The 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.
| Status | Meaning |
204 | Disconnected. |
401 | The API Key (or Agency Key) is missing, invalid or revoked (code `unauthorized`). |
403 | This 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`. |
404 | No such resource in your Workspace (code `not_found`). |
429 | The 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.
| Status | Meaning |
200 | A page of Social Accounts. |
401 | The API Key (or Agency Key) is missing, invalid or revoked (code `unauthorized`). |
403 | This 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`. |
422 | The request is invalid (code `validation_failed`); `details.field` names the offending field when there is one. |
429 | The 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.
| Status | Meaning |
200 | The Post. |
401 | The API Key (or Agency Key) is missing, invalid or revoked (code `unauthorized`). |
403 | This 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`. |
404 | No such resource in your Workspace (code `not_found`). |
422 | The request is invalid (code `validation_failed`); `details.field` names the offending field when there is one. |
429 | The 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.
| Status | Meaning |
200 | The Post. |
401 | The API Key (or Agency Key) is missing, invalid or revoked (code `unauthorized`). |
403 | This 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`. |
404 | No such resource in your Workspace (code `not_found`). |
429 | The 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.
| Status | Meaning |
200 | A page of Posts. |
401 | The API Key (or Agency Key) is missing, invalid or revoked (code `unauthorized`). |
403 | This 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`. |
422 | The request is invalid (code `validation_failed`); `details.field` names the offending field when there is one. |
429 | The 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`.
| Status | Meaning |
201 | The Post (or, for a replayed Idempotency-Key, the original Post in its current state). |
401 | The API Key (or Agency Key) is missing, invalid or revoked (code `unauthorized`). |
403 | This 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`. |
422 | Invalid 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). |
429 | The 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.
| Status | Meaning |
200 | The ready Media. |
401 | The API Key (or Agency Key) is missing, invalid or revoked (code `unauthorized`). |
403 | This 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`. |
404 | No such resource in your Workspace (code `not_found`). |
409 | Nothing has been uploaded yet, or the Media is not a pending upload. Codes: `media_not_uploaded`, `media_not_pending`. |
422 | The uploaded file was rejected; the Media is now `failed`. Codes: `media_too_large`, `unsupported_media_type`. |
429 | The 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.
| Status | Meaning |
200 | The Media. |
401 | The API Key (or Agency Key) is missing, invalid or revoked (code `unauthorized`). |
403 | This 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`. |
404 | No such resource in your Workspace (code `not_found`). |
429 | The 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.
| Status | Meaning |
201 | Pending Media with upload instructions. |
401 | The API Key (or Agency Key) is missing, invalid or revoked (code `unauthorized`). |
403 | This 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`. |
422 | The request is invalid. Codes: `validation_failed`, `unsupported_media_type`, `media_too_large`. |
429 | The 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`.
| Status | Meaning |
202 | Pending Media; the copy runs in the background. |
401 | The API Key (or Agency Key) is missing, invalid or revoked (code `unauthorized`). |
403 | This 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`. |
422 | The URL is malformed or not allowed. Codes: `validation_failed`, `source_url_not_allowed`. |
429 | The 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.
| Status | Meaning |
201 | The Connect Link. |
401 | The API Key (or Agency Key) is missing, invalid or revoked (code `unauthorized`). |
403 | This 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`. |
422 | The request is invalid (code `validation_failed`); `details.field` names the offending field when there is one. |
429 | The 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).
| Status | Meaning |
200 | The Workspace, with its new status. |
401 | The Agency Key is missing, invalid or revoked (code `unauthorized`). |
403 | The 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`). |
404 | No 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. |
429 | The 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.
| Status | Meaning |
200 | The Workspace, with its new status. |
401 | The Agency Key is missing, invalid or revoked (code `unauthorized`). |
403 | A Workspace's API Key was sent instead of an Agency Key (code `agency_key_required`), or the operator disabled the Agency (code `agency_disabled`). |
404 | No 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. |
429 | The 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`.
| Status | Meaning |
204 | Revoked. |
401 | The Agency Key is missing, invalid or revoked (code `unauthorized`). |
403 | A Workspace's API Key was sent instead of an Agency Key (code `agency_key_required`), or the operator disabled the Agency (code `agency_disabled`). |
404 | No 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. |
429 | The 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.
| Status | Meaning |
200 | A page of keys. |
401 | The Agency Key is missing, invalid or revoked (code `unauthorized`). |
403 | A Workspace's API Key was sent instead of an Agency Key (code `agency_key_required`), or the operator disabled the Agency (code `agency_disabled`). |
404 | No 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. |
422 | The request is invalid (code `validation_failed`); `details.field` names the offending field when there is one. |
429 | The 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.
| Status | Meaning |
201 | The new key, with its secret (shown once). |
401 | The Agency Key is missing, invalid or revoked (code `unauthorized`). |
403 | A Workspace's API Key was sent instead of an Agency Key (code `agency_key_required`), or the operator disabled the Agency (code `agency_disabled`). |
404 | No 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. |
429 | The 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.
| Status | Meaning |
200 | The Workspace. |
401 | The Agency Key is missing, invalid or revoked (code `unauthorized`). |
403 | A Workspace's API Key was sent instead of an Agency Key (code `agency_key_required`), or the operator disabled the Agency (code `agency_disabled`). |
404 | No 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. |
429 | The 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`.
| Status | Meaning |
200 | The renamed Workspace. |
401 | The Agency Key is missing, invalid or revoked (code `unauthorized`). |
403 | A Workspace's API Key was sent instead of an Agency Key (code `agency_key_required`), or the operator disabled the Agency (code `agency_disabled`). |
404 | No 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. |
422 | The request is invalid (code `validation_failed`); `details.field` names the offending field when there is one. |
429 | The 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.
| Status | Meaning |
200 | A page of Workspaces. |
401 | The Agency Key is missing, invalid or revoked (code `unauthorized`). |
403 | A Workspace's API Key was sent instead of an Agency Key (code `agency_key_required`), or the operator disabled the Agency (code `agency_disabled`). |
422 | The request is invalid (code `validation_failed`); `details.field` names the offending field when there is one. |
429 | The 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.
| Status | Meaning |
201 | The new Workspace. |
401 | The Agency Key is missing, invalid or revoked (code `unauthorized`). |
403 | A Workspace's API Key was sent instead of an Agency Key (code `agency_key_required`), or the operator disabled the Agency (code `agency_disabled`). |
422 | The 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`). |
429 | The 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.
| Status | Meaning |
200 | The Agency. |
401 | The Agency Key is missing, invalid or revoked (code `unauthorized`). |
403 | A Workspace's API Key was sent instead of an Agency Key (code `agency_key_required`), or the operator disabled the Agency (code `agency_disabled`). |
429 | The 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.
| Status | Meaning |
200 | Healthy. |
503 | The database is unreachable. |