These docs are public. Live client_id / client_secret credentials are issued when your realm is provisioned — during closed beta, on approval. Request access →
KeyTrace API
KeyTrace is a token and identity service for distributed systems. It issues short-lived OIDC/OAuth 2.1 access tokens, validates them at the edge against published JWKS, and revokes them globally in under a second. This reference covers the token endpoint, validation, the error envelope, and revocation.
{realm_id}. Examples below use the realm k9x1p3.Concepts
| Concept | Description |
|---|---|
Realm | An isolated tenant with its own signing keys, policies, clients, and audit stream. Compromise stops at the realm boundary. |
Client | A service or application that authenticates with a client_id / client_secret (confidential) or PKCE (public). |
Access token | A short-lived (default 300s) RS256 JWT scoped to a realm. Verified locally against JWKS. |
Refresh token | A longer-lived credential that rotates on every use to mint fresh access tokens. |
JWKS | The realm's public signing keys, published at /.well-known/jwks.json and rotated on schedule via kid. |
Authentication
KeyTrace supports two grant types. Confidential clients (backend services) use client_credentials with a client_id and client_secret. Public clients (SPAs, mobile, CLIs) use the authorization-code grant with PKCE, which is enforced — a request without a valid code_verifier is rejected.
Credentials are scoped to a single realm and are issued when your realm is provisioned. During the closed beta, provisioning happens after your batch is approved.
# Recommended: read credentials from the environment export KEYTRACE_CLIENT_ID="cid_k9x1p3_…" export KEYTRACE_CLIENT_SECRET="csk_live_…"
The token endpoint
/v2/realm/{realm_id}/token/
Exchanges client credentials (or an authorization code + PKCE verifier) for a short-lived access token.
Request body
| Field | Type | Description |
|---|---|---|
grant_type | string | One of client_credentials, authorization_code, refresh_token. |
client_id | string | The client identifier for your realm. |
client_secret | string | Required for confidential clients. Never ship this to a browser. |
scope | string | Space-delimited scopes, e.g. token:issue. |
code_verifier | string | Required for public clients using PKCE. |
Example
$ curl -X POST https://auth.keytrace.net/v2/realm/k9x1p3/token/ \ -H "Content-Type: application/json" \ -d '{ "grant_type": "client_credentials", "client_id": "$KEYTRACE_CLIENT_ID", "client_secret": "$KEYTRACE_CLIENT_SECRET", "scope": "token:issue" }'
{ "access_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6Ims5eDFwMy0yMDI2LTAzIn0…", "token_type": "Bearer", "expires_in": 300, "realm": "k9x1p3" }
expires_in defaults to 300 seconds (5 minutes). Configure per-realm TTL in the dashboard, or per-client with a rotation policy. token_type is always Bearer.
Validating tokens
Access tokens are RS256 JWTs. Verify them locally — no network round-trip on the happy path:
- Fetch and cache the realm's public keys from
GET /v2/realm/{realm_id}/.well-known/jwks.json. - Match the token header's
kidto a key and verify the RS256 signature. - Check
iss=https://auth.keytrace.net,realm,exp, and requiredscope. - Optionally consult the revocation signal (see below) to reject tokens revoked before
exp.
{ "keys": [{ "kty": "RSA", "use": "sig", "alg": "RS256", "kid": "k9x1p3-2026-03", "n": "0vx7agoebGcQSuu…", "e": "AQAB" }]}
Keys rotate on schedule. Old kids stay published until every token they signed has expired, so rotation is invisible to validators that cache JWKS with a short TTL.
Error envelope
Every error is a JSON envelope with a stable shape — never an HTML page. An unauthenticated request to any route returns:
{ "error": "UNAUTHENTICATED", "code": 401, "realm": null, "hint": "A valid client_id and client_secret are required. See https://www.keytrace.net/docs", "request_id": "req_0000000000000", "ts": "2026-03-26T19:35:51.004Z" }
Envelope fields
| Field | Description |
|---|---|
error | Machine-readable code in UPPER_SNAKE_CASE. Switch on this, not on the message. |
code | The HTTP status, mirrored in the body for clients that only read JSON. |
realm | The realm the request resolved to, or null if none could be determined. |
hint | A human-readable next step. For display and logs — do not parse. |
request_id | An opaque correlation ID. Quote it verbatim when contacting support. |
ts | RFC 3339 timestamp (UTC) the error was generated. |
Error codes
| error | HTTP | Meaning |
|---|---|---|
UNAUTHENTICATED | 401 | No valid credentials on the request. |
INVALID_CLIENT | 401 | The client_id / client_secret pair is wrong or disabled. |
INSUFFICIENT_SCOPE | 403 | Authenticated, but the token lacks a required scope. |
REALM_NOT_FOUND | 404 | No realm matches {realm_id}. |
TOKEN_REVOKED | 401 | The token was revoked before its exp. |
RATE_LIMITED | 429 | Too many requests; retry after the Retry-After header. |
Revocation
/v2/realm/{realm_id}/revoke
Revoke a single token, everything issued to a subject, or an entire realm. Compatible with RFC 7009. Propagation to every edge validator completes in under 800 ms p99.
$ curl -X POST https://auth.keytrace.net/v2/realm/k9x1p3/revoke \ -H "Authorization: Bearer $ADMIN_TOKEN" \ -d '{ "sub": "svc_ledger" }'
{ "revoked": "svc_ledger", "scope": "subject", "receipt": "rev_8f2c…", "propagated_in_ms": 742, "ts": "2026-03-26T19:41:02.318Z" }
The signed receipt is written to the realm's append-only audit stream, so you can prove a revocation happened and when.
SDKs
Official SDKs read KEYTRACE_CLIENT_ID and KEYTRACE_CLIENT_SECRET from the environment and handle issuance, JWKS caching, and validation for you.
npm i @keytrace/nodego get keytrace.dev/gopip install keytraceimport { KeyTrace } from "@keytrace/node"; const kt = new KeyTrace({ realm: "k9x1p3" }); // issue a 5-minute access token const { access_token } = await kt.token({ scope: "token:issue" }); // verify locally against JWKS (no round-trip) const claims = await kt.verify(access_token);
Can't authenticate yet?
KeyTrace is in closed beta. Credentials are issued when your batch is approved.
Request access