API keys
Create, list, deactivate, and revoke Tokeflow API keys programmatically — discover assignable scopes, pin keys to IP allowlists, and rotate credentials safely.
Every request to the Tokeflow API authenticates with an API key — a credential bound to exactly one entity (an organization or one of its merchants) and to an explicit list of scopes. The endpoints on this page manage those keys programmatically: create a key with least-privilege scopes, list what exists, pause a key temporarily, and revoke it for good. Everything the Tokeflow Dashboard does with keys, your backend can automate — useful for onboarding flows that mint a key per merchant, and for scheduled credential rotation.
Keys come in two types. Secret keys (sk_…) grant server-side access and are sent in the Authorization: Bearer header; public keys (pk_…) are client-safe and restricted to the scopes the Bridge SDK needs in the browser. See Authentication for the full model, and Environments & API keys for how keys encode environment and entity in their prefix.
The full key is returned exactly once. The api_key field appears only in the creation response — Tokeflow stores a hash, not the key itself, and cannot show it again. Store it in your secrets manager immediately. Every later response identifies the key by its non-secret key_prefix only.
Key management is entity-scoped, not merchant-scoped. Where most resources take a merchant_id, these endpoints address their target with the entity_type + entity_id pair — because a key can belong to the organization itself, not just to a merchant. A Merchant key only ever sees and manages its own merchant's keys.
Key lifecycle and rotation
When rotating, always create and deploy the replacement key before revoking the old one — revocation takes effect immediately, and any service still holding the old key starts receiving 401 authentication_error.
The API key object
Returned by every endpoint on this page. The creation response additionally carries api_key — the full secret — one time only.
| Field | Type | Description |
|---|---|---|
id | string | Unique identifier for the API key, prefixed ak_. |
entity_type | string | Type of entity the key belongs to — organization or merchant. |
entity_id | string | ID of the owning entity (org_… or mrc_…). |
name | string | Human-readable name for the key. |
key_prefix | string | Non-secret key prefix used for identification (e.g. sk_live_mer_7d21). The only part of the key shown after creation. |
scopes | array | Scopes/permissions granted to the key. |
allowed_ips | array | null | IP addresses and CIDR ranges allowed to use the key. null when the key is unrestricted. |
is_active | boolean | Whether the key is active. Requests with an inactive key fail with 401 authentication_error. |
expires_at | string | null | Expiration date (ISO 8601 UTC), or null if the key never expires. |
last_used_at | string | null | Last time the key was used, or null if it has never been used. |
created_at | string | Creation timestamp (ISO 8601 UTC). |
api_key | string | The actual API key. Only returned on creation. |
key_hash | string | Hash of the key material, used server-side for verification. Not a credential — it cannot be used to authenticate. |
last_used_at tells you whether a key is actually in use. Before revoking a key you believe is idle, check that its last_used_at has not moved recently — a stale name does not guarantee a stale key.
Endpoints
GET/api/v1/api-keys
Auth: Organization key (all keys in the org; filter with entity_type/entity_id) or Merchant key (its own keys). Scope: api_keys:read.
Returns a paginated list of API keys. Organizations can filter by entity_type and entity_id to view the keys of a specific merchant.
Query parameters
| Field | Type | Required | Description |
|---|---|---|---|
entity_type | string | No | Filter by entity type — organization or merchant. |
entity_id | string | Conditional | Filter by entity ID (e.g. mrc_8a3f12d9). Required when filtering by merchant. |
page | integer | No | Page number, 1-indexed (default 1, min 1). |
limit | integer | No | Items per page (default 20, min 1, max 100). |
Example request
curl -G https://api.tokeflow.com/api/v1/api-keys \
-H "Authorization: Bearer sk_live_org_2b7e91c4a0f53d68" \
--data-urlencode "entity_type=merchant" \
--data-urlencode "entity_id=mrc_8a3f12d9" \
--data-urlencode "page=1" \
--data-urlencode "limit=20"Example response — 200 OK
{
"data": [
{
"id": "ak_9f2e6c81",
"entity_type": "merchant",
"entity_id": "mrc_8a3f12d9",
"name": "Checkout service (production)",
"key_prefix": "sk_live_mer_7d21",
"scopes": ["transactions:read", "transactions:write"],
"allowed_ips": ["203.0.113.10", "198.51.100.0/24"],
"is_active": true,
"expires_at": null,
"last_used_at": "2026-08-11T18:22:04.000Z",
"created_at": "2026-03-02T09:15:00.000Z",
"key_hash": "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3"
},
{
"id": "ak_5b0d3a47",
"entity_type": "merchant",
"entity_id": "mrc_8a3f12d9",
"name": "Storefront widget",
"key_prefix": "pk_live_mer_0c1d",
"scopes": ["tokens:read"],
"allowed_ips": null,
"is_active": false,
"expires_at": "2026-12-31T23:59:59.000Z",
"last_used_at": "2026-07-30T10:41:52.000Z",
"created_at": "2026-01-20T16:00:00.000Z",
"key_hash": "3c363836cf4e16666669a25da280a1865c2d2874"
}
],
"meta": {
"page": 1,
"limit": 20,
"total": 2,
"total_pages": 1,
"has_next": false,
"has_prev": false
}
}data holds the keys for the current page and meta the pagination metadata — advance page while has_next is true. See Pagination & filtering for the general pattern.
POST/api/v1/api-keys
Auth: Organization key (keys for itself or any of its merchants) or Merchant key (its own keys only). Scope: api_keys:write.
Creates an API key for an organization or merchant. The response is the only place the full key (api_key) is ever returned.
Request fields
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Human-readable name for the key (max 255 characters). Name it after the workload, e.g. Checkout service (production). |
scopes | array of strings | Yes | Scopes/permissions for the key — at least one. Must be valid for the target entity and key type; discover the assignable values with GET /api-keys/scopes. |
entity_type | string | Conditional | Type of entity the key belongs to — organization or merchant. Omit to create a key for the authenticated entity; an Organization key sends merchant (with entity_id) to mint a key for one of its merchants. |
entity_id | string | Conditional | ID of the entity the key belongs to (org_… or mrc_…). Send together with entity_type. |
key_type | string | No | Type of key to create — secret (server-side, sk_…) or public (client-safe, pk_…). |
allowed_ips | array of strings | No | IP addresses allowed to use the key — up to 50 entries, each up to 45 characters. Omit the field entirely for no restriction — an empty array is rejected, since it reads as a restriction but enforces none. See the note below for the accepted formats. |
expires_at | string | No | Expiration date for the key (ISO 8601). Omit for a key that never expires. |
allowed_ips formats. IPv4 addresses must be dotted-quad (203.0.113.10); CIDR entries must be in network form (192.168.1.0/24, not 192.168.1.5/24); IPv6 addresses and ranges are accepted (2001:db8::/32). A wildcard entry — *, 0.0.0.0/0, or ::/0 — allows every IP and cannot be combined with specific addresses. See IP allowlisting for guidance on when to pin IPs.
Create one key per workload, with only the scopes that workload needs. Narrow, single-purpose keys limit blast radius if a key leaks, and make each key's last_used_at meaningful when you audit or rotate.
Response codes
| Code | When |
|---|---|
201 Created | Key created. api_key in the body is your only chance to capture the full key. |
400 Bad Request | Invalid input — unknown scope, malformed or empty allowed_ips, or a bad expires_at. |
403 Forbidden | Insufficient permissions — for example, a Merchant key trying to create a key for another entity. |
Example request
curl -X POST https://api.tokeflow.com/api/v1/api-keys \
-H "Authorization: Bearer sk_live_org_2b7e91c4a0f53d68" \
-H "Content-Type: application/json" \
-d '{
"entity_type": "merchant",
"entity_id": "mrc_8a3f12d9",
"name": "Checkout service (production)",
"scopes": ["transactions:read", "transactions:write"],
"allowed_ips": ["203.0.113.10", "198.51.100.0/24"],
"key_type": "secret"
}'Example response — 201 Created
{
"id": "ak_9f2e6c81",
"entity_type": "merchant",
"entity_id": "mrc_8a3f12d9",
"name": "Checkout service (production)",
"key_prefix": "sk_live_mer_7d21",
"scopes": ["transactions:read", "transactions:write"],
"allowed_ips": ["203.0.113.10", "198.51.100.0/24"],
"is_active": true,
"expires_at": null,
"last_used_at": null,
"created_at": "2026-08-12T14:03:00.000Z",
"api_key": "sk_live_mer_7d21c39ab8e4f560",
"key_hash": "6dcd4ce23d88e2ee9568ba546c007c63d9131c1b"
}Handle this response like the credential it contains: write api_key straight to your secrets manager, and keep the response body out of logs, error trackers, and analytics. From here on, identify the key by id and recognize it by key_prefix.
GET/api/v1/api-keys/scopes
Auth: Organization key or Merchant key. Scope: api_keys:read.
Returns all valid scopes that can be assigned to API keys. Filter by entity_type (organization/merchant) and/or key_type (secret/public) to narrow the results to what a given key could actually carry — useful for validating a scopes list before calling POST /api-keys, or for building a key-creation form.
Query parameters
| Field | Type | Required | Description |
|---|---|---|---|
entity_type | string | No | Filter scopes by entity type. Merchant keys exclude org-only scopes (merchants, organizations, team). |
key_type | string | No | Filter scopes by key type. Public keys are restricted to client-safe scopes (tokens, instruments, customers). |
Example request
curl -G https://api.tokeflow.com/api/v1/api-keys/scopes \
-H "Authorization: Bearer sk_live_org_2b7e91c4a0f53d68" \
--data-urlencode "entity_type=organization" \
--data-urlencode "key_type=secret"Example response — 200 OK (truncated)
{
"scopes": [
{
"scope": "transactions:read",
"description": "List and view transactions"
},
{
"scope": "transactions:write",
"description": "Create, capture, and refund transactions"
},
{
"scope": "customers:read",
"description": "List and view customers"
}
],
"wildcards": [
"org:*",
"org:transactions:*"
]
}Each item in scopes pairs the scope string with a human-readable description. wildcards lists the wildcard scope strings valid for the requested entity/key type — a wildcard grants every scope it covers, so reserve them for trusted, tightly-controlled services and prefer explicit scope lists everywhere else.
PATCH/api/v1/api-keys/:id
Auth: Organization key (any key in the org) or Merchant key (its own keys). Scope: api_keys:write.
Updates a key's activation state. Set is_active to false to deactivate the key — requests using it start failing with 401 authentication_error immediately — or back to true to reactivate it. Use this as the reversible switch; use revoke when the key should never come back.
Request fields
| Field | Type | Required | Description |
|---|---|---|---|
is_active | boolean | No | Whether the API key is active. false deactivates the key; true reactivates it. |
Example request
curl -X PATCH https://api.tokeflow.com/api/v1/api-keys/ak_9f2e6c81 \
-H "Authorization: Bearer sk_live_org_2b7e91c4a0f53d68" \
-H "Content-Type: application/json" \
-d '{ "is_active": false }'Example response — 200 OK
{
"id": "ak_9f2e6c81",
"entity_type": "merchant",
"entity_id": "mrc_8a3f12d9",
"name": "Checkout service (production)",
"key_prefix": "sk_live_mer_7d21",
"scopes": ["transactions:read", "transactions:write"],
"allowed_ips": ["203.0.113.10", "198.51.100.0/24"],
"is_active": false,
"expires_at": null,
"last_used_at": "2026-08-11T18:22:04.000Z",
"created_at": "2026-08-12T14:03:00.000Z",
"key_hash": "6dcd4ce23d88e2ee9568ba546c007c63d9131c1b"
}A 403 is returned if the key exists but does not belong to your entity, and a 404 if it does not exist.
POST/api/v1/api-keys/:id/revoke
Auth: Organization key (any key in the org) or Merchant key (its own keys). Scope: api_keys:write.
Revokes an API key, deactivating it permanently. Revocation takes effect immediately: any request presenting the revoked key fails with 401 authentication_error. Unlike a PATCH deactivation, a revoked key is retired for good — if the workload still needs access, create a replacement key first.
Example request
curl -X POST https://api.tokeflow.com/api/v1/api-keys/ak_9f2e6c81/revoke \
-H "Authorization: Bearer sk_live_org_2b7e91c4a0f53d68"Example response — 200 OK
{
"id": "ak_9f2e6c81",
"entity_type": "merchant",
"entity_id": "mrc_8a3f12d9",
"name": "Checkout service (production)",
"key_prefix": "sk_live_mer_7d21",
"scopes": ["transactions:read", "transactions:write"],
"allowed_ips": ["203.0.113.10", "198.51.100.0/24"],
"is_active": false,
"expires_at": null,
"last_used_at": "2026-08-11T18:22:04.000Z",
"created_at": "2026-08-12T14:03:00.000Z",
"key_hash": "6dcd4ce23d88e2ee9568ba546c007c63d9131c1b"
}A 403 is returned if the key does not belong to your entity, and a 404 if it does not exist.
If a secret key leaks, revoke it now — before creating the replacement. A brief outage of one workload is a better outcome than an attacker holding a live server-side credential. For planned rotation (no exposure), do the reverse: create and deploy the new key first, confirm it works, then revoke the old one.
Errors
Errors use the standard envelope. The most common cases for this resource:
| HTTP | type | Typical cause |
|---|---|---|
400 | validation_error | Unknown scope, empty or malformed allowed_ips, missing name or scopes. |
401 | authentication_error | Missing, invalid, deactivated, or revoked API key on the request itself. |
403 | authorization_error | Insufficient permissions, or the target key does not belong to your entity. |
404 | not_found_error | API key not found. |
429 | rate_limit_error | Rate limit exceeded — back off exponentially. |
See Errors for the full envelope and type catalog.
Related
- Authentication — the Bearer scheme, scopes, and key anatomy.
- Environments & API keys — key lifecycle, storage, and rotation practices.
- Pagination & filtering — list conventions used by the list endpoint.
- Errors — the standard error envelope.