Platform

API keys & JWT

The anon and service_role project keys, the project JWT secret, token lifetimes, key rotation, and how to keep credentials safe.

Every project has two API keys and a JWT secret. The anon key is public and goes in your app. The service_role key is a server-only master key. The JWT secret signs both keys and every token issued to your app's users.

The two keys

anonservice_role
Where to use itBrowsers, mobile apps, any clientYour servers, scripts, CI, edge functions you trust
Row Level SecurityAppliesBypassed (full database access)
Safe to publishYes, if your tables have RLS policiesNever

Both keys are JWTs. The SDKs read the project slug from the key's project_slug claim, so a key is all you need to create a client:

TypeScript
import { createClient } from '@orbitneststudio/js';

const orbit = createClient({ apiKey: import.meta.env.VITE_ORBITNEST_ANON_KEY });
server.ts
import { createClient } from '@orbitneststudio/node';

// Server only: this key bypasses RLS
const admin = createClient({ apiKey: process.env.ORBITNEST_SERVICE_KEY! });

What each key can do

On the client database endpoints (/api/project/<slug>/database/...):

Operationanon keyanon key + user access tokenservice_role key
Read rows (table data endpoint)YesYes, with the user's claimsYes, bypassing RLS
Insert a rowYesYes, with the user's claimsYes
Run SQL (POST .../sql)One SELECT or INSERT statement, as the anon roleSame, as authenticated with the user's claimsAny statements that pass the SQL checks, including multi-statement scripts
Update or delete rows (single or bulk)No (401)Yes, with the user's claimsYes
Bulk insertNoNoYes
Enable or disable RLS, create policiesNoNoYes
Read or write auth_* tablesNo (403)No (403)Through SQL and the single-row endpoints, but SQL can't drop, truncate, alter or DELETE FROM them

See Database for how RLS policies are evaluated on each path. Other services, such as storage, functions and the email API, have their own rules on their pages. Sending email through the Email API requires the service_role key.

Find your keys

Open your project in Studio and go to API Keys:

  • API Keys shows the anon and service_role keys (reveal and copy) and the Project URL, https://api.orbitnest.io/api/project/<slug>.
  • JWT Settings shows the JWT secret, the signing algorithm (HS256), and the token lifetimes for your app's users.

Every workspace member, admin or developer, can view and rotate keys. See Team & roles. The MCP server can also read them with orbitnest_get_api_keys.

Send a key with a request

Send the key as a bearer token:

Terminal
curl "https://api.orbitnest.io/api/project/my-app/database/tables/posts/data?limit=10" \
  -H "Authorization: Bearer $ORBITNEST_ANON_KEY"

To act as a signed-in user, put the anon key in the apikey header (x-api-key also works) and the user's access token in Authorization. The database endpoints then apply the user's claims, so RLS policies can use auth.uid(). Only access tokens count: refresh tokens and MFA challenge tokens are never accepted as access tokens. See Authentication.

Terminal
curl -X POST https://api.orbitnest.io/api/project/my-app/database/tables/notes/rows \
  -H "apikey: $ORBITNEST_ANON_KEY" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"title":"My note"}'

Key claims

A decoded key looks like this:

JSON
{
  "role": "anon",
  "project_slug": "my-app",
  "permissions": { "read": true, "insert": true, "update": false, "delete": false },
  "iat": 1790000000
}
  • role is anon or service_role. This, together with your RLS policies, decides what a request may do.
  • project_slug must match the project in the URL, or the request is rejected.
  • permissions is descriptive only. No endpoint enforces it.
  • Keys created at project setup and keys re-signed by a JWT secret rotation never expire. Keys made with Regenerate Keys carry an exp one year out.

JWT settings

JWT secret

The project's JWT secret (HS256) signs the anon and service_role keys and your users' access and refresh tokens. The secret shown under JWT Settings is the exact signing key, so it verifies those tokens. MFA challenge tokens are signed with a separate key derived from the secret and only work at verify-mfa. Anyone who has the secret can mint a service_role key, so treat it like one.

Access tokens carry type: "access" and refresh tokens type: "refresh". Every OrbitNest service that accepts a user token (database, storage, realtime, edge functions and analytics ingestion) refuses refresh tokens and MFA challenge tokens.

Token lifetimes

SettingDefaultApplies to
Access token expiry900 seconds (15 minutes)access_token issued to your app's users
Refresh token expiry604,800 seconds (7 days)refresh_token issued to your app's users

Change them under API Keys → JWT Settings → Token Expiry. Studio requires at least 60 seconds for each. New values apply to tokens issued after you save. Existing tokens keep their original expiry.

Verify user tokens on your server

Verify an access token locally with the JWT secret, checking the audience (the project slug) and issuer. Accept only access tokens: reject anything whose type is set to a value other than access.

verify.ts
import jwt from 'jsonwebtoken';

export function verifyUser(token: string) {
  const claims = jwt.verify(token, process.env.ORBITNEST_JWT_SECRET!, {
    algorithms: ['HS256'],
    audience: 'my-app',
    issuer: 'orbitnest:project:my-app',
  }) as jwt.JwtPayload;
  if (claims.type !== undefined && claims.type !== 'access') {
    throw new Error('Not an access token');
  }
  return claims; // sub, email, role, projectId, type, jti, iat, exp
}

Local verification checks only the signature and expiry. It doesn't know whether the user has since signed out; GET /api/projects/<slug>/auth/verify does.

If you don't want to hold the secret, call GET /api/projects/<slug>/auth/verify with the token instead. The full claim list is in Authentication.

Rotate credentials

Regenerate the keys

API Keys → Regenerate Keys asks for a name and replaces both keys with a new pair that expires in one year. Studio shows the new keys right away.

The previous anon and service_role keys stop working immediately on every endpoint that accepts project keys (database, storage, the Email API, realtime over WebSocket and REST, edge function invocation and analytics ingestion), which accept only the project's current keys. The ORBITNEST_ANON_KEY environment variable of your edge functions is updated to the new anon key automatically. Your users' sessions are not affected.

Rotate the JWT secret

API Keys → JWT Settings → Regenerate Secret generates a new random secret and re-signs both keys (without an expiry). The change takes effect immediately:

  • Every previously issued anon and service_role key stops working.
  • Every access and refresh token issued to your users stops working, so all users are signed out.
  • Anything that verifies tokens with the old secret must be updated.

After rotating or regenerating, copy the new keys into every app, server and CI secret that uses them. Edge functions receive the current anon key as ORBITNEST_ANON_KEY, which OrbitNest updates for you. If you added ORBITNEST_SERVICE_ROLE_KEY as a function environment variable, update it yourself.

Keep keys safe

  • Never ship the service_role key or the JWT secret in a browser bundle, mobile app or public repository. Anyone who has either one has full access to the project's data.
  • Treat the anon key as public. Protect data with RLS policies, not by hiding the key. See Database.
  • Keep server keys in environment variables or your platform's secret store, never in source code.
  • Give functions the least privilege. Edge functions get ORBITNEST_ANON_KEY automatically. The service key is only available to a function if you add ORBITNEST_SERVICE_ROLE_KEY as an environment variable yourself. See Edge functions.
  • If a key leaks, regenerate the keys. The old keys stop working everywhere immediately and your users stay signed in. Rotate the JWT secret instead only if the secret itself leaked; that signs out all of your users.

Other credentials

CredentialUsed forWhere to manage it
Analytics ingest tokenSending analytics events (x-analytics-token header)Analytics → Tokens. Shown once, can be deactivated or revoked. See Analytics.
Webhook signing secretVerifying webhook deliveriesShown once when you create a webhook. See Webhooks.
Studio sign-in (CLI, MCP)Managing projects from a terminal or AI agentorbitnest login or the MCP sign-in tools. They use your Studio account, not project keys, and store credentials in ~/.orbitnest/credentials.json. See CLI and MCP server.