API Key Management
API keys authenticate your server to the SwitchLock API. They are long-lived secrets — treat them like passwords.
Keys are server secrets
Every API key is a bearer credential. Anyone who holds a valid key can read and write data for your company. The rules below are not optional — a leaked key is a full account compromise.
- ✕ Include a key in client-side JavaScript (browser, React Native)
- ✕ Commit a key to version control, even in a private repo
- ✕ Log a key in application logs or monitoring services
- ✕ Share a single key across staging and production
- Store keys in environment variables or a secrets manager
- Issue one key per environment (staging, production)
- Revoke and reissue when a team member leaves
- Give each key a descriptive name so you know what to revoke
Key format
All keys start with zl_sk_ followed by 32 random alphanumeric characters — 38 characters total. The prefix encodes nothing about environment; use the key's name field and your own naming convention to distinguish staging from production.
The first 8 characters of the random portion are stored as key_prefix for display in the dashboard and list API. This lets you identify a key without exposing the full secret.
How keys are stored — SHA-256 hashing
The raw key is never stored. At the moment you generate a key, SwitchLock computes a SHA-256 hash of the raw value, stores only that hash, and returns the raw key in the response body exactly once. On each subsequent API call, the incoming key is hashed and compared — the raw value is never written to disk.
If you lose a key, you cannot recover it. Revoke it and generate a replacement. There is no "show key" endpoint.
Generate a key
Generating a key requires an authenticated admin session (JWT Bearer token). Log in as a company admin first, then POST to the key endpoint. The raw key is in the response — copy it before moving on.
curl -X POST "https://api.switchlock.co.zw/v1/auth/login" \
-H "Content-Type: application/json" \
-d '{
"email": "admin@acme.co.zw",
"password": "SuperSecret123!"
}'{
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expiresIn": 3600
}curl -X POST "https://api.switchlock.co.zw/v1/core/companies/api-keys" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
-H "x-company-id: cmp_01HXYZ..." \
-H "Content-Type: application/json" \
-d '{ "name": "Production Server Key" }'{
"id": "key_01HXYZ...",
"name": "Production Server Key",
"key": "zl_sk_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
"key_prefix": "a1b2c3d4",
"is_active": true,
"created_at": "2026-06-23T09:01:00.000Z"
}The key field is only present in this response. Copy it to your environment variables now — it will not appear again.
| Field | Required | Description |
|---|---|---|
| name | Yes | Human-readable label. Use something that tells you where it's deployed, e.g. Production API Server or Staging CI. |
Using a key in requests
Pass the raw key in the x-api-key header alongside x-company-id on every authenticated call.
curl "https://api.switchlock.co.zw/v1/core/companies/me" \
-H "x-api-key: zl_sk_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6" \
-H "x-company-id: cmp_01HXYZ..."List API key metadata
Returns the metadata records for your company's API integrations, ordered newest first. This is used to populate your dashboard so you know which IDs to pass to the revoke endpoint. The raw key is never returned here.
curl "https://api.switchlock.co.zw/v1/core/companies/api-keys" \
-H "x-api-key: zl_sk_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6" \
-H "x-company-id: cmp_01HXYZ..."[
{
"id": "key_01HXYZ...",
"name": "Production Server Key",
"key_prefix": "a1b2c3d4",
"is_active": true,
"created_at": "2026-06-23T09:01:00.000Z"
},
{
"id": "key_01HABC...",
"name": "Staging CI Key",
"key_prefix": "b5c6d7e8",
"is_active": true,
"created_at": "2026-06-20T08:30:00.000Z"
}
]The key_prefix shows the first 8 characters of the raw key — enough to identify which key is which without revealing the secret.
Revoke a key
Revoking sets is_active = false on the key record. Any subsequent request using that key returns 401 Unauthorized immediately. The key record is retained for audit purposes — it is not deleted.
curl -X POST "https://api.switchlock.co.zw/v1/core/companies/api-keys/key_01HXYZ.../revoke" \
-H "x-api-key: zl_sk_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6" \
-H "x-company-id: cmp_01HXYZ..."{ "success": true }Revocation is scoped to your company. You can only revoke keys that belong to the company in the x-company-id header — this prevents cross-company key tampering even if you somehow obtain another company's key ID.
Best practices
Staging and production should use entirely separate keys — ideally separate company registrations. This limits blast radius if a key leaks.
When someone with access to your production key leaves, revoke it and generate a replacement before their access is removed from other systems.
If a key appears in a public repository, a log file, or a Slack message — assume it has been read. Revoke it immediately, then audit recent API activity for unexpected calls.
Name keys so you know exactly which system uses them: "Production API Server", "Staging CI Pipeline", "Webhook Relay Lambda". When something goes wrong at 2am, you want to know what you're revoking.
Quick reference
| Action | Method | Path | Auth |
|---|---|---|---|
| Generate key | POST | /v1/core/companies/api-keys | JWT Bearer |
| List keys | GET | /v1/core/companies/api-keys | x-api-key + x-company-id |
| Revoke key | POST | /v1/core/companies/api-keys/:id/revoke | x-api-key + x-company-id |
Next steps
Add and remove human logins. Roles, permissions, and the JWT flow for admin sessions.
Full reference for x-api-key header format, error codes, and JWT refresh flows.
Multi-tenant architecture, isolation guarantees, MERCHANT vs DEVELOPER business models.
End-to-end walkthrough from registration through funding your first escrow.