Authentication
Every protected endpoint expects an Authorization header. Two credential
types are accepted, and you choose whichever matches how you're calling the
API.
API keys
API keys look like sk_ followed by 32 base64url characters. They're intended
for scripts and server-side integrations — anything that runs without a human at
the keyboard. Every key is long-lived: it stays valid until you revoke it,
which is what makes it suitable for unattended automation.
There are two kinds of sk_ key, and they differ in who owns them and how
much throughput they get:
- Personal API keys. Every seat comes with one. It's a per-seat, low-throughput key for light, occasional scripting — handy for a quick one-off automation alongside your interactive work. It is tied to you as an individual rather than to automation that runs at scale.
- Service-account API keys. These are org-level keys built for headless, unattended use — CI pipelines, agents, and backend services that run with no human present. They belong to the organization rather than to any one person and run at a higher throughput tier. Service-account keys are available only on the API plan — see API plan for what it unlocks and how to turn it on. The official SDKs authenticate with this kind of key; personal seat keys and browser sessions are not supported there.
Both kinds are sent the same way: the key goes verbatim in the
Authorization header, with no Bearer prefix.
Authorization: sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
In practice — calling /me with a key:
- curl
- TypeScript SDK
- Python SDK
curl https://api.slothbox.dev/me \
-H "Authorization: sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
import { Slothbox } from "@slothbox/sdk";
// Reads SLOTHBOX_API_KEY when apiKey is omitted — an org service-account
// key. Either way the key is sent verbatim in the Authorization header —
// the SDK never adds a Bearer prefix.
const slothbox = new Slothbox({ apiKey: "sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" });
const { user, organizations } = await slothbox.me.get();
The TypeScript SDK is pre-release — see the SDK quickstart for installation and setup.
from slothbox import Slothbox
# Reads SLOTHBOX_API_KEY when api_key is omitted — an org service-account
# key. Either way the key is sent verbatim in the Authorization header —
# the SDK never adds a Bearer prefix.
client = Slothbox(api_key="sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
me = client.me.get()
The Python SDK is pre-release — see the SDK quickstart for installation and setup.
If the API plan lapses
Service-account keys are an entitlement of the API plan, so they follow it. If
the organization's API plan lapses, its service-account keys are suspended,
not deleted: a request made with one is rejected with 403 and the
machine-readable error code api_plan_lapsed:
{ "error": { "message": "…", "code": "api_plan_lapsed" } }
If the organization resubscribes, the same keys resume working automatically — there is nothing to re-issue and nothing to rotate. Authorization results are cached briefly at the edge, so both suspension and resumption can take up to five minutes to propagate. Personal API keys belong to the seat, not the API plan, and are unaffected.
Notes:
- Keys are shown once at creation time and stored as a SHA-256 hash on our side. If you lose one, revoke it and create another.
- Every key is associated with an organization. A personal key belongs to a seat within an organization; a service-account key is owned by the organization itself. A key's access is scoped to the organization it belongs to.
- Keys cannot mint more keys — see Quickstart.
Cognito ID tokens
Browsers and the official web app sign in through a Cognito user pool and send
the raw ID token in the same header — again verbatim, with no Bearer
prefix. The CLI and the MCP server authenticate the same way, exchanging an
OAuth sign-in for a session token.
Authorization: eyJraWQ...
Unlike sk_ keys, these JWT sessions are short-lived and TTL-scoped: each
token expires after a fixed window and has to be refreshed. The web app, CLI,
and MCP server refresh silently for you; if you're building a custom integration
against the user pool you'll need to do the same. For anything that runs
unattended, reach for a long-lived sk_ key instead.
Public endpoints
A small handful of routes are reachable without any credential — health checks, the GitHub OAuth callback, the public invite preview, and the CloudFormation template that customers deploy into their own account. The API reference marks these with an open lock icon.