API Keys
Truss uses API keys to authenticate client API requests. Every /v1/* endpoint requires a key passed via the apikey header (or x-api-key).
Key types
Section titled “Key types”| Type | Prefix | RLS behavior | Use case |
|---|---|---|---|
anon | truss_pk_ | Respects RLS policies | Client-side apps, public-facing |
service_role | truss_sk_ | Bypasses RLS (runs as postgres role) | Server-side, admin scripts, CLI |
anon keys
Section titled “anon keys”Anon keys are safe to expose in client-side code. They respect PostgreSQL Row-Level Security (RLS) policies, so users can only access data their policies allow.
When a request uses an anon key with a JWT in the Authorization header, Truss:
- Decodes the JWT payload
- Sets
request.jwt.claimsandrequest.jwt.subas Postgres session config - Switches to the
authenticatedrole
service_role keys
Section titled “service_role keys”Service role keys bypass RLS entirely and run queries as the postgres role. Never expose these in client-side code. Use them for:
- Server-side API calls
- Admin scripts and migrations
- Management API endpoints (which require service_role)
- CI/CD pipelines
Creating keys
Section titled “Creating keys”Via dashboard
Section titled “Via dashboard”Navigate to Settings > API Keys and click “Create Key”. Choose the type and give it a label.
Via API
Section titled “Via API”curl -X POST http://localhost:8787/api/keys \ -H "Content-Type: application/json" \ -d '{"keyType": "service_role", "label": "backend-server"}'Response:
{ "key": { "id": 1, "key_type": "service_role", "key_prefix": "truss_sk_abc", "label": "backend-server", "created_at": "2025-01-15T10:00:00Z" }, "secret": "truss_sk_abcdefghij..."}The secret is only returned once at creation time. Store it securely.
Using keys
Section titled “Using keys”Pass the key via the apikey header:
curl http://localhost:8787/v1/db/users \ -H "apikey: truss_pk_your_anon_key"const res = await fetch('http://localhost:8787/v1/db/users', { headers: { apikey: 'truss_pk_your_anon_key' }});The x-api-key header also works as an alias.
Revoking keys
Section titled “Revoking keys”curl -X DELETE http://localhost:8787/api/keys/{id}Revoked keys immediately stop working. The key row is kept for audit purposes but marked as revoked.
Rate limiting
Section titled “Rate limiting”Rate limiting is opt-in per API key. In the open-source core there is no default limit — keys are unlimited unless you set a per-key rate_limit (requests/minute) on the key. The limit headers are returned on every response; when a key is unlimited they report -1:
X-RateLimit-Limit: -1X-RateLimit-Remaining: -1When a per-key limit is configured, the headers report that limit and the remaining count in the current 60-second window, and the API returns 429 Too Many Requests once it is exceeded.
Security
Section titled “Security”- Keys are stored as SHA-256 hashes — the raw key is never stored
- Each key tracks
last_used_atfor auditing - Revoked keys are rejected immediately
- Rate limiting is per-key, in-memory, with a 60-second sliding window
Key Rotation
Section titled “Key Rotation”To rotate a key without downtime:
- Create a new key with the same type (anon or service_role)
- Update your application to use the new key
- Verify the new key works in production
- Revoke the old key once all clients have migrated
# 1. Create new keycurl -X POST http://localhost:8787/api/keys \ -H "Content-Type: application/json" \ -d '{"keyType": "service_role", "label": "backend-v2"}'
# 2. Update your app's environment variable with the new key
# 3. Revoke old keycurl -X DELETE http://localhost:8787/api/keys/{old_key_id}There is no automatic rotation schedule — rotation is a manual operation. The last_used_at timestamp on each key helps you verify whether the old key is still in use before revoking it.
Using Keys with the Client API
Section titled “Using Keys with the Client API”All /v1/* endpoints require an API key. Pass it via the apikey header (or x-api-key).
REST CRUD
Section titled “REST CRUD”# Read (anon key is fine if RLS allows it)curl http://localhost:8787/v1/db/posts \ -H "apikey: truss_pk_your_anon_key"
# Write (service_role key bypasses RLS)curl -X POST http://localhost:8787/v1/db/posts \ -H "apikey: truss_sk_your_key" \ -H "Content-Type: application/json" \ -d '{"title": "Hello", "body": "World"}'SQL-over-HTTP
Section titled “SQL-over-HTTP”curl -X POST http://localhost:8787/v1/sql \ -H "apikey: truss_sk_your_key" \ -H "Content-Type: application/json" \ -d '{"sql": "SELECT count(*) FROM users"}'With JWT (anon key + authenticated user)
Section titled “With JWT (anon key + authenticated user)”When using an anon key with a JWT, include the token in the Authorization header. Truss extracts the claims and passes them to Postgres for RLS evaluation.
const res = await fetch('http://localhost:8787/v1/db/messages', { headers: { 'apikey': 'truss_pk_your_anon_key', 'Authorization': `Bearer ${userJwt}` }});Management endpoints
Section titled “Management endpoints”Management endpoints (/v1/status, /v1/database/schema, /v1/webhooks, etc.) require a service_role key:
curl http://localhost:8787/v1/status \ -H "apikey: truss_sk_your_key"
curl http://localhost:8787/v1/database/schema \ -H "apikey: truss_sk_your_key"Management API keys
Section titled “Management API keys”The management API endpoints (/v1/status, /v1/keys, /v1/database/schema, etc.) require a service_role key. Attempting to access them with an anon key returns 403 Forbidden.