Verified live. Every header name and every sample value on this page was
captured with
curl -D - against the partner sandbox at
https://phosra-api-sandbox-production.up.railway.app on 2026-07-06. The
reads used below (/health, /api/v1/platforms, /.well-known/ocss/trust-list)
need no API key, so you can reproduce the exact bytes.Request headers
Headers you send. OnlyAuthorization (or X-Api-Key) is required for
authenticated routes; the RFC 9421 triplet is required only for signed census
writes.
| Header | When to send it | Example | Reference |
|---|---|---|---|
Authorization | Every authenticated call. Developer API key or WorkOS session JWT as a bearer token. | Authorization: Bearer phosra_live_4a1b2c… | Authentication |
X-Api-Key | Exact alias for Authorization: Bearer. Use only when a proxy or framework reserves Authorization. If both are present, Authorization wins. | X-Api-Key: phosra_live_4a1b2c… | Authentication |
X-Device-Key | On-device enforcement agents (iOS FamilyControls) instead of a developer key. | X-Device-Key: phosra_dev_… | Authentication |
Content-Type | Any request with a body (POST / PUT / PATCH). Phosra speaks JSON. | Content-Type: application/json | — |
Accept | Optional. Responses are JSON regardless; send it to be explicit. | Accept: application/json | — |
Signature-Input | Signed census writes only (binding mint, rotate, enforcement-confirmations, bound-resolver profile GETs). The RFC 9421 covered-components line. | see Signed writes | RFC 9421 |
Signature | Paired with Signature-Input. The Ed25519 signature bytes. Exactly one of each is required. | Signature: ocss=:<base64-ed25519>: | Errors · 401 |
Content-Digest | Signed writes with a body. SHA-256 of the exact body bytes; covered by the signature. | Content-Digest: sha-256=:<base64-sha256>: | RFC 9421 |
OCSS-Spec-Version | Signed census writes. The census-spec version your payload targets; it is a covered signature component. | OCSS-Spec-Version: 2026-06 | Versioning |
Response headers
Headers Phosra sets. The rate-limit trio rides every/api/v1/* response; the
correlation id rides everything.
| Header | On which responses | Example | Reference |
|---|---|---|---|
Content-Type | All | application/json | — |
X-RateLimit-Limit | Every /api/v1/* response | 100 | Rate limits |
X-RateLimit-Remaining | Every /api/v1/* response | 87 | Rate limits |
X-RateLimit-Reset | Every /api/v1/* response. Unix epoch seconds the window resets — not a duration. | 1783344660 | Rate limits |
Retry-After | 429 Too Many Requests only. Seconds to wait. | 60 | Rate limits |
Deprecation | An endpoint/field in a sunset window (RFC 9745). | @1788220800 | Deprecation policy |
Sunset | An endpoint with a committed removal date (RFC 8594). HTTP-date. | Tue, 01 Dec 2026 00:00:00 GMT | Deprecation policy |
Link | Deprecated surfaces — points to the migration guide, rel="deprecation" / rel="sunset". | <https://…>; rel="deprecation" | Deprecation policy |
ETag | Cacheable census reads (e.g. the Trust List) for conditional If-None-Match fetches. | "32e5fb0d…" | Trust Framework |
OCSS-Replay | A faithful idempotent replay: original. Its absence means a fresh apply. | OCSS-Replay: original | Idempotency |
X-Railway-Request-Id | Every response. The correlation id to quote in a support ticket. | 2HO1Woj4RFGk9Z7VjVra_w | The correlation id |
Authentication headers
A developer API key goes inAuthorization: Bearer. This is the canonical form
and the one every SDK sends:
X-Api-Key: phosra_… is accepted as an exact equivalent — the API checks
Authorization: Bearer first and falls back to X-Api-Key. Reach for it only
when a proxy or framework reserves the Authorization header; otherwise prefer
the bearer form so API keys, WorkOS JWTs, and MCP tokens all share one header.
Full details — key formats, scoping, rotation — live in
Authentication.
Content negotiation
The API speaks JSON in and JSON out. SendContent-Type: application/json on
any request with a body; Accept is optional because responses are JSON
regardless. Signed writes send their body as raw JSON bytes — the exact bytes
you signed the Content-Digest over, never a re-stringified object.
Signed writes (RFC 9421)
Census writes — binding mint (POST /enforcement-endpoints), key rotation,
POST /enforcement-confirmations, and bound-resolver profile GETs — are
authenticated with an RFC 9421
HTTP Message Signature (Ed25519), not just a bearer key. You send exactly one
Signature-Input and one Signature, plus a Content-Digest covering the
body:
keyid must be a did#kid published on the Trust List.
Get the triplet wrong — missing, malformed, duplicated, or an unknown key — and
the census returns 401 signature_invalid before any standing check:
signRequest helper builds all four headers for you — see
Idempotency for a full signed-write example and
Errors for the failure modes.
Rate-limit headers
Every/api/v1/* response tells you exactly where you stand in your window, so
you never have to guess. Read the headers on the response you already have:
X-RateLimit-Reset is Unix epoch seconds, not a duration. When you exhaust
the window you get 429 Too Many Requests with a Retry-After (seconds).
Branch on the status code and honour Retry-After — never parse a
human-readable error string. The full backoff loop, in four languages, is on
Rate limits.
Census safety paths —
/.well-known/ocss/*, the Trust List, enforcement
writes, consent ingest — are never rate-limited (fail-open is a P0
invariant), so they carry no X-RateLimit-* headers. A /.well-known/ocss/trust-list
read returns a cacheable ETag instead:Deprecation signals
Before anything inv1 changes, it carries machine-readable notice on the wire
so a gateway can detect it without reading these docs. A surface in a sunset
window responds with:
Deprecation— RFC 9745, an@-prefixed Unix timestamp of when the deprecation took effect.Sunset— RFC 8594, an HTTP-date of the committed removal.Link— a pointer to the human migration guide.
Sunset header in staging and you will never be
surprised by a removal. Windows and the full lifecycle are on the
Deprecation & Sunset policy.
Idempotency replay
A faithful retry — same body-borne key, same payload — returns the original bytes with200 OK and the header OCSS-Replay: original. Its absence
means a fresh apply. Read the status and this header; never diff bodies. A
conflicting replay (same key, changed payload) is rejected with 409 replay and
carries no OCSS-Replay. Full semantics on Idempotency.
The correlation id
Every response carries a correlation id you can quote in a support ticket so we can pull the exact request from our logs. Grab it before you contact anyone:Straight talk about this header’s name. The correlation id is currently
surfaced as
X-Railway-Request-Id, assigned by the sandbox’s hosting edge
(the census runs on Railway). It is a real, stable-per-request id our support
team can look up — it is not a Phosra-issued API field, and the name may
change to a vendor-neutral alias in a future release. Quote whatever
x-railway-request-id value the failing response actually returned; don’t
synthesize one, and don’t send an inbound X-Request-Id expecting it to be
echoed (it isn’t).See every header at once
curl -D - dumps the full response head. This is the fastest way to inspect
what Phosra actually set on any call:
Related
Authentication
Key formats, the
Authorization vs X-Api-Key fallback, scoping, rotation.Rate limits
The
X-RateLimit-* trio, Retry-After, and a copy-paste backoff loop.Idempotency
Why there is no
Idempotency-Key header, and what OCSS-Replay means.Deprecation & Sunset
The
Deprecation / Sunset / Link signals and the notice windows.Errors
Every status, class, and the
401 signature_invalid header failures.Support
What to send with a ticket — starting with the correlation id above.