Authentication
Every API request (except the public /health probe and the signature-verified webhooks) must carry a credential. PandaStack authenticates with a standard bearer token in the Authorization header:
Authorization: Bearer <token>There are two kinds of token the API accepts, and the difference matters because one of them carries its organization context and the other does not.
Logging in
Section titled “Logging in”Most integrations use an API token, which you generate in the dashboard and never expire until you revoke them. The API also exposes the login routes the dashboard itself uses to mint a session JWT.
Social login / signup
Section titled “Social login / signup”POST /v1/auth/social
Native login and signup: verifies a Firebase ID token, finds-or-creates the user, and mints a platform JWT. This route is the login, so it needs no prior credential.
Auth: none. Body: { auth_secret, terms_accepted? }. Response: { access_token, user: { approval_status, profile_completed, mfaEnabled } }.
Use the returned access_token as your Authorization: Bearer JWT (remember to supply an org via x-organization-id for org-scoped routes — see JWTs).
Demo login (development only)
Section titled “Demo login (development only)”POST /v1/auth/demo
Mints a session JWT for the seeded demo user. Available in development only — it returns 404 in production.
Auth: none. Response: { token, email }.
Token types
Section titled “Token types”psk_ API tokens (recommended for automation)
Section titled “psk_ API tokens (recommended for automation)”An API token is a self-contained secret prefixed with psk_. It encodes the user, the organization, and the role it was created under, so you do not need to send an organization header alongside it — the org is baked in. This makes psk_ tokens the right choice for CI pipelines, scripts, and the CLI.
curl https://api.pandastack.io/v1/projects \ -H "Authorization: Bearer psk_live_xxxxxxxxxxxxxxxxxxxx"JWTs (session tokens)
Section titled “JWTs (session tokens)”A JWT is an HS256 session token minted at login. It proves identity only — it does not name an active organization. When you authenticate with a JWT against an org-scoped route, you must supply the active organization separately, using either a header or a query parameter:
x-organization-id: 1907or
?organization_id=1907Your membership in that organization is verified against the database on every request. Naming an organization you do not belong to returns 404 not_found (this deliberately closes the cross-org IDOR — you cannot probe for orgs you can’t see).
A few routes are identity-only and never require an org context, because they are used before an org is selected (on boot) or by an invitee who is not yet a member:
GET /v1/meGET /v1/orgsandPOST /v1/orgsGET /v1/invitations/:idandPOST /v1/invitations/:id/respond
Resolving your context — whoami
Section titled “Resolving your context — whoami”If you have a credential and want to know which org and role it resolves to (useful for CLI login discovery — it works for both psk_ tokens and JWTs), call:
GET /v1/whoami
curl https://api.pandastack.io/v1/whoami \ -H "Authorization: Bearer $PANDASTACK_TOKEN"{ "success": true, "data": { "userId": 42, "orgId": "1907", "orgName": "Acme", "role": "owner" }}Access to write endpoints is gated by a role floor. Roles are ordered, and a route that requires a given role accepts that role or any higher one (roleAtLeast):
viewer < member < admin < owner| Role | Can do |
|---|---|
viewer | Read org resources (any member can read). |
member | Create and update resources (projects, databases, functions, cronjobs, monitors). |
admin | Manage teams, members, API tokens, org settings, delete projects, change DB tiers. |
owner | Everything, including deleting the org and managing the subscription. |
The required role is documented on each endpoint. When your role is too low, the API returns 403 forbidden.
Creating and using an API token
Section titled “Creating and using an API token”API tokens are managed under the Organizations & teams endpoints. In short:
POST /v1/api-tokens (admin)
curl -X POST https://api.pandastack.io/v1/api-tokens \ -H "Authorization: Bearer $PANDASTACK_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "ci-deploy", "expiresInDays": 90 }'The raw token is returned exactly once, in the token field of the response. Store it securely; you cannot retrieve it again. Every subsequent read of the token list shows only a masked preview (psk_...a1b2). To stop a token working, revoke it with DELETE /v1/api-tokens/:id.
Full request/response contracts for token management are on the Organizations & teams — API tokens page.
Server-Sent Events (SSE)
Section titled “Server-Sent Events (SSE)”A few endpoints stream events using SSE via EventSource, which cannot set request headers. For those routes only, pass the token as a query parameter, and (for JWTs) the org too:
?access_token=<token>&organization_id=<orgId>The SSE endpoints are:
GET /v1/deployments/:uuid/logs/stream— live build logsGET /v1/projects/:name/metrics/stream— live metric tiles
The card hard-wall
Section titled “The card hard-wall”An organization that has not verified a payment method is placed behind a hard wall: every org-scoped route returns 402 quota_exceeded until a card is added. This prevents unpaid orgs from consuming resources.
A small allowlist of route prefixes stays reachable so the org can read its state and add a card:
| Allowlisted prefix | Purpose |
|---|---|
/health | Liveness probe. |
/v1/me | Current user + orgs. |
/v1/orgs | List/create orgs. |
/v1/account | Org detail. |
/v1/billing | Add/verify a card, view plans and invoices. |
/v1/limits | Plan allowances. |
/v1/usage | Metered usage. |
/v1/notifications | In-app notifications. |
To clear the wall, add and verify a card via the Billing endpoints. See also Plans & limits.
Ban recheck
Section titled “Ban recheck”On every authenticated request the API rechecks the caller’s ban status. A user who is suspended receives 403 forbidden immediately, even if their token is otherwise still valid — a still-live token does not bypass a suspension.
Rate limits
Section titled “Rate limits”All authenticated traffic shares a global limit of 300 requests per minute, keyed by the last 16 characters of the bearer token (or by client IP if no token is present). /health is exempt. Over the limit → 429 rate_limited. See the API overview.
Webhooks are not token-authenticated
Section titled “Webhooks are not token-authenticated”The Stripe and GitHub webhook receivers verify a cryptographic signature instead of a bearer token, so never send them an Authorization header:
- Stripe verifies the
stripe-signatureheader —POST /webhook. - GitHub verifies an HMAC-SHA256
x-hub-signature-256over the raw body —POST /api/v1/webhook/github-events.