Skip to main content
This is the signed OCSS data-plane — the wire the @phosra/gatekeeper SDK drives when a platform enforces child-safety rules at the edge. These are not plain REST: profile reads are RFC 9421-signed by the resolver, binding writes require an accredited caller signature, and profile responses are root-verifiable signed documents.
Building a parental-controls product (families, children, policies)? That surface is the Phosra product API — a different plane. This data-plane is for enforcement partners running a gatekeeper. Start with the Platform quickstart if you are new.

The nine operations

Bind an endpoint

POST /enforcement-endpoints — mint a §9.3(b) bound-resolver label for a child.

Rotate a label

POST /enforcement-endpoints/{id}/rotate — issue a fresh label, invalidate the old.

Fetch the signed profile

GET /enforcement-profiles/{endpoint_id} — poll the router-signed enforcement profile.

Confirm enforcement

POST /enforcement-confirmations — submit a signed §8.3.8 result receipt.

Sandbox: run the connect ceremony

POST /sandbox/test-connect — the provider side of connect, to your own DID.

Sandbox: mint test consent

POST /sandbox/consent-attestations — satisfy the consent-first gate with no roster edit.

Sandbox OAuth: authorize

GET /oauth/authorize — reference parent-consent authorize leg.

Sandbox OAuth: token

POST /oauth/token — exchange an authorization code for an access token.

Sandbox OAuth: profiles

GET /oauth/profiles — bearer-scoped seeded child list.

Base URL

The /oauth/* reference legs live on the census host root, not under /api/v1 — use https://phosra-api-sandbox-production.up.railway.app/oauth/authorize.
Every example on these pages was executed against the hosted sandbox above and the real responses are pasted in verbatim. The sandbox reference OAuth and sandbox/* legs are gated on PHOSRA_ENV==sandbox — they return 404 on production, by design.

How a signed request is built

Every write and every profile read on this plane carries an RFC 9421 Ed25519 signature under your Trust-List-published key. The census reconstructs the same signature base and verifies provenance + accreditation before processing. Copy-paste signers in curl+openssl, TypeScript, Python, and Go — each verified end-to-end against this sandbox — live in the request-signing guide; the TypeScript signRequest from @openchildsafety/ocss is shown below.
The Phosra signing-requests guide showing four language tabs: Python, Go, TypeScript, and curl+openssl

The request-signing guide — copy-paste Ed25519 signers in Python, Go, TypeScript, and curl+openssl, each verified against the hosted sandbox.

signer.ts
signRequest covers @method, @target-uri, ocss-spec-version, and — when a body is present — content-digest, in that literal order. It emits:
A signature is bound to the method, URL, body, and created timestamp — it cannot be copy-pasted into a static curl and reused (it will fail signature verification or expire). Compute headers per-request with a signer. The runnable curl+openssl / TypeScript / Python / Go recipes each mint fresh headers from a monotonic clock; the shell wire-shape examples on the reference pages are for illustration only.

The end-to-end loop

A gatekeeper binds once, then polls and confirms on a rotation cadence:
1

Bind

POST /enforcement-endpoints returns your endpoint_id_label (the credential), binding_id (for rotation), and connect_secret (for inbound connect verification) — all three exactly once.
2

Poll

GET /enforcement-profiles/{endpoint_id_label} returns a router-signed profile. Verify the signature to the router key, then to the root, before trusting categories[].
3

Enforce + confirm

Apply the decision locally, then POST /enforcement-confirmations with a signed §8.3.8 receipt (or call verdict.confirm("applied") in the SDK).
4

Rotate

Periodically POST /enforcement-endpoints/{binding_id}/rotate to get a fresh label; the prior label 404s immediately.