API reference
Every product and protocol in the OrangeCheck family that ships an HTTP API
exposes a machine-readable OpenAPI 3.1 spec at /api/openapi on its own
origin, plus an interactive /api-explorer page (Swagger UI). Session-bound
endpoints use the same auth scheme name (cookieAuth) everywhere.
Currently shipped
| Product | Subdomain | OpenAPI spec | Interactive explorer |
|---|---|---|---|
| Auth host | ochk.io | /api/openapi | /api-explorer |
| OC Attest | attest.ochk.io | /api/openapi | /api-explorer |
| Me | me.ochk.io | /api/openapi | inline at /me |
The narrowed product pages (linked in the sidebar under this group) embed the same spec rendered inline + cross-link to the live explorer.
Errors
Every API signals failure with the HTTP status first. The JSON body is not uniform across the three hosts, so branch on the status code and treat the body as a machine-readable hint. What each one returns today:
attest.ochk.io
| Case | Status | Body |
|---|---|---|
| Query or body fails validation | 400 | { "error": "bad_request", "issues": [ … ] } — issues is optional |
/api/check finds no proof | 404 | { "ok": false, "reasons": ["not_found"] } |
| Check ran, thresholds not met | 200 | { "ok": false, "reasons": [ … ], … } |
| Wrong method | 405 | { "error": "method_not_allowed" } |
| Per-IP limit exceeded | 429 | { "error": "rate_limited" } — no Retry-After header; wait a minute |
| Unexpected failure | 500 | { "error": "server_error" } |
/api/publish-attestation answers { "ok": false, "error": "<code>" }
(missing_envelope, invalid_json, rate_limited, …).
ochk.io (auth host)
| Case | Status | Body |
|---|---|---|
| Most failures | 4xx/5xx | { "ok": false, "reason": "<code>" } |
| No session | 401 | { "ok": false, "reason": "not_authenticated" } |
| Request validation (some) | 400 | { "error": "bad_request", "issues": [ … ] } |
| Per-IP limit exceeded | 429 | { "ok": false, "reason": "rate_limited" } on most endpoints; { "error": "rate_limited" } on /api/challenge and /api/auth/switch |
me.ochk.io
Handlers answer { "ok": false, "reason": "<code>" },
{ "ok": false, "error": "<code>" } or { "error": "<code>" }, depending on
the route; some add context fields (project_key, …). Rate limits answer 429
with { "error": "rate_limit_exceeded", "retry_after_seconds", "bucket" }, or
for per-project event caps { "error": "project_rate_limit_exceeded", … } plus
a Retry-After header.
What about the other subdomains?
pledge.ochk.io,stamp.ochk.io,vote.ochk.io,agent.ochk.io,lock.ochk.io— protocol resolver sites; the load-bearing crypto + canonicalization is in the SDKs, not in HTTP APIs. The/api/auth/meendpoint each of them exposes is a delegated session check that goes through the auth host (documented at /api-reference/auth-host).
Generating clients
Each spec is canonical OpenAPI 3.1 — works with every standard generator. Example: a Python client for the Me API:
openapi-generator-cli generate \
-i https://me.ochk.io/api/openapi \
-g python \
-o ./me-client
Same recipe with -g go, -g rust, -g typescript-fetch, etc. The specs are
public + CORS-permissive so you can hit them from anywhere; cached 5 minutes
server-side so client-generation tools that hammer them don't poison the
response.
Trust model
These OpenAPI specs are documentation, not protocol artifacts. The canonical
contracts are still the TypeScript handler source + @orangecheck/* SDK
conformance vectors. If a spec ever drifts from the implementation, the
implementation wins — file an issue on oc-me-web / oc-www /
oc-attest-web and the spec gets corrected on the next deploy.