Authentication
API keys and their three scopes, OAuth for the MCP server, rate limits, and what every refusal code means.
Two credentials reach the same account. A person in a browser rides a signed session cookie. An agent rides either an API key (the API and the CLI) or an OAuth token (the hosted MCP server). Both are judged by one scope policy.
API keys
Mint one under Settings, API keys. Name it, pick its scopes, copy it once: only its sha256 is stored, so we cannot show it again. Revoking it in the same screen kills it on the next request.
A key is slk_ followed by 40 base64url characters. Send it on every request:
Scopes
A key holds any subset of three. A cookie session is the human at the keyboard, so it implicitly holds all three.
| Scope | What it opens |
|---|---|
read | Every GET: posts, agents, runs, analytics, Brand Memory, media, identity. |
write | Drafting and editing: chat turns, files, assets, agents, queue slots, dismissals. |
publish | Anything that puts content out or marks it out: creating a post, approving, publishing, waking an agent, sending an engagement action. |
The line that matters is write against publish. A write key can draft all day and change
nothing the world sees. Only publish puts something on a real account.
Give a key the least it needs. A read-only key handed to a reporting script that leaks is a leak, not a foothold.
OAuth, for MCP
The hosted MCP server at https://shapelessai.com/mcp takes no key. It is OAuth 2.1 with dynamic
client registration and PKCE, so a host registers itself:
- Protected-resource metadata:
https://shapelessai.com/.well-known/oauth-protected-resource/mcp - Scopes:
read,write,publish,offline_access - Transport: streamable HTTP
An unauthenticated call answers 401 with a WWW-Authenticate: Bearer resource_metadata="..."
header pointing at that document, which is the handshake every MCP client keys off. Tokens are
revocable by the human under Settings, API keys, Connected apps. Setup per host is on
MCP server.
Rate limit
600 requests an hour per key. Requests that fail the scope check still count. Cookie sessions are not limited by this.
Over the limit is a 429. Back off; do not spin. Polling a job every second for ten minutes is
600 requests, which is the whole hour - use jobs_tail (or GET /api/studio/tail), which holds
one connection open instead.
Refusals
| Status | Meaning |
|---|---|
400 | The body is wrong, and the message says how. |
401 | Unknown or revoked key, or an expired OAuth token. |
402 | Out of allowance: the Free plan's ten posts for that UTC day (code: "free_daily_cap", with the day and its resetsAt) or an empty credit wallet (code: "trial_exhausted"). |
403 | The credential is missing the scope this route needs. |
409 | The request contradicts the row's current state. |
422 | A platform rule refused the content. The message names the rule. |
429 | Over the rate limit. |
503 | The service has no database. |
What a key deliberately cannot do
These refuse a bearer key. It is not an oversight:
- Key management (
/api/keys) - a key must never mint itself a wider key. - Money (
/api/checkout,/api/portal) - buying and cancelling belong to the person paying. - Sign-in and platform connections - browser redirect flows carrying platform grants. Only
GET /api/connections, the identity list, takes a key. - Webhooks and internal cron - they carry their own signatures.
The full list is on API reference.
