New Sign up free, 10 calls on us. Up to $1, no card needed.

Provisioning Keys

A provisioning key lets your backend create and manage inference API keys programmatically - without anyone logging into the Console. Ideal for SaaS products that issue one key per end-customer, per device, or per CI job.

⚠

A provisioning key can create, modify, and delete any inference key in its workspace. Treat it like an admin credential: store it server-side only and never ship it to browsers or mobile clients.

ℹ

These endpoints take an admin key (sk-syn-admin-): the keys.write scope for every endpoint, or keys.read for the two GET endpoints alone. Without the scope they answer 403 insufficient_scope. Provisioning keys issued before admin keys (sk-syn-prov-) keep working, and new ones are no longer issued.

ℹ

The same keys are also at /api/v1/keys, the management API, which is where new work should go: one answer shape ({"data", "meta"}) and one error shape, cursor paging, limit_usd, limit_reset and lists instead of comma-separated text, the key’s own per-minute ceilings (rate_limit), POST /api/v1/keys/{id}/rotate, an Idempotency-Key header on the two POSTs, rate-limit headers on every answer, and an audit row for every change. Its full description is the OpenAPI document. The endpoints on this page keep working as they are.

How it works

  1. An owner or admin creates a provisioning key in Console → API keys → Admin keys. It carries the prefix sk-syn-admin-.
  2. Your backend calls the /api/provisioning/* endpoints with that key to create inference keys (prefix sk-syn-).
  3. Each inference key is scoped to the same workspace and can carry a USD spending cap and opaque metadata tags.
  4. Hand the inference keys to your customers. Revoke any of them individually at any time - deleting a provisioning key does not affect the inference keys it already created.
ℹ

Provisioning keys themselves are created in the Console only - there is no API to mint a provisioning key. Open Console → API keys → Admin keys (owner / admin only).

Authentication

Pass the provisioning key as a Bearer token. It works only on /api/provisioning/* - a provisioning key cannot make inference calls, and a regular inference key cannot call these management endpoints (both return 401 wrong_key_kind).

Authorization: Bearer sk-syn-admin-...

Create an inference key

POST /api/provisioning/keys

ParameterTypeDescription
namestringHuman-readable label for the key (e.g. "Device abc-123").
quotanumberSpending cap in USD. Omit or set 0 for unlimited.
allowed_modelsstringComma-separated model allowlist (e.g. "claude-haiku-4-5"). Empty = all models the workspace can access.
ip_whiteliststringComma-separated IP / CIDR allowlist for requests made with this key. Empty = no restriction.
expires_atstringOptional RFC3339 expiry timestamp. Omit for no expiry.
include_byok_in_limitbooleanIf true, BYOK calls count toward this key’s quota at upstream list price. Default false.
metadataobjectOpaque JSON tags you define (e.g. {"tenant_id":"acme"}). Stored verbatim, max 8KB. Used for listing / filtering only - never affects auth.
curl https://synthorai.io/api/provisioning/keys \
  -H "Authorization: Bearer sk-syn-admin-..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Device abc-123",
    "quota": 5.0,
    "allowed_models": "claude-haiku-4-5",
    "metadata": { "tenant_id": "acme", "device_id": "abc-123" }
  }'

The response returns the full key in the key field exactly once. Store it immediately - it cannot be retrieved later.

{
  "id": 42,
  "key": "sk-syn-d4f0...e91b",
  "key_prefix": "sk-syn-d4f0...",
  "name": "Device abc-123",
  "kind": "inference",
  "created_via": "provisioning_api",
  "parent_provisioning_id": 7,
  "workspace_id": 11,
  "quota_usd": 5.0,
  "unlimited_quota": false,
  "allowed_models": "claude-haiku-4-5",
  "metadata": { "tenant_id": "acme", "device_id": "abc-123" },
  "created_at": "2026-06-01T12:00:00Z"
}

List inference keys

GET /api/provisioning/keys

Lists every inference key in the workspace. Filter by any metadata field with ?metadata.<key>=<value>; multiple filters are combined with AND.

# all keys
curl https://synthorai.io/api/provisioning/keys \
  -H "Authorization: Bearer sk-syn-admin-..."

# only keys tagged tenant_id=acme
curl "https://synthorai.io/api/provisioning/keys?metadata.tenant_id=acme" \
  -H "Authorization: Bearer sk-syn-admin-..."

Retrieve, update & delete

Address a single inference key by its numeric id:

  • GET /api/provisioning/keys/{id} - fetch one key.
  • PATCH /api/provisioning/keys/{id} - update name, quota, status, allowed_models, ip_whitelist, expires_at, include_byok_in_limit, or metadata. The kind and lineage fields cannot be changed.
  • DELETE /api/provisioning/keys/{id} - revoke a key. Effective immediately.
# disable a key (status 2 = disabled)
curl -X PATCH https://synthorai.io/api/provisioning/keys/42 \
  -H "Authorization: Bearer sk-syn-admin-..." \
  -H "Content-Type: application/json" \
  -d '{ "status": 2 }'

# delete a key
curl -X DELETE https://synthorai.io/api/provisioning/keys/42 \
  -H "Authorization: Bearer sk-syn-admin-..."

Quotas & spend control

quota is a USD cap per inference key. Usage is metered after each request, so a key may slightly exceed its cap within a short settlement window under heavy concurrency - size caps with a small buffer for high-value models. Read a key's current spend from used_usd in the list / retrieve responses.

✓

Tag keys at creation with metadata (tenant, device, environment) so you can list, audit, and bulk-revoke them later by filter. Keys created this way also appear in Console → API Keys with a Programmatic source badge and their tags.

Sizing per-key USD caps? The LLM API cost calculator converts a customer's expected token volume into a monthly spend figure you can set as the quota.