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 building | Credential | Guide |
|---|---|---|
| A backend that calls Gate | API key (gate_sk_…) | API keys |
| A browser app that signs users in | Web app OAuth client, authorization code + PKCE | Web app |
| A CLI, desktop, or mobile app | Native app OAuth client (no secret) + PKCE | Native and CLI |
| A backend calling your own API as itself | Machine-to-machine OAuth client | Machine to machine |
| An API that receives Gate tokens | No credential — verify against JWKS | Verifying tokens |
| An MCP server for your product | Project MCP server URL — verify against JWKS | MCP servers |
Three things to know
Gate has three kinds of access, and each answers a different question.
API keys: your backend calls Gate. A project's API key goes in the X-API-Key header. It reads
config, records usage, checks entitlements, and runs checkout. Each key has a few
scopes and may be limited to one environment.
OAuth clients: people sign in to your product. Your app sends someone to Gate, they sign in, and
you receive tokens that represent them. The token's sub is a Gate user id, and your API decides
what that user may do. A machine-to-machine client gets short-lived tokens for your own API instead.
Gate's own API does not accept these tokens, apart from the linked-accounts endpoints.
Organization roles: people use the dashboard. Members of an organization hold one of five roles that decide what they may change.
Mixing up the first two is the most common integration mistake. An API key can never represent a user, and a user's token cannot administer Gate.
What API keys cannot do
These require a real user session, because they issue credentials or change settings. An API key
is refused with 403 even when it holds *:
- every organization route, including listing and creating projects
- updating, archiving, or restoring a project
- creating or revoking API keys, managing OAuth clients and social login
- connecting Stripe (
PUTandPATCH /environments/{environmentId}/stripe-connection) - revealing a secret as a person would; a key with
config:readreads values directly
Token shapes at a glance
| Authorization code | Client credentials | API key | |
|---|---|---|---|
| Access token lifetime | 600 s | 300 s | Until revoked or expired |
| Refresh token | With offline_access, 30 days | Never issued | Not applicable |
| Represents | A user | A client | Your backend |
| Verified by | Your API, against JWKS | Your API, against JWKS | Gate, on every request |
| Sent as | Authorization: Bearer | Authorization: Bearer | X-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://clippingkk.annatarhe.com,
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.