{"openapi":"3.1.0","info":{"title":"Social API","version":"1.0.0","summary":"Read and write one workspace: its queue, its accounts, its competitors and its collected metrics.","description":"Every response is JSON. Every list is cursor-paged. Every error carries a stable `code` and a `request_id`. The full written reference is at /docs/api.","contact":{"name":"Support","url":"https://crestnote.com/contact"}},"servers":[{"url":"https://crestnote.com/api/v1"}],"security":[{"bearerAuth":[]}],"tags":[{"name":"Accounts","description":"The connected social accounts."},{"name":"Posts","description":"The publishing queue."},{"name":"Rivals","description":"Tracked competitors and what they published."},{"name":"Analytics","description":"Collected follower and post metrics."},{"name":"Media","description":"The workspace media library."}],"paths":{"/accounts":{"get":{"operationId":"listAccounts","tags":["Accounts"],"summary":"List connected accounts","description":"The social accounts this workspace publishes to. This is the call every other one depends on: a post needs account ids and nothing else in this API hands them out. `needs_reconnect` is true for any account that is not healthy, so an integration can stop queueing to a channel a human has to fix.\n\nRequires a **member** token.","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer"},"description":"Rows to return, 1 to 200. Defaults to 50.","example":"100"},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"The `next_cursor` from a previous response. Omit for the first page. Cursors are opaque and must not be constructed by hand."}],"responses":{"200":{"description":"Success.","headers":{"X-Request-Id":{"schema":{"type":"string"},"description":"On every response."},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left in the current window."}},"content":{"application/json":{"schema":{"type":"object","required":["data","has_more","next_cursor"],"properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Use this as an `accountIds` entry."},"platform":{"type":"string","description":"instagram, tiktok, threads and so on."},"handle":{"type":["string","null"],"description":"As the platform reports it."},"display_name":{"type":["string","null"],"description":"The account name."},"active":{"type":"boolean","description":"Whether the customer has it switched on."},"connection_status":{"type":"string","description":"active, needs_reauth, revoked or needs_verification. The three unhealthy ones are different problems and are not collapsed."},"needs_reconnect":{"type":"boolean","description":"True for anything that is not active. Stop queueing to it."},"token_expires_at":{"type":["string","null"],"description":"When the platform grant lapses, if it does."}}}},"has_more":{"type":"boolean"},"next_cursor":{"type":["string","null"]}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/posts":{"get":{"operationId":"listPosts","tags":["Posts"],"summary":"List posts","description":"The queue and its history, newest scheduled time first. Filter by status to find what is waiting on a human (`awaiting_approval`), what is about to go out (`scheduled`), or what already did (`published`).\n\nRequires a **member** token.","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer"},"description":"Rows to return, 1 to 200. Defaults to 50.","example":"100"},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"The `next_cursor` from a previous response. Omit for the first page. Cursors are opaque and must not be constructed by hand."},{"name":"status","in":"query","required":false,"schema":{"type":"string"},"description":"Return only posts in this state.","example":"scheduled"},{"name":"accountId","in":"query","required":false,"schema":{"type":"string"},"description":"Return only posts for one connected account."}],"responses":{"200":{"description":"Success.","headers":{"X-Request-Id":{"schema":{"type":"string"},"description":"On every response."},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left in the current window."}},"content":{"application/json":{"schema":{"type":"object","required":["data","has_more","next_cursor"],"properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The post id."},"account_id":{"type":"string","description":"Which connected account it goes to."},"platform":{"type":"string","description":"The network."},"content":{"type":"string","description":"The caption or body."},"status":{"type":"string","description":"Where it is in the queue."},"scheduled_at":{"type":"string","description":"When it publishes, as an instant with a UTC offset."},"published_at":{"type":["string","null"],"description":"When it actually went out. Null until it has."},"url":{"type":["string","null"],"description":"The permalink on the platform, once published."},"ai_generated":{"type":"boolean","description":"Whether a model wrote it. Anything created through this API is true."}}}},"has_more":{"type":"boolean"},"next_cursor":{"type":["string","null"]}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"createPost","tags":["Posts"],"summary":"Create a post","description":"Writes one post per account named, so a single call crossposts, with an optional ordered carousel from the library. It defaults to `draft`, deliberately: what this product sells is a pre-filled queue a human approves, so a caller that wants it queued has to say so. Send an `Idempotency-Key` header; a retry after a timeout would otherwise publish twice.\n\nRequires a **admin** token.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"},"description":"A unique string per logical operation. A repeat of the same key returns the first response."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["accountIds","content"],"properties":{"accountIds":{"type":"array","description":"One or more account ids from `GET /accounts`. Each becomes its own post."},"content":{"type":"string","description":"The caption or body."},"status":{"type":"string","description":"`draft` (default) or `scheduled`."},"scheduledAt":{"type":"string","description":"Required when status is `scheduled`. Must carry a UTC offset: `2026-08-20T09:00:00Z`. A time with no zone is refused rather than guessed."},"mediaIds":{"type":"array","description":"Asset ids from `GET /media`, in the order they should appear. Checked against every platform in the call before anything is written, so a five-image post to X is refused here rather than at publishing time. Instagram and TikTok cannot publish text alone, so a post to either needs this."}}}}}},"responses":{"201":{"description":"Success.","headers":{"X-Request-Id":{"schema":{"type":"string"},"description":"On every response."},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left in the current window."}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","description":"The created posts, one per account id."}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/posts/{id}":{"get":{"operationId":"getPost","tags":["Posts"],"summary":"Retrieve a post","description":"One post by id. 404 for an id in another workspace, which is the same answer as an id that does not exist.\n\nRequires a **member** token.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"The post id."}],"responses":{"200":{"description":"Success.","headers":{"X-Request-Id":{"schema":{"type":"string"},"description":"On every response."},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left in the current window."}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","description":"The post."}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"patch":{"operationId":"updatePost","tags":["Posts"],"summary":"Update a post","description":"Change the content, the time, or whether it is queued. Only a post that has not gone out can be edited. A published post is refused, because editing our row would change our record of what was published without changing what is on the platform.\n\nRequires a **admin** token.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"The post id."},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"},"description":"A unique string per logical operation. A repeat of the same key returns the first response."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":[],"properties":{"content":{"type":"string","description":"Replacement text."},"scheduledAt":{"type":"string","description":"A new time, with a UTC offset."},"status":{"type":"string","description":"`draft` or `scheduled`."}}}}}},"responses":{"200":{"description":"Success.","headers":{"X-Request-Id":{"schema":{"type":"string"},"description":"On every response."},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left in the current window."}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","description":"The updated post."}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}},"delete":{"operationId":"cancelPost","tags":["Posts"],"summary":"Cancel a post","description":"Sets the post to `cancelled`. It is a soft cancel rather than a delete: the metrics collector and the recycler both reason about history, and a hole where a post used to be is indistinguishable from a post that never existed. Cancelling an already cancelled post returns it unchanged, so a retry is safe.\n\nRequires a **admin** token.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"},"description":"The post id."},{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"},"description":"A unique string per logical operation. A repeat of the same key returns the first response."}],"responses":{"200":{"description":"Success.","headers":{"X-Request-Id":{"schema":{"type":"string"},"description":"On every response."},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left in the current window."}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","description":"The cancelled post."}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/rivals":{"get":{"operationId":"listRivals","tags":["Rivals"],"summary":"List tracked competitors","description":"The competitor profiles this workspace watches. `tracking_status` is worth reading rather than ignoring: a `pending` profile has never resolved and has no history behind it, so a chart drawn from it is showing the absence of a collector.\n\nRequires a **member** token.","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer"},"description":"Rows to return, 1 to 200. Defaults to 50.","example":"100"},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"The `next_cursor` from a previous response. Omit for the first page. Cursors are opaque and must not be constructed by hand."},{"name":"platform","in":"query","required":false,"schema":{"type":"string"},"description":"Only competitors on one network.","example":"youtube"}],"responses":{"200":{"description":"Success.","headers":{"X-Request-Id":{"schema":{"type":"string"},"description":"On every response."},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left in the current window."}},"content":{"application/json":{"schema":{"type":"object","required":["data","has_more","next_cursor"],"properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Use as `profileId` on `/rivals/posts`."},"platform":{"type":"string","description":"The network."},"handle":{"type":"string","description":"As the customer entered it."},"display_name":{"type":["string","null"],"description":"As the platform reports it."},"profile_url":{"type":["string","null"],"description":"The permalink."},"tracking_status":{"type":"string","description":"active, paused, pending or failing."},"last_error":{"type":["string","null"],"description":"Why collection is failing, when it is."},"last_collected_at":{"type":["string","null"],"description":"The last successful read."}}}},"has_more":{"type":"boolean"},"next_cursor":{"type":["string","null"]}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/rivals/posts":{"get":{"operationId":"listRivalPosts","tags":["Rivals"],"summary":"List competitor posts","description":"What tracked competitors published, with the counts we were able to read. `title` and `excerpt` are null on rows older than the platform retention window: the counts are kept and the text is purged, so an absent excerpt is a normal row and never a missing post.\n\nRequires a **member** token.","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer"},"description":"Rows to return, 1 to 200. Defaults to 50.","example":"100"},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"The `next_cursor` from a previous response. Omit for the first page. Cursors are opaque and must not be constructed by hand."},{"name":"profileId","in":"query","required":false,"schema":{"type":"string"},"description":"Only posts by one competitor, from `GET /rivals`."}],"responses":{"200":{"description":"Success.","headers":{"X-Request-Id":{"schema":{"type":"string"},"description":"On every response."},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left in the current window."}},"content":{"application/json":{"schema":{"type":"object","required":["data","has_more","next_cursor"],"properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Our id for the row."},"profile_id":{"type":"string","description":"Which competitor published it."},"url":{"type":"string","description":"The permalink. Always present."},"posted_at":{"type":"string","description":"When they published it."},"title":{"type":["string","null"],"description":"Null after the retention purge."},"excerpt":{"type":["string","null"],"description":"Null after the retention purge."},"likes":{"type":["integer","null"],"description":"Null when the platform did not report it."},"comments":{"type":["integer","null"],"description":"Null when unreadable."},"shares":{"type":["integer","null"],"description":"Null when unreadable."},"views":{"type":["integer","null"],"description":"Null when unreadable."}}}},"has_more":{"type":"boolean"},"next_cursor":{"type":["string","null"]}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/analytics":{"get":{"operationId":"readAnalytics","tags":["Analytics"],"summary":"Read collected metrics","description":"Two series behind one path, chosen with `?series=`. They used to come back together under one limit, which meant a caller who wanted a year of follower history also got a year of post metrics and could page through neither. Every number is exactly as collected: a metric the platform did not return is null and is never a zero.\n\nRequires a **member** token.","parameters":[{"name":"series","in":"query","required":true,"schema":{"type":"string","enum":["followers","posts"]},"description":"`followers` for daily audience counts, `posts` for per-post performance.","example":"followers"},{"name":"platform","in":"query","required":false,"schema":{"type":"string"},"description":"Only one network."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer"},"description":"Rows to return, 1 to 200. Defaults to 50.","example":"100"},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"The `next_cursor` from a previous response. Omit for the first page. Cursors are opaque and must not be constructed by hand."}],"responses":{"200":{"description":"Success.","headers":{"X-Request-Id":{"schema":{"type":"string"},"description":"On every response."},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left in the current window."}},"content":{"application/json":{"schema":{"type":"object","required":["data","has_more","next_cursor"],"properties":{"data":{"type":"array","items":{"type":"object","properties":{"platform":{"type":"string","description":"The network."},"date":{"type":"string","description":"followers only: the day of the reading."},"followers":{"type":["integer","null"],"description":"followers only."},"post_id":{"type":"string","description":"posts only: which post."},"age_hours":{"type":"integer","description":"posts only: how old it was when read."},"likes":{"type":["integer","null"],"description":"posts only. Null when unreadable."},"views":{"type":["integer","null"],"description":"posts only. Null when unreadable."}}}},"has_more":{"type":"boolean"},"next_cursor":{"type":["string","null"]}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/media":{"get":{"operationId":"listMedia","tags":["Media"],"summary":"List media","description":"The workspace library. Upload in two calls: `POST /media` for a signed URL, then `POST /media/register` once the bytes are in place.\n\nRequires a **member** token.","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer"},"description":"Rows to return, 1 to 200. Defaults to 50.","example":"100"},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"The `next_cursor` from a previous response. Omit for the first page. Cursors are opaque and must not be constructed by hand."}],"responses":{"200":{"description":"Success.","headers":{"X-Request-Id":{"schema":{"type":"string"},"description":"On every response."},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left in the current window."}},"content":{"application/json":{"schema":{"type":"object","required":["data","has_more","next_cursor"],"properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"The asset id. This is what `mediaIds` takes."},"kind":{"type":"string","description":"image or video."},"url":{"type":"string","description":"Where it is served from."},"mime_type":{"type":"string","description":"As uploaded."},"width":{"type":["integer","null"],"description":"Pixels, when known."},"height":{"type":["integer","null"],"description":"Pixels, when known."},"byte_size":{"type":["integer","null"],"description":"Bytes, when known."},"filename":{"type":["string","null"],"description":"The original name."},"alt_text":{"type":["string","null"],"description":"What is in the image, for somebody who cannot see it. Null means nobody has described it; an empty string means it was judged decorative."}}}},"has_more":{"type":"boolean"},"next_cursor":{"type":["string","null"]}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}}},"post":{"operationId":"startMediaUpload","tags":["Media"],"summary":"Start a media upload","description":"Returns a signed URL to PUT the file to. The bytes never pass through this API, which is what lets you upload a 200 MB video: a request body here is capped at 4.5 MB by the platform, and a binary body would be corrupted by the idempotency fingerprint before any handler saw it. Follow with `POST /media/register`.\n\nRequires a **admin** token.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"},"description":"A unique string per logical operation. A repeat of the same key returns the first response."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":[],"properties":{"filename":{"type":"string","description":"The original name. Used to work out the type when `contentType` is absent, which is what browsers do for some files."},"contentType":{"type":"string","description":"The mime type, for example `image/jpeg`. An unsupported type is refused here."},"byteSize":{"type":"integer","description":"The file size. Checked against the limit for its kind before a URL is minted, so an oversized file is refused before it is uploaded rather than after."}}}}}},"responses":{"201":{"description":"Success.","headers":{"X-Request-Id":{"schema":{"type":"string"},"description":"On every response."},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left in the current window."}},"content":{"application/json":{"schema":{"type":"object","properties":{"storagePath":{"type":"string","description":"Pass this back to `/media/register`."},"signedUrl":{"type":"string","description":"PUT the file here."},"token":{"type":"string","description":"The upload token, for clients that take one."},"kind":{"type":"string","description":"image or video, decided from the type."},"contentType":{"type":"string","description":"The normalised mime type."}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/media/register":{"post":{"operationId":"registerMediaUpload","tags":["Media"],"summary":"Finish a media upload","description":"Records the uploaded object as an asset, and returns the id `mediaIds` takes. The object is checked for real existence in storage first, so a PUT that failed silently cannot leave a library row whose image is missing. Registering a path that is already in the library answers 409.\n\nRequires a **admin** token.","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string"},"description":"A unique string per logical operation. A repeat of the same key returns the first response."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["storagePath","contentType"],"properties":{"storagePath":{"type":"string","description":"The `storagePath` from `POST /media`."},"contentType":{"type":"string","description":"The same type declared at step one. It must agree with the path."},"altText":{"type":"string","description":"What is in the image, for somebody who cannot see it. Worth sending: some networks require the field, and one that arrives without a description publishes an empty one."},"width":{"type":"integer","description":"Pixels. Improves how some networks frame it."},"height":{"type":"integer","description":"Pixels."},"durationSeconds":{"type":"integer","description":"For a video."},"folderId":{"type":"string","description":"A folder in this workspace to file it under."}}}}}},"responses":{"201":{"description":"Success.","headers":{"X-Request-Id":{"schema":{"type":"string"},"description":"On every response."},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left in the current window."}},"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","description":"The created asset."}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}},"/platform-limits":{"get":{"operationId":"listPlatformLimits","tags":["Media"],"summary":"What each network accepts","description":"Caption ceilings, the unit each network counts them in, how much media each takes, and whether we can publish there at all. Read this rather than hardcoding it: these are somebody else's numbers, they move, and what comes back is what the publish path is currently enforcing rather than a summary of it.\n\nRequires a **member** token.","responses":{"200":{"description":"Success.","headers":{"X-Request-Id":{"schema":{"type":"string"},"description":"On every response."},"RateLimit-Remaining":{"schema":{"type":"integer"},"description":"Requests left in the current window."}},"content":{"application/json":{"schema":{"type":"object","properties":{"platform":{"type":"string","description":"The id used everywhere else in this API."},"label":{"type":"string","description":"What a person calls it."},"char_limit":{"type":"integer","description":"The caption ceiling."},"char_unit":{"type":"string","description":"`characters` or `graphemes`. Bluesky counts graphemes, so a caption of 300 emoji is 300 there and 3,300 to `String.length`. Measure in the unit named here."},"publishes":{"type":"boolean","description":"False when a post here has to be made by hand. `publish_note` says why."},"publish_note":{"type":["string","null"],"description":"Why publishing is unavailable. Null when it is available."},"media":{"type":"object","description":"What a post there accepts: `max_images`, `min_images`, `max_videos`, `text_only`, and `accepted_image_types` where the network gates on it."}}}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"A workspace API token, minted at /settings/api inside the product. Send it as `Authorization: Bearer sk_social_...`."}},"parameters":{"Limit":{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":200,"default":50},"description":"Rows to return. Between 1 and 200."},"Cursor":{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"The `next_cursor` from a previous response. Opaque: do not construct one by hand."},"IdempotencyKey":{"name":"Idempotency-Key","in":"header","required":false,"schema":{"type":"string","maxLength":255},"description":"A unique string per logical operation. A repeat of the same key returns the first response instead of doing the work again."}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["type","code","message","request_id"],"properties":{"type":{"type":"string","enum":["invalid_request_error","authentication_error","permission_error","api_error"]},"code":{"type":"string","enum":["missing_field","invalid_field","unknown_parameter","invalid_cursor","malformed_json","idempotency_conflict","not_found","method_not_allowed","missing_token","invalid_token","insufficient_role","plan_limit_reached","rate_limited","internal_error"]},"message":{"type":"string","description":"Written for a human. Do not match on it."},"param":{"type":"string","description":"The field at fault, when there is one."},"request_id":{"type":"string","description":"Quote this in a support request."}}}}}},"responses":{"Unauthorized":{"description":"No token, or not a token we know.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"The token does not rank high enough for this endpoint.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"No such resource in this workspace.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests this minute. `Retry-After` says how long to wait.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}