Skip to content
On this page

Core concepts

API keys

Keys are the credential for inference. They are scoped, rate-limited, and shown only once.

Keys look like sk-or-v1-<64 hex>. Only the first 12 characters are stored for display, and the full secret is returned once on create or rotate.

List keys

GET/keysJWT or API key
[
{
"id": "ce8bdcf0-99ea-44c3-b61d-515a697ee462",
"name": "default",
"prefix": "sk-or-v1-d09",
"created_at": 1792276475,
"expires_at": null,
"rpm_limit": 0,
"conc_limit": 0,
"daily_cap_micro": 0,
"monthly_cap_micro": 0,
"allow_fallback": true,
"byok_provider": null,
"byok_base_url": null,
"key": null
}
]

Non-revoked keys only, newest first. key is always null here. last_used_at is stored but not returned yet.

Create a key

POST/keysJWT or API key
FieldTypeDescription
namerequiredstring1–100 characters.
ttl_hoursnumberRelative expiry. Wins over expires_at when both are set.
expires_atnumberAbsolute unix expiry. Must be in the future.
rpm_limitnumberRequests per minute. 0 = unlimited. Range 0–1,000,000.
conc_limitnumberConcurrent in-flight requests. 0 = unlimited.
daily_cap_micronumberDaily spend cap in micro-USD. 0 = unlimited.
monthly_cap_micronumberMonthly spend cap in micro-USD. 0 = unlimited.
allow_fallbackbooleanWhether this key may fail over to fallback models.
byok_providerstringProvider id for bring-your-own-key. Must exist in the provider registry and be supplied together with byok_key.
byok_keystringUpstream provider secret. Encrypted at rest.
byok_base_urlstringOptional https base URL. Credentials and loopback/private/internal hosts are rejected.
curl -X POST https://api.relayrouter.org/keys \
-H "Authorization: Bearer $JWT" \
-H 'content-type: application/json' \
-d '{
"name": "production",
"ttl_hours": 720,
"rpm_limit": 60,
"conc_limit": 8,
"daily_cap_micro": 5000000,
"monthly_cap_micro": 50000000,
"allow_fallback": true
}'

The response includes "key": "sk-or-v1-…". Show it once, offer a copy button, and never re-fetch it. Each account may have at most 20 active keys; the 21st returns 400.

Update a key

PATCH/keys/{id}JWT or API key
  • expires_at: null clears expiry; a timestamp sets an absolute expiry.
  • ttl_hours sets a relative expiry and wins over expires_at.
  • byok_key: "" clears BYOK; a non-empty value sets it.
  • byok_provider: "" clears the provider. A non-empty value must reference a configured provider.
  • byok_base_url: null or "" clears it; a non-empty value is SSRF-validated.

Returns the updated key object with key: null. Limit changes take effect immediately.

Rotate and revoke

Method & pathResult
POST /keys/{id}/rotateSame id, name, and limits with a brand-new secret returned once. The old secret stops working immediately.
DELETE /keys/{id}{ "revoked": true }. Idempotent, even if the id does not exist.

BYOK vs dedicated keys

CredentialWho sets itToken billingNetwork fee
BYOKuser$0 — the provider account pays for tokens.still charged
DedicatedoperatorNormal token billing.normal
PlatformoperatorNormal token billing.normal

Credential precedence is user BYOK → dedicated key → platform key. If a BYOK key is configured for provider X, requests to X use it or fail; they never silently fall back to the platform key.

Model allowlists

Two allowlists exist and the key-level list wins when it is non-empty:

LevelSet viaScope
AccountadminPATCH /admin/users/{id}/limits with { allowed_models: [...] }. Applies to every key on the account.
Keyops CLIapi_keys.allowed_models, set with ./relayctl or SQL. Applies to that one key only.

An empty list means “every public model”. The public POST /keys and PATCH /keys/{id} endpoints do not accept a per-key list yet.

Precedence

Key allowlist → account allowlist → every public model. A private model never appears in /v1/models.