Choose an integration mode

Almost every integration question resolves once you know which credential your code carries. Pick from this table, then read the matching guide.

You are buildingCredentialGuide
A browser app that signs users inweb OIDC client, authorization code + PKCEWeb app
A CLI, desktop, or mobile appnative OIDC client (public, no secret) + PKCENative and CLI
A backend calling another service as itselfservice OIDC client, client credentialsMachine to machine
An API that receives Gate tokensNo credential — verify against JWKSVerifying tokens
A backend that administers GateService API key (gate_sk_…)Service API keys

The distinction that matters

There are two fundamentally different things you might mean by "integrate with Gate."

Acting on behalf of a user. Your app sends someone to Gate, they sign in, and you receive tokens that represent them. Use an OIDC flow. The token's sub is a Gate user id, and your API decides what that user may do.

Acting as yourself. Your backend needs to read configuration, mint credentials, record usage, or manage billing. No user is involved. Use a service API key, or client credentials if you need a short-lived bearer token that another service can verify.

Mixing these up is the most common integration mistake. A service API key can never represent a user; requireUserPrincipal rejects it with 403 A user session is required on every endpoint that mints credentials.

Which endpoints refuse service accounts

These require a real user session, because they issue credentials or long-lived secrets:

POST/projects/{projectId}/service-accountscredential:manageuser only
POST/projects/{projectId}/service-accounts/{accountId}/disablecredential:manageuser only
GET/projects/{projectId}/oidc-clientscredential:readuser only
POST/projects/{projectId}/oidc-clientscredential:manageuser only
POST/projects/{projectId}/oidc-clients/{clientId}/disablecredential:manageuser only
PUT/environments/{environmentId}/stripe-connectionbilling:manageuser only

Every organization endpoint also requires a user session.

Token shapes at a glance

Authorization codeClient credentialsService API key
Access token lifetime600 s300 sUntil revoked or expired
Refresh tokenWith offline_access, 30 daysNever issuedNot applicable
RepresentsA userA clientA service account
Verified byYour API, against JWKSYour API, against JWKSGate, on every request
Sent asAuthorization: BearerAuthorization: BearerX-API-Key

Cross-origin constraints

Browser callers must originate from an allow-listed origin. Gate hard-codes the list in src/server/origins.ts: its own GATE_BASE_URL, plus https://shelltime.xyz, https://rawback.app, https://athena.annatarhe.com, and https://athena.annatarhe.cn. This applies to both CORS on /api/v1 and better-auth's trusted origins.