Issue and verify API keys
An issued API key is a credential that Ory Talos generates. The admin surface creates the key, returns the full secret once at creation, and stores only a hash afterward. Use issued keys when you want Ory Talos to own the full lifecycle — generation, lookup, rotation, and revocation — for the credentials your customers or services use to call your API. For how issued keys compare to imported keys and derived tokens, see credential types.
This guide walks through the issue-and-verify flow end to end. CI runs every example.
Prerequisites
You need a running Ory Talos server and the talos CLI. See the quickstart to start one locally.
Issue an API key
Send a request to the admin endpoint to create a key:
- CLI
- curl
RESPONSE=$(talos keys issue "backend-service" \
--actor user_42 \
--scopes "read:orders,write:orders" \
--ttl 720h \
--metadata '{"team": "payments", "environment": "staging"}' \
--format json \
-e "$TALOS_URL" 2>/dev/null)
echo "$RESPONSE" | jq .
export API_SECRET=$(echo "$RESPONSE" | jq -er '.secret')
export KEY_ID=$(echo "$RESPONSE" | jq -er '.issued_api_key.key_id')
RESPONSE=$(curl -s -X POST "$TALOS_URL/v2alpha1/admin/issuedApiKeys" \
-H "Content-Type: application/json" \
-d '{
"name": "backend-service",
"actor_id": "user_42",
"scopes": ["read:orders", "write:orders"],
"ttl": "720h",
"metadata": {"team": "payments", "environment": "staging"}
}')
echo "$RESPONSE" | jq .
export API_SECRET=$(echo "$RESPONSE" | jq -er '.secret')
export KEY_ID=$(echo "$RESPONSE" | jq -er '.issued_api_key.key_id')
Request fields
The required fields are name (human-readable label) and actor_id (the key's actor). scopes, ttl, metadata, and
rate_limit_policy are optional. For the complete field reference, see the
IssueAPIKey API reference.
Over the HTTP API, ttl accepts protobuf seconds (86400s), Go durations (720h, 1h30m), and extended units (1d, 1w,
1mo, 1y) including compounds such as 1y6mo. The CLI --ttl flag accepts Go durations only, such as 720h and 1h30m.
Response fields
The response contains two top-level fields:
issued_api_key— The key metadata (ID, name, actor, scopes, status, and timestamps).secret— The full API key credential. Talos returns this value only once and never exposes it again. Store it securely.
For the complete issued_api_key field reference, see the
IssueAPIKey API reference.
Verify a key
Send the secret to the admin verify endpoint to check whether the credential is valid:
- CLI
- curl
talos keys verify "$API_SECRET" -e "$TALOS_URL"
curl -s -X POST "$TALOS_URL/v2alpha1/admin/apiKeys:verify" \
-H "Content-Type: application/json" \
-d "{\"credential\":\"$API_SECRET\"}" | jq .
Verification response
The response includes is_valid (true when verification succeeds), status (key lifecycle state), key_id, actor_id,
scopes, metadata, and expire_time. When is_valid is false, error_code and error_message give the reason. With rate
limit enforcement (Commercial), keys that have a rate limit policy also return rate_limit_remaining and rate_limit_reset_time.
For the complete field reference, see the VerifyAPIKey API reference.
Verification error codes
When is_valid is false, the error_code field indicates why. Common codes include VERIFICATION_ERROR_EXPIRED,
VERIFICATION_ERROR_REVOKED, VERIFICATION_ERROR_NOT_FOUND, and VERIFICATION_ERROR_RATE_LIMITED (Commercial, when the key
exhausts its rate limit quota). For the complete list, see the
verification error codes reference.
Cache bypass
Talos caches verification results. After revoking a key, bypass the cache to read its current state immediately:
curl -s -X POST "$TALOS_URL/v2alpha1/admin/apiKeys:verify" \
-H "Content-Type: application/json" \
-H "Cache-Control: no-cache" \
-d '{"credential":"sk_..."}'
Cache control headers:
| Header | Effect |
|---|---|
Cache-Control: no-cache | Bypass cache read, force fresh DB lookup |
Cache-Control: no-store | Bypass both cache read and write |
Pragma: no-cache | Same as no-cache (HTTP/1.0 compatibility) |
Retrieve a key by ID
Look up key metadata without the secret:
- CLI
- curl
talos keys issued get "$KEY_ID" -e "$TALOS_URL"
curl -s "$TALOS_URL/v2alpha1/admin/issuedApiKeys/$KEY_ID" | jq .
Talos never returns the secret from GET requests.
List keys
List all issued keys with optional filtering and pagination:
- CLI
- curl
talos keys issued list --actor user_42 --page-size 10 -e "$TALOS_URL"
curl -s "$TALOS_URL/v2alpha1/admin/issuedApiKeys?page_size=10&filter=actor_id%3D%22user_42%22" | jq .
Query parameters
Pagination is cursor-based with page_size (default 50, max 1000) and page_token. Filtering uses an
AIP-160 filter expression on the indexed fields actor_id and status, for example
filter=actor_id="user_42" or filter=status=KEY_STATUS_ACTIVE. The CLI --actor and --status flags translate into the
equivalent filter value. For the complete parameter reference, see the
ListIssuedAPIKeys API reference.
The response includes next_page_token. When empty, you've reached the last page.
Next steps
- Key lifecycle — update, rotate, and revoke keys
- Import keys — bring existing keys into Ory Talos
- Derive tokens — mint short-lived JWTs or macaroons
- Error handling — error response format and retry logic
