Skip to main content
Every header the Phosra API reads on the way in or sets on the way out, in one place. If you only skim one page before wiring up a client, make it this one — then jump to the deep-dive linked from each row.
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. Only Authorization (or X-Api-Key) is required for authenticated routes; the RFC 9421 triplet is required only for signed census writes.
HeaderWhen to send itExampleReference
AuthorizationEvery authenticated call. Developer API key or WorkOS session JWT as a bearer token.Authorization: Bearer phosra_live_4a1b2c…Authentication
X-Api-KeyExact 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-KeyOn-device enforcement agents (iOS FamilyControls) instead of a developer key.X-Device-Key: phosra_dev_…Authentication
Content-TypeAny request with a body (POST / PUT / PATCH). Phosra speaks JSON.Content-Type: application/json
AcceptOptional. Responses are JSON regardless; send it to be explicit.Accept: application/json
Signature-InputSigned census writes only (binding mint, rotate, enforcement-confirmations, bound-resolver profile GETs). The RFC 9421 covered-components line.see Signed writesRFC 9421
SignaturePaired with Signature-Input. The Ed25519 signature bytes. Exactly one of each is required.Signature: ocss=:<base64-ed25519>:Errors · 401
Content-DigestSigned 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-VersionSigned census writes. The census-spec version your payload targets; it is a covered signature component.OCSS-Spec-Version: 2026-06Versioning
There is no Idempotency-Key header. Unlike Stripe, Phosra ignores an Idempotency-Key request header entirely. Idempotency is keyed off a field in the request body, and a faithful replay is signalled by the OCSS-Replay response header. Sending Idempotency-Key does nothing — see Idempotency.

Response headers

Headers Phosra sets. The rate-limit trio rides every /api/v1/* response; the correlation id rides everything.
HeaderOn which responsesExampleReference
Content-TypeAllapplication/json
X-RateLimit-LimitEvery /api/v1/* response100Rate limits
X-RateLimit-RemainingEvery /api/v1/* response87Rate limits
X-RateLimit-ResetEvery /api/v1/* response. Unix epoch seconds the window resets — not a duration.1783344660Rate limits
Retry-After429 Too Many Requests only. Seconds to wait.60Rate limits
DeprecationAn endpoint/field in a sunset window (RFC 9745).@1788220800Deprecation policy
SunsetAn endpoint with a committed removal date (RFC 8594). HTTP-date.Tue, 01 Dec 2026 00:00:00 GMTDeprecation policy
LinkDeprecated surfaces — points to the migration guide, rel="deprecation" / rel="sunset".<https://…>; rel="deprecation"Deprecation policy
ETagCacheable census reads (e.g. the Trust List) for conditional If-None-Match fetches."32e5fb0d…"Trust Framework
OCSS-ReplayA faithful idempotent replay: original. Its absence means a fresh apply.OCSS-Replay: originalIdempotency
X-Railway-Request-IdEvery response. The correlation id to quote in a support ticket.2HO1Woj4RFGk9Z7VjVra_wThe correlation id

Authentication headers

A developer API key goes in Authorization: Bearer. This is the canonical form and the one every SDK sends:
curl https://phosra-api-sandbox-production.up.railway.app/api/v1/platforms \
  -H "Authorization: Bearer $PHOSRA_API_KEY"
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. Send Content-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:
Signature-Input: ocss=("@method" "@target-uri" "ocss-spec-version" "content-digest");created=1783315514;keyid="did:ocss:loopline#2026-06";alg="ed25519"
Signature: ocss=:<base64-ed25519-sig>:
Content-Digest: sha-256=:<base64-sha256-of-body>:
OCSS-Spec-Version: 2026-06
The signing 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:
curl -sS -X POST \
  https://phosra-api-sandbox-production.up.railway.app/api/v1/enforcement-confirmations \
  -H "Content-Type: application/json" -d '{}'
{
  "error": "Unauthorized",
  "message": "exactly one Signature-Input and one Signature header are required",
  "code": 401,
  "class": "signature_invalid"
}
The SDK’s 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:
curl -sS -D - -o /dev/null \
  https://phosra-api-sandbox-production.up.railway.app/api/v1/platforms \
  | grep -i x-ratelimit
x-ratelimit-limit: 100
x-ratelimit-remaining: 87
x-ratelimit-reset: 1783344660
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:
cache-control: public, max-age=300
etag: "32e5fb0d59af537f8b6bad4a72578a756c5312b089f13114fd70272c28af56a6"

Deprecation signals

Before anything in v1 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: @1788220800
sunset: Tue, 01 Dec 2026 00:00:00 GMT
link: <https://phosra.com/docs/migrations/…>; rel="deprecation"
  • DeprecationRFC 9745, an @-prefixed Unix timestamp of when the deprecation took effect.
  • SunsetRFC 8594, an HTTP-date of the committed removal.
  • Link — a pointer to the human migration guide.
Alert on the presence of a 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 with 200 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:
curl -sS -D - -o /dev/null \
  https://phosra-api-sandbox-production.up.railway.app/health \
  | grep -i x-railway-request-id
x-railway-request-id: 2HO1Woj4RFGk9Z7VjVra_w
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).
Include this id, the endpoint, and the timestamp when you email developers@phosra.com — see Support and Troubleshooting for the full checklist.

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:
curl -sS -D - -o /dev/null \
  https://phosra-api-sandbox-production.up.railway.app/api/v1/platforms
HTTP/2 200
content-type: application/json
x-ratelimit-limit: 100
x-ratelimit-remaining: 87
x-ratelimit-reset: 1783344660
x-railway-request-id: 2HO1Woj4RFGk9Z7VjVra_w

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.