Conventions
HTTP headers
Every request and response header the APIs read or send through passkey-x.com/api, and the headers on webhook deliveries.
Every header the APIs read or send through passkey-x.com/api, whichever API answers (Customer, Organization or Agent). Header names are case-insensitive; browser code on allowed origins can read every response header listed here. The SDK reads them for you (SDK and headers).
Request headers
| Header | APIs | What it means | Example |
|---|---|---|---|
Authorization | All | Bearer credential: a personal key (pxu_…), an organization key (pxk_…), an agent token (pxa1.…) or a signed-in session's access token. | Authorization: Bearer pxk_3f9a… |
Content-Type | All (writes) | Bodies are JSON. Anything else is answered 415 unsupported_media_type. | Content-Type: application/json |
Accept | All | Optional. Every answer is JSON. | Accept: application/json |
Idempotency-Key | All (POST, PUT, PATCH, DELETE) | 8–128 letters, digits, dots, dashes, underscores or colons. A repeat with the same key and body returns the first answer with Idempotent-Replayed: true (Organization API: kept 24 hours; Customer and Agent API: 10 minutes at the gateway). The same key with another body is 422 idempotency_key_reused; while the first is still running, 409 idempotency_in_progress. An invalid key is 400 invalid_idempotency_key. | Idempotency-Key: 7b4f0c1e-2d3a-4b5c-8d9e-0f1a2b3c4d5e |
If-Match | Customer API | The ETag of the revision you read, on PATCH and DELETE of vault items. A newer revision answers 412 revision_conflict. | If-Match: "3" |
If-None-Match | All (GET) | The ETag of an answer you already have. When nothing changed the answer is 304 Not Modified with no body (it still counts toward the rate limit). | If-None-Match: W/"q2v9X0f3kL1m8n7b6v5c4x" |
X-Request-Id | All | Optional: your own trace id (1–128 safe characters). It comes back as X-Client-Request-Id; our own X-Request-Id stays the one to quote to support. | X-Request-Id: checkout-42-retry-1 |
apikey | Session calls | Only with a session access token: the project's publishable key. Not needed with pxu_, pxk_ or pxa1. credentials. | apikey: sb_publishable_… |
X-Client-Info | All | Optional client name and version, for your own logs (the SDK sends it). | X-Client-Info: passkey-x-sdk-js/1.1.0 |
Response headers
| Header | APIs | What it means | Example |
|---|---|---|---|
X-Request-Id | All | Unique id of this request, also in every error body as request_id. Quote it to support; log it instead of bodies. | X-Request-Id: 6f1c2a9e-4b7d-4e2a-9c1f-3d5e7a9b1c2d |
Content-Type | All | Every body is JSON (UTF-8). | Content-Type: application/json; charset=utf-8 |
Cache-Control | All | Answers are never stored by browsers or proxies. | Cache-Control: no-store |
X-Content-Type-Options | All | Browsers must not guess another content type. | X-Content-Type-Options: nosniff |
RateLimit-Limit | All | Requests allowed in the current window (IETF draft name). | RateLimit-Limit: 300 |
RateLimit-Remaining | All | Requests left in the current window. | RateLimit-Remaining: 297 |
RateLimit-Reset | All | Seconds until the window resets (on 429: the same as Retry-After). | RateLimit-Reset: 41 |
RateLimit-Policy | All | The limit and its window in seconds: 120;w=60 (Customer), 300;w=60 (Organization), 1;w=1 (Agent). | RateLimit-Policy: 300;w=60 |
X-RateLimit-Limit | Customer, Organization | Older name of RateLimit-Limit, kept for existing clients. | X-RateLimit-Limit: 300 |
X-RateLimit-Remaining | Customer, Organization | Older name of RateLimit-Remaining, kept for existing clients. | X-RateLimit-Remaining: 297 |
Retry-After | All | Seconds to wait: on every 429, and on 502/503 when the service is briefly unavailable. Add jitter. | Retry-After: 60 |
ETag | All (GET) | Vault items: the revision ("3"), to send back as If-Match. Other reads: a weak hash of the body (W/"…"), to send back as If-None-Match. | ETag: W/"q2v9X0f3kL1m8n7b6v5c4x" |
Link | All (lists) | The next page (rel="next") and the first page (rel="first") as relative URLs, built from next_cursor (or next_after for audit events). rel="deprecation" points at the notice of a deprecated parameter. | Link: </api/org/v1/members?limit=100&cursor=b2Zmc2V0OjEwMA>; rel="next" |
Idempotency-Key | All (writes) | Your Idempotency-Key, echoed back. | Idempotency-Key: 7b4f0c1e-2d3a-4b5c-8d9e-0f1a2b3c4d5e |
Idempotent-Replayed | All (writes) | true when this is the stored first answer to a repeated Idempotency-Key. | Idempotent-Replayed: true |
Deprecation | Organization API | RFC 9745: the request used something deprecated, since this date (@unix seconds). Today: offset= paging (use cursor=). | Deprecation: @1792540800 |
Sunset | Organization API | RFC 8594: when the deprecated behaviour stops working. | Sunset: Wed, 30 Jun 2027 23:59:59 GMT |
API-Version | All | Version of the API contract that answered. | API-Version: 2026-10-01 |
X-Org-API-Version | Organization API | Organization API release. | X-Org-API-Version: 1.8 |
WWW-Authenticate | Customer API | On 401: Bearer (a credential is needed). On 403 insufficient_scope: the scope the personal key needs. | WWW-Authenticate: Bearer error="insufficient_scope", scope="vault:read" |
Allow | All | On 405: the methods the path accepts. | Allow: GET, POST, OPTIONS |
X-Client-Request-Id | All | Your own X-Request-Id, echoed back when you sent one. | X-Client-Request-Id: checkout-42-retry-1 |
Access-Control-Expose-Headers | All (browsers) | Every header above can be read by browser code on allowed origins. | Access-Control-Expose-Headers: X-Request-Id, ETag, Link, RateLimit-Limit, … |
Webhook delivery headers
Sent by Passkey-X to your endpoint. See verifying signatures.
| Header | APIs | What it means | Example |
|---|---|---|---|
X-PasskeyX-Webhook | Webhooks | The webhook (destination) id. | X-PasskeyX-Webhook: f1e2d3c4-b5a6-4978-8a9b-0c1d2e3f4a5b |
X-PasskeyX-Timestamp | Webhooks | Unix seconds when the delivery was signed. Reject anything older than five minutes. | X-PasskeyX-Timestamp: 1792540800 |
X-PasskeyX-Signature | Webhooks | v1=hex HMAC-SHA256(secret, "<timestamp>.<raw body>"). While a secret rotates there are two, comma-separated: v1=<new>,v1=<old>. Accept a delivery when any one matches. | X-PasskeyX-Signature: v1=5257a8…,v1=9c1d0e… |
X-PasskeyX-Event-Catalog | Webhooks | Version of the event catalog the delivery was built with. | X-PasskeyX-Event-Catalog: 2026-10-21 |
User-Agent | Webhooks | Always Passkey-X-Webhooks/1. | User-Agent: Passkey-X-Webhooks/1 |
A typical answer
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Cache-Control: no-store
X-Content-Type-Options: nosniff
X-Request-Id: 6f1c2a9e-4b7d-4e2a-9c1f-3d5e7a9b1c2d
API-Version: 2026-10-01
X-Org-API-Version: 1.8
RateLimit-Limit: 300
RateLimit-Remaining: 297
RateLimit-Reset: 41
RateLimit-Policy: 300;w=60
ETag: W/"q2v9X0f3kL1m8n7b6v5c4x"
Link: </api/org/v1/members?limit=100&cursor=b2Zmc2V0OjEwMA>; rel="next"Need help building on Passkey-X?
Questions about the API, a bug or a missing endpoint? Our team replies by email, usually within one business day.