Authentication
The Sidedoor API authenticates with an opaque bearer token, sent as an
Authorization: Bearer <token> header on every request. The recommended way to
obtain one is an API token - a scoped, revocable
credential you create once from your operator account. This is the canonical auth
flow - other pages refer back here.
Obtain a token
Section titled “Obtain a token”Create an API token from your operator account: open Access in the sidebar, then the API tab, and click Create Token. You give it a name, a role that sets exactly what it can do, and an optional IP allow-list and expiry. The secret is shown once at creation:
sk_<secret>Copy it into your secrets store straight away - it is not shown again. See API tokens for the full walkthrough, including rotation and revocation.
An API token is bound to your operator account and authorised by the role you
give it, so it presents the same identity to the API as a logged-in operator -
your Authorization: Bearer <token> handling stays unchanged.
Make an authenticated request
Section titled “Make an authenticated request”Send the token as a bearer header. Store it in the SIDEDOOR_TOKEN environment
variable and reuse it across calls. GET /api/auth/me is a good check that a
token is valid.
curl "https://thesidedoor.co/api/auth/me" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN"const res = await fetch("https://thesidedoor.co/api/auth/me", { headers: { Authorization: `Bearer ${process.env.SIDEDOOR_TOKEN}`, },});const { user } = await res.json();$ch = curl_init("https://thesidedoor.co/api/auth/me");curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer {$token}", ],]);$data = json_decode(curl_exec($ch), true);{ "user": { "id": "usr_123", "role": "client", "name": "Jo Operator" } }Interactive login
Section titled “Interactive login”For interactive use - where a person signs in - log in with
POST /api/auth/login to mint a temporary session token instead. This suits
attended use; for unattended server-to-server integrations, prefer an
API token.
| Method | Path | Purpose | Auth |
|---|---|---|---|
| POST | /api/auth/login |
Log in, mint a session token | public |
| GET | /api/auth/me |
Return the authenticated user | session |
| POST | /api/auth/logout |
Invalidate the current session | session |
The body takes email and password. A successful login returns
{ user, token } - store token and send it on subsequent requests.
curl -X POST "https://thesidedoor.co/api/auth/login" \ -H "Content-Type: application/json" \ -d '{"email":"operator@example.com","password":"your-password"}'const res = await fetch("https://thesidedoor.co/api/auth/login", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ email: "operator@example.com", password: "your-password", }),});const { user, token } = await res.json();$ch = curl_init("https://thesidedoor.co/api/auth/login");curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ["Content-Type: application/json"], CURLOPT_POSTFIELDS => json_encode([ "email" => "operator@example.com", "password" => "your-password", ]),]);$data = json_decode(curl_exec($ch), true);$token = $data["token"];{ "user": { "id": "usr_123", "role": "client", "name": "Jo Operator" }, "token": "sdt_9f2a7c…redacted"}The token is an opaque credential - treat it as a secret string; its internal
structure is not part of the contract and may change.
Two-factor accounts
Section titled “Two-factor accounts”If the account has two-factor authentication enabled, the login is completed with
a second step: an authenticator code challenge. Once the challenge is satisfied
you receive the same { user, token } result. Handle it interactively (a person
enters their code); it isn’t suited to unattended server-to-server use - use an
API token there instead.
Token lifetime
Section titled “Token lifetime”An API token stays valid until it expires (if you set an expiry) or you revoke it - manage that from Access → API in your operator account.
A session token from interactive login is stored server-side and slides on use - each authenticated request extends the expiry. An operator/staff session lasts 3 days on a sliding window (any authenticated request extends it).
A missing, invalid, or expired token on any /api/* route returns 401:
{ "error": "Unauthorized" }Re-run the login flow, or issue a fresh API token, to obtain a new token.
Log out
Section titled “Log out”POST /api/auth/logout invalidates the current session server-side. Discard
the token afterwards. (To retire an API token, revoke or roll it from
Access → API rather than logging out.)
curl -X POST "https://thesidedoor.co/api/auth/logout" \ -H "Authorization: Bearer $SIDEDOOR_TOKEN"await fetch("https://thesidedoor.co/api/auth/logout", { method: "POST", headers: { Authorization: `Bearer ${process.env.SIDEDOOR_TOKEN}`, },});$ch = curl_init("https://thesidedoor.co/api/auth/logout");curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ "Authorization: Bearer {$token}", ],]);curl_exec($ch);{ "success": true }