API reference
One base URL, bearer keys, JSON problem details, and a surface the build itself publishes. If you are starting from zero, the quickstart is three commands to a verdict, and the sample is one real analysis shown end to end — certificate included, verifiable. This page is the map around both.
Base URL and authentication
curl https://api.hiddencontent.ai/v1/usage \ -H "authorization: Bearer $HCS_KEY" # Keys are bearer credentials, shown once, stored only as hashes.# Every key is live; analyses spend prepaid credit at a cent a page.Everything lives under https://api.hiddencontent.ai/v1 (the JWKS is the one exception). Keys are shown exactly once and stored as hashes — a lost key is replaced from any key you still hold, never recovered. Revocation cascades: revoking a key also revokes every key it minted.
Endpoints
| Method | Path | Auth | |
|---|---|---|---|
GET | / | no | what this deployment offers, including its sign-up routes |
GET | /v1/healthz | no | liveness, engine and rule-pack versions, and whether the vision pass is enabled |
GET | /v1/output-surface | no | the recorded output surface this build ships — every technique, limit, and field, generated from the build. Build your fixtures from this |
POST | /v1/signup | no | email sign-up: {"email", "name", "source"?} → account + live key. A known address is a 409 and no key |
POST | /v1/signup/github | no | begin GitHub device-flow sign-up; returns a code and a pollToken |
POST | /v1/signup/github/poll | no | poll it; 202 until authorised, then the account and key. Signing in again later recovers the same account |
POST | /v1/contact | no | the talk-to-a-human form: {"email", "message"?} — answered by a person |
POST | /v1/analyze | yes | the one that matters. Multipart file upload → verdict, findings, canonical text, certificate. Large scanned documents return 202 + a job |
GET | /v1/analyze/{jobId} | yes | poll a 202 |
GET | /v1/documents/{sha256} | yes | stored reports for a document hash |
GET | /v1/usage | yes | balance, usage this period, and account state — alarm on analysisUnitsRemaining before a 402 does it for you |
POST | /v1/keys | yes | mint another live key on your own account — one per environment, separately revocable |
GET | /v1/keys | yes | your keys, metadata only — never a token |
POST | /v1/keys/{keyId}/revoke | yes | revoke it and everything it created, transitively |
POST | /v1/text/sanitize | yes | text you already hold, no file |
POST | /v1/billing/checkout | yes | a Stripe Checkout URL for a credit purchase ($10 minimum, $5,000 maximum) |
POST/GET/DELETE | /v1/billing/auto-refill | yes | opt-in below-X-charge-Y; consent required verbatim; one call disables |
GET | /v1/account/export | yes | everything we hold on your account, as JSON |
DELETE | /v1/account | yes | delete the account; needs {"confirm": "<customer id>"} |
POST | /v1/certificates/verify | no | unauthenticated by design, so an auditor can check a certificate. Gate on attests, not valid |
GET | /.well-known/jwks.json | no | public keys, for verifying certificates offline — with our servers switched off entirely |
The health path is /v1/healthz, not /healthz — the first thing every new integration gets wrong.
Errors
{ "type": "/v1/errors/quota-exhausted", // open it — every type URL is a docs page "title": "Quota exhausted", "status": 402, "detail": "Analysis balance exhausted: 12 of 12 used. Buy credit with POST /v1/billing/checkout — one cent is one page. …", "requestId": "req_…" // quote this when you write to us}Every error is an RFC 9457 problem document, and every type URL is a page this service serves — open it in a browser for the full story and the fix. The two worth knowing in advance: a 402 names the exact shortfall before any work is billed (a document you cannot afford is refused whole, never partially analysed and charged), and a 429 carries Retry-After — honour it.
Certificates
A certified analysis returns a signed certificate: this document (by hash), this verdict, this engine version, at this time. It verifies offline against/.well-known/jwks.json with our servers switched off entirely — months or years later. Two rules keep you honest when you build on them: gate onattests, not valid (a correctly signed certificate can still attest nothing), and a certificate attests to process, not safety — it records that an analysis ran and what it found, never that a document is clean.certified: false always names its reason.
The surface that cannot drift
GET /v1/output-surfaceEvery technique the running build detects, every limit it enforces, and every field it emits — generated from the build, so it cannot disagree with what you receive. Build your fixtures and your routing against it, and diff it across deploys: theengineVersion inside is the same one pinned into every certificate. Known blind spots are declared per finding (residuals) rather than discovered by your users; we treat narrowing one as a change worth announcing.
The full integration guide
Behind this reference is a much longer integration guide — verified request-by-request against the running service, with a changelog that records every behaviour change, which direction your numbers move, and how far. It comes with every evaluation:ask us.