API reference
Two surfaces: a public verification plane that anyone may call, and an administrative plane for Authority systems, authenticated with capability-scoped credentials.
Authentication
Public endpoints need no credential. Administrative endpoints take a bearer token issued in the console under API credentials:
Authorization: Bearer ak_1a2b3c4d5e6f7890.<secret>
Each credential carries an explicit capability list. A call outside that list returns
403 naming the capability it needed, so a read-only reporting integration
cannot be repurposed into a revocation tool. Rotate a credential by issuing a new one and
revoking the old; both actions are written to the audit ledger.
Public verification plane
GET /v1/load.js
The loader script. Short cache lifetime so the Authority can roll out changes.
Pin a version with /v1/load.js if your change-management policy requires it.
POST /v1/seal/render
Issues a fresh seal asset and verification stamp. Validates the request Origin against the declared domain server-side.
curl -X POST https://seal.attestregistry.com/v1/seal/render \
-H 'content-type: application/json' \
-H 'origin: https://operator.example' \
-d '{"seal_id":"<64 hex>","domain":"operator.example"}'
{
"state": "VALID",
"seal_id": "…",
"stamp": "<32 hex>",
"domain": "operator.example",
"licence_number": "VW-2026-00001",
"verification_url": "https://attestregistry.com/validate?…",
"svg": "<svg …>",
"signature": "<base64 Ed25519>"
}
GET /v1/license/status
Structured licence status, signed so you can verify it offline instead of re-querying.
Parameters: seal_id, domain, optional stamp.
curl 'https://attestregistry.com/v1/license/status?seal_id=<64 hex>&domain=operator.example'
Returns {"licence": {…}, "signature": "…", "algorithm": "Ed25519"}.
Verify the signature over the canonical JSON form of the licence object
(sorted keys, no whitespace, UTF-8) against the key at
/.well-known/attest-signing-key.
GET /validate
The human-readable verification page. Renders one of five public states: VALID, EXPIRED, SUSPENDED, REVOKED, INVALID.
POST /v1/telemetry
Analytics collector. Accepts a single event or {"events": [...]}.
Event types accepted from the client: impression, hover,
click, validation_interaction.
GET /evidence/{id}
Look up an evidence identifier printed on a verification record and confirm that exact check took place at that exact moment. Response is signed.
Administrative plane
Base path /admin/v1, served only on admin.attestregistry.com.
| Endpoint | Capability | Purpose |
|---|---|---|
| GET /licences | licence:read | List with filters: status, domain, licensee, expiring_within_days. |
| POST /licences | licence:issue | Issue a licence. Returns the record, the embed snippet, and the certificate URL. |
| POST /licences/bulk | licence:issue | Batch issuance. Rows commit independently and are reported per row. |
| GET /licences/{id} | licence:read | Full record, snippets, attestations, ledger history. |
| PATCH /licences/{id} | licence:modify_minor / modify_material | Change detail. Set material: true for changes the public should see. |
| POST /licences/{id}/activate | licence:issue | PENDING → VALID. |
| POST /licences/{id}/suspend | licence:suspend | Needs reason_category. Takes effect on the next status check. |
| POST /licences/{id}/reinstate | licence:reinstate | SUSPENDED → VALID. |
| POST /licences/{id}/revoke | licence:revoke | Permanent. Needs reason_category and a written justification. |
| POST /licences/{id}/renew | licence:renew | Needs new_valid_until. |
| POST /licences/{id}/close | licence:close | Retire the record, optionally naming a successor. |
| POST /licences/{id}/verify-embed | licence:read | Run the integration verifier. A clean pass activates a PENDING licence. |
| GET /audit | audit:read | Query the ledger; full-text search on search. |
| GET /audit/verify | audit:read | Recompute the hash chain and report any divergence. |
| GET /audit/export | audit:read | Signed, time-stamped export for regulatory proceedings. |
| GET /analytics/realtime | analytics:read | Minute-level rollups across the estate. |
| GET /analytics/licence/{id} | analytics:read | Per-licence detail over a window. |
| POST /analytics/query | analytics:read | Ad-hoc aggregation over a fixed dimension set. |
| GET /analytics/ageing | analytics:read | Expiring, overdue audit, elevated fraud, display non-compliance. |
| GET /enforcement/queue | enforcement:read | Open cases by severity. |
| POST /enforcement/cases/{id}/action | enforcement:action | Progress a case through triage, investigate, action, closed. |
| GET /surveillance/feed | surveillance:read | Probe and look-alike findings. |
| GET /reference | licence:read | Every vocabulary this API accepts. Call this first. |
Attestations
Record a compliance attestation against a licence. Published attestations appear on the public verification page, so every write is audit-ledgered.
curl -X POST https://attestregistry.com/admin/v1/licences/<licence-id>/attestations \
-H "Authorization: Bearer $ATTEST_TOKEN" \
-H 'content-type: application/json' \
-d '{
"label": "Annual AML audit completed",
"body": "Independent audit by Example LLP, no material findings.",
"published": true
}'
GET /admin/v1/licences/{id}/attestations
lists them; DELETE
/admin/v1/licences/{id}/attestations/{attestation-id}
withdraws one. Both need attestation:write.
Webhooks
Register an endpoint to be told about changes as they happen rather than polling.
curl -X POST https://attestregistry.com/admin/v1/webhooks \
-H "Authorization: Bearer $ATTEST_TOKEN" \
-H 'content-type: application/json' \
-d '{"url":"https://authority.example/hooks/attest",
"event_classes":["licence_state","fraud","surveillance"]}'
The response carries the signing secret once. Every delivery includes
X-Attest-Signature, the SHA-256 HMAC of the raw request body under that
secret. Compare it in constant time before trusting the payload.
Errors and rate limits
| Status | Meaning |
|---|---|
| 400 | The request is malformed, or a required field is missing. The body names it. |
| 401 | No credential, or the credential is unknown, revoked, or expired. |
| 403 | The credential or role lacks the capability. The body names the capability. |
| 404 | No such record — or the administrative plane was called on a public hostname. |
| 409 | The action conflicts with the current state, such as a transition the lifecycle does not permit. |
| 429 | Rate limited at the edge. Back off and retry. |
The public verification plane is rate limited per source address at the edge. If you operate a legitimate compliance-monitoring service, ask the Authority to add your source to the allow list rather than working around the limit — allow-listed sources stop generating fraud signals as a side effect.