API keys
Every VerifyAI request authenticates with a vai_… API key in the
X-API-Key header. This page covers managing those keys: what a key
looks like, how to create and revoke them, and the one rule that trips
people up — the full key is shown exactly once.
For how keys are used on a request, see Authentication.
The key object
A key is scoped to a single customer account. The server stores only a SHA-256 hash of the key — never the plaintext — so a key that's lost can only be replaced, never recovered.
{
"id": "key_4f9a2c",
"name": "Production backend",
"key_prefix": "vai_live_8x9k…",
"scope": "full",
"active": true,
"customer_id": "cus_forest1",
"rate_limit_per_minute": 100,
"rate_limit_per_hour": 6000,
"last_used_at": "2026-06-01T11:58:02Z",
"created_at": "2026-05-12T14:30:00Z",
"expires_at": null
}Fields
| Field | Type | Description |
| ----------------------- | -------- | ------------------------------------------------------------------------------------ |
| id | string | Stable key identifier, prefix key_. Use this to revoke. Not the secret. |
| name | string | Human label you set at creation (e.g. Production backend, iOS app). |
| key_prefix | string | The first few characters of the key, for identifying it in lists. Never the full key.|
| scope | string | full or verify_only. See Scopes. |
| active | boolean | false once revoked. Inactive keys return 401 Invalid API key. |
| customer_id | string? | The customer this key acts on behalf of. See Customers.|
| rate_limit_per_minute | number | Per-key request cap per minute. Defaults to 100. |
| rate_limit_per_hour | number | Per-key request cap per hour. Defaults to 6000. |
| last_used_at | string? | ISO 8601 timestamp of the most recent authenticated request. null if never used. |
| created_at | ISO 8601 | When the key was created. |
| expires_at | string? | Optional expiry. null means the key never expires until revoked. |
Create returns the complete vai_… secret a single time. We store only
a hash, so we cannot show it again. Copy it into your secret manager
immediately. If you lose it, revoke the key and create a new one.
Managing keys in the dashboard
The fastest path is the dashboard, which is the supported way to author keys today:
- Open Dashboard → Settings → API keys.
- Click Create key, give it a name, and pick a scope.
- Copy the full
vai_…value from the one-time reveal into your secret manager. - To retire a key, click Revoke next to it. Revocation takes effect
immediately — in-flight requests using that key start returning
401.
Sandbox keys are issued automatically when you sign up; their usage is free and never bills your account.
REST contract
The endpoints below expose the same operations programmatically. They are account-scoped — a key can only manage keys belonging to its own customer — and they authenticate exactly like every other VerifyAI request.
Programmatic key management is rolling out behind the same auth as the
rest of the API. If a call below returns 404, manage keys from
Dashboard → Settings → API keys in the meantime — the request and
response shapes match what ships.
Authentication
| Header | Required | Value |
| ----------- | -------- | ------------------------------------------------------ |
| X-API-Key | Yes | A vai_… key with full scope. verify_only keys cannot manage keys. |
List keys
GET /api/v1/api-keysReturns every key on your account, newest first. The plaintext secret is
never included — only id, key_prefix, and metadata.
curl https://verify.switchlabs.dev/api/v1/api-keys \
-H "X-API-Key: vai_your_api_key"{
"data": [
{
"id": "key_4f9a2c",
"name": "Production backend",
"key_prefix": "vai_live_8x9k…",
"scope": "full",
"active": true,
"rate_limit_per_minute": 100,
"rate_limit_per_hour": 6000,
"last_used_at": "2026-06-01T11:58:02Z",
"created_at": "2026-05-12T14:30:00Z",
"expires_at": null
}
]
}Create a key
POST /api/v1/api-keysCreates a new key on your account. The response is the only place the full secret appears.
Body parameters
| Parameter | Type | Required | Description |
| ------------ | ------ | -------- | --------------------------------------------------------------------------- |
| name | string | Yes | A label to identify the key later (e.g. iOS app). |
| scope | string | No | full (default) or verify_only. See Scopes. |
| expires_at | string | No | ISO 8601 expiry. Omit for a non-expiring key. |
curl -X POST https://verify.switchlabs.dev/api/v1/api-keys \
-H "X-API-Key: vai_your_api_key" \
-H "Content-Type: application/json" \
-d '{ "name": "iOS app", "scope": "verify_only" }'{
"id": "key_7b1d8e",
"name": "iOS app",
"key": "vai_live_3q5w8r1t6y2u4i9o0a3s8x9k2m4n7p3q",
"key_prefix": "vai_live_3q5w…",
"scope": "verify_only",
"active": true,
"rate_limit_per_minute": 100,
"rate_limit_per_hour": 6000,
"created_at": "2026-06-01T12:00:00Z",
"expires_at": null
}The key field is present only on this create response. Every other
endpoint returns key_prefix instead.
Revoke a key
DELETE /api/v1/api-keys/:idRevokes a key by its key_ id. Revocation is immediate and permanent —
the key flips to active: false and all future requests with it return
401. There is no un-revoke; create a fresh key instead.
curl -X DELETE https://verify.switchlabs.dev/api/v1/api-keys/key_7b1d8e \
-H "X-API-Key: vai_your_api_key"{ "id": "key_7b1d8e", "active": false, "revoked_at": "2026-06-01T12:05:00Z" }Scopes
A key's scope limits what it can do. Use the narrowest scope that works:
| Scope | Can do |
| ------------- | ------------------------------------------------------------------- |
| full | Everything — verify, read history, and manage keys/customers/webhooks. |
| verify_only | Submit photos to /v1/verify only. Cannot list, manage keys, or delete. |
A key embedded in a mobile app is semi-public — anyone who unpacks the
binary can read it. Scope embedded keys to verify_only so a leaked
key can submit photos but cannot enumerate your verifications or touch
your other keys. Rotate after each major app release.
Rate limits
Each key carries its own rate_limit_per_minute (default 100) and
rate_limit_per_hour (default 6000). When a key exceeds either window,
requests return 429 with a Retry-After header indicating how many
seconds to wait. Limits are enforced per key, so splitting traffic across
keys gives each its own budget. Contact support to raise the defaults on
a key.
Errors
| Status | Reason |
| ------ | ------------------------------------------------------------------- |
| 400 | Missing name, or an invalid scope / expires_at value. |
| 401 | Missing or invalid API key. |
| 403 | The authenticating key is verify_only and cannot manage keys. |
| 404 | No key with that id on this account. |
| 429 | Rate limit — check the Retry-After header. |
| 503 | Authentication service unavailable. Retry. |
What's next
- Authentication — how to send a key on every request, plus rotation guidance.
- Customers API — the account a key acts on behalf of.
- Verify endpoint — what a
vai_key authorizes you to call.