- OAuth 2.1 short-lived access tokens (
kc_live_…,kc_test_…) — minted viaPOST /oauth/token, expire in 1 hour, optionally sender-constrained with DPoP (RFC 9449). This is what the SDKs use under the hood, and the recommended path for new integrations. - Long-lived API keys (
tk_live_…,tk_test_…,AKE…) — the original path, still works everywhere; treat it as the legacy alternative.
Quick start — OAuth 2.1
tk_… API key becomes the OAuth client_id + client_secret:
client_id=tk_abcd1234(everything before the second underscore)client_secret= the remainder
- 1-hour TTL instead of long-lived
- Scope narrowing — request a subset of your key’s permissions per call
- DPoP sender-constraint (optional) — proof-of-possession of a keypair required per request
OAuth clients that are not API keys
A client created on the OAuth Clients page (or viaPOST /v1/oauth-clients) has no API key
behind it. Its client_credentials tokens call /v1 with whatever roles the client holds —
assign them when you create the client, or later on its Permissions tab. A client with no
role is denied on every /v1 endpoint, and its allowed_scopes narrow each token further;
a token can never do more than both its scope and the client’s roles allow.
A token issued to a user (authorization-code and device flows, including knoxcall login) calls
/v1 as that user: their own roles in the workspace, narrowed by the token’s scope. Whatever
roles or custom policies the user holds, a user-bound token:
- needs an enrolled second factor to act on the Live host;
- never performs a human-plane action — revealing a secret’s plaintext, managing permissions, changing custody. Those need a signed-in session with MFA and step-up, not a bearer token;
- never uses a reveal-class permission — decrypting (
transit:decrypt), detokenizing a vault value, minting a database credential, issuing a certificate that comes back with its private key (from the PKI engine, or a client’s mTLS certificate withmode: issue— registering a certificate you generated yourself by itssha256_thumbprintstill works), or fetching a live upstream OAuth2 token — and never gives one to a machine credential: it cannot attach a role carrying one to a new API key, or rotate the secret of an OAuth client that holds one. Automation that needs these uses an API key or OAuth client that an owner or admin grants the permission in the dashboard; - uses owner and admin capabilities only for an owner or admin of the workspace, as in the
dashboard: listing, minting and revoking API keys; creating, changing and deleting OAuth apps;
registering agents; changing AI gateways and minting their tokens; managing transit keys and
rewrapping or signing with them (on
/v1, managing includes listing keys and reading a key’s metadata, which the dashboard shows to any member); managing PKI and database credentials; vaults; webhook test sends; and the ephemeral proxy; - needs an edit permission, not only a read one, to mint or revoke a wrap token or to report
egress observations —
secret:updateforPOST/DELETE /v1/wrap/tokens,route:createforPOST /v1/wrap/egress-observations. An API key keeps doing these on read alone.
deny rule (or any
rule that is not a well-formed allow). One made only of allow rules is no longer held by any
person (see the 2026-10-08 entry in the changelog), and a custom role
can no longer be assigned to a person. One that contains a deny rule stays on the person who
holds it, and on /v1 both its denies and its allows still apply. API keys and OAuth clients are
not affected.
OAuth 2.1 endpoints
Supported grant types:
client_credentials— server-to-server, the simplest pathauthorization_code+ PKCE — human consent flow (with refresh tokens). Public clients may register loopback redirect URIs per RFC 8252 —http://127.0.0.1,http://[::1], andhttp://localhostmatch on any port at authorization time, so native and CLI apps can receive the callback locally.urn:ietf:params:oauth:grant-type:device_code— RFC 8628 device-code flow for headless/SSH machines:POST /oauth/device_authorizationreturns a user code, the user approves it at/oauth/activateon any browser, and the device polls the token endpoint. This is whatknoxcall login --deviceuses.refresh_token— single-use rotation with family-detection theft protectionurn:ietf:params:oauth:grant-type:token-exchange— workload identity federation. Any OIDC issuer works; the SDKs additionally auto-detect several platforms for you (see below)
DPoP — sender-constrained tokens (RFC 9449)
For tenants requiring the highest security tier, request DPoP-bound tokens. Send a DPoP proof JWT on the token request; the issued token is bound to your keypair via thecnf.jkt claim. Every subsequent API call must carry a fresh DPoP proof signed by the same key.
Workload identity federation (RFC 8693)
Running on a platform that issues its workloads an OIDC token? You don’t need an API key at all. Register a binding, and the exchange trades that token for a short-lived KnoxCall access token. Any OIDC issuer is accepted — if it publishes/.well-known/openid-configuration over https and signs with any asymmetric algorithm (the full RS/PS/ES family plus EdDSA — see What your issuer’s token must carry below), you can federate from it by registering the issuer and the claims that identify your workload.
Auto-detection is a convenience on top of that. Where the SDK recognises the platform it fetches the token for you with no configuration: GitHub Actions, Google Cloud, AWS (IRSA), Azure managed identity, Vercel, CircleCI and Buildkite. Coverage differs by language — the Node SDK covers all seven, the Python SDK covers five (no Azure, no Buildkite). On any other platform, or from an SDK that does not detect yours, fetch the token yourself and call the exchange directly; HCP Terraform works this way today.
Which claims identify your workload depends on the issuer. KnoxCall carries a claim
table for three issuers — GitHub Actions (
repository, job_workflow_ref or sub),
HCP Terraform (sub) and Google (email or sub) — and requires your binding to pin at
least one of them. Every other issuer falls back to sub alone, which works but is a
blunter instrument: sub is the only claim OIDC guarantees identifies a subject. This
matters because a binding that pins no identifying claim would describe a population
rather than a workload — on a shared issuer like GitHub’s, {"ref": "refs/heads/main"} is
true for every repository on GitHub — so the API refuses one with 400.What your issuer’s token must carry
These are checked on every exchange, on bothPOST /oauth/token and
POST /v1/oauth/token. Every mainstream IdP satisfies them by default; they are listed
because a refusal that names a missing claim is easier to fix than one that does not.
Assertions are single use. Each one is spent by the exchange that accepts it, so a
long-running job must mint a fresh token from its platform before each exchange rather
than reusing the first — the SDKs’
WorkloadCredentialProvider does this for you.
Create an OAuth client
An OAuth client is how an SDK or a script authenticates in CI and on servers. (On a developer machine you can log in once with the CLI instead.) You create it once in the dashboard:- Switch the dashboard to the mode you want to call, Live or Test. A client belongs to the mode it was created in.
- Open Settings → API → OAuth Clients and click Create OAuth Client.
- Enter a Name (for example
my-laptop) and keep the type Server-to-server. - Under Allowed scopes, list the scopes the client needs, separated by spaces. For example,
routes:read routes:invokelets it list your routes and call them through the proxy; addroutes:writeorsecrets:writeif it will create them. An empty list is refused. - Under Permissions, keep Key — Read-only (selected by default), or choose
Key — Editor if the client will create or change routes, secrets or clients. A client with
no role is refused on every
/v1endpoint. - Click Create Client, then copy the Client ID and Client secret. The secret is shown once.
sandbox
option (sandbox: true in Node and Ruby, sandbox=True in Python, Sandbox: true in Go,
'sandbox' => true in PHP).
Using the SDK (recommended)
KnoxCall ships first-party SDKs for Node.js/TypeScript, Python, Go, PHP, and Ruby (plus a Terraform provider and browser/React Elements) — see the SDKs overview for the full family. Every SDK handles token minting, caching, retries, idempotency keys, and DPoP for you.Authorization: Bearer <token> (a legacy tk_… key in the same header also works) and parse the JSON response.
Log in once with the CLI
On a developer machine, skip keys and env vars entirely. Every KnoxCall SDK package ships theknoxcall command (pip, npm, gem, Composer, go install — same surface, same credentials file); it runs the authorization-code + PKCE flow in your browser (or the RFC 8628 device-code flow with --device) and stores the credential in ~/.knoxcall/credentials.json:
--device, open the link the terminal prints on any device, confirm the code it shows, and approve there. Approving needs an account with a passkey, security key or authenticator app set up.
knoxcall login signs you in to Live. To call the Test environment, use an OAuth client created in Test mode and the SDK’s sandbox option.
Every KnoxCall SDK — Node.js, Python, Go, PHP, and Ruby — picks the file up automatically, so a zero-argument constructor just works:
knoxcall login uses the reserved knoxcall-cli client alias, which provisions a per-tenant system OAuth client on first login — you’ll see it on the OAuth Clients page, where it is visible but protected. See the SDKs overview for profiles (KNOXCALL_PROFILE) and the full credential-resolution order.
Postman / Insomnia / Bruno
KnoxCall Postman collection
Download the official collection and import it into Postman. It uses Postman’s native OAuth 2.0 support — sign in once via the Authorization tab, and every saved request inherits the token.
https://api.knoxcall.com/.well-known/oauth-authorization-server.
API Key Types
KnoxCall uses API keys to authenticate requests to the Management API. There are three types of keys, each with a distinct prefix:Key prefixes make it easy to identify which environment a key belongs to at a glance. This follows the same live/test paradigm used by Stripe and other modern APIs.
Creating API Keys
Via the Dashboard
- Log in to the KnoxCall Dashboard.
- Navigate to Settings in the sidebar.
- Scroll to the API Keys section.
- Click Create API Key.
- Give your key a descriptive name (e.g., “CI/CD Pipeline”, “Backend Server”).
- Copy the key immediately — it will only be shown once.
Via the API
You can also create API keys programmatically:The
api_key field in the response is the only time the full API key is returned. Store it securely.Production vs. Sandbox
KnoxCall provides completely separate production and sandbox environments, following the same live/test key paradigm popularized by Stripe. Routes, secrets, environments, webhooks, vaults, crypto keys, PKI authorities, OAuth clients, and workflows are all isolated between the two modes — nothing leaks across.Production (Live)
Management API:
https://api.knoxcall.com/v1Proxy traffic: https://{slug}.knoxcall.com/{route}- Uses
tk_live_orAKEprefixed keys - Routes proxy real traffic to live upstream APIs
- Secrets contain real credentials
- Usage counts against your plan limits
- Changes affect your live integrations immediately
Sandbox (Test)
Management API:
https://sandbox.knoxcall.com/v1Proxy traffic: https://sandbox-{slug}.knoxcall.com/{route}- Uses
tk_test_prefixed keys - Routes, secrets, and clients are isolated from production
- Safe for development, testing, and CI/CD
- No impact on production traffic or billing
- Mirrors production API behavior exactly
Dashboard toggle
The admin dashboard has a Live / Test toggle in the bottom-left of the sidebar (look for the flask icon). Click it to switch the entire dashboard between modes — every page (Routes, Secrets, Vaults, API Keys, Webhooks, etc.) refetches and shows only the data for the active mode. An amber banner across the top of the screen confirms when you are in Test mode. While in Test mode:- Creating an API key automatically produces a
tk_test_test key (the standard / Enterprise Access Key options are hidden). - Creating any other resource (route, secret, vault, etc.) stamps it as sandbox — it is invisible from Live mode.
- Existing live resources are not visible; you start with an empty sandbox until you populate it.
Using the SDK in sandbox mode
Both the Node and Go SDKs accept asandbox: true option which auto-resolves the management and proxy base URLs to the sandbox hostnames:
KNOXCALL_BASE_URL=https://sandbox.knoxcall.com and KNOXCALL_PROXY_BASE_URL=https://sandbox-{slug}.knoxcall.com.
Audit logs
Every audit log entry is stamped withdetails.sandbox: true|false so you can filter live vs test activity in the Audit Logs page or via the /v1/audit-logs endpoint. Background jobs (cron, queue workers) always emit sandbox: false.
Making Authenticated REST Requests
The SDK is the recommended path — it mints, caches, and refreshes tokens for you. Calling the REST API directly, mint a short-lived OAuth token (see Quick start) and pass it in theAuthorization header as a Bearer token:
Examples
Legacy: Long-Lived API Keys
Long-lived keys (tk_live_…, tk_test_…, AKE…) remain fully supported on the Management API. Pass the key exactly where the token goes:
Legacy: Using the x-api-key Header
If your HTTP client or framework makes it difficult to set theAuthorization header, you can pass a long-lived API key in the x-api-key header instead:
Authentication Errors
When authentication fails, the API returns one of these error types:
Example error response:
Security Best Practices
Never expose keys in client-side code
An API key can call your routes with your credentials injected, and do whatever its roles allow on the management API. Never embed one in frontend JavaScript, mobile apps, or any code that runs in the browser.
Use environment variables
Store API keys in environment variables or a secrets manager. Never hard-code them in source files or commit them to version control.
Rotate keys regularly
Create new keys and revoke old ones on a regular cadence. If a key is compromised, revoke it immediately from the dashboard.
Use the principle of least privilege
Attach the narrowest role each key needs — roles limit what it can do on the management API — and create a separate key per service so you can revoke one without the others. Roles do not limit which routes a key can call: require authorized clients or an IP allowlist on each route.
Environment Variable Setup
On a developer machine, preferknoxcall login — no environment variables at all. For CI and servers, create an OAuth client (dashboard → Settings → API → OAuth Clients) and set KNOXCALL_CLIENT_ID + KNOXCALL_CLIENT_SECRET — every SDK picks them up automatically. KNOXCALL_API_KEY remains supported as the legacy spelling for a long-lived key.
Key Rotation Procedure
- Create a new API key in the dashboard or via the API.
- Update your application’s environment variables with the new key.
- Deploy the updated configuration.
- Verify that requests succeed with the new key.
- Revoke the old key in the dashboard.
Both the old and new keys will work simultaneously until you revoke the old one. This allows zero-downtime rotation.