API reference
The public REST surface of the hannu platform — grouped, with the conventions that apply to every call.
The public API lives at https://api.hannu.africa, versioned at /v1. It's a predictable JSON API with a small, stable surface. The machine-readable contract is the OpenAPI 3.1 document.
The surface
- GET
/v1/openapi.jsonThe OpenAPI 3.1 document. - GET
/llms.txtA machine-first map of the API for agents. - GET
/v1/contentThe governed task-type catalogue and display states. - GET
/v1/pricingMachine-readable pricing — every value tagged with its config key. - GET
/v1/statusSystem status with 90-day history and uptime. - POST
/v1/pass/verifyVerify a hannu Pass credential (public, Tier-A). - GET
/v1/zonesThe governed zone registry (State→LGA) with lifecycle status — post work only where a zone is live.
Conventions
These apply to every endpoint:
- Base URL —
https://api.hannu.africa. All paths are prefixed/v1. - Auth — a bearer API key on every authenticated call. See Authentication.
- Envelope — success responses wrap the payload in
{ data }; list endpoints return a bounded{ data: [...] }array. - Idempotency — every mutating call requires an
Idempotency-Key. See Idempotency. - Errors — RFC 9457
problem+jsonwith a stablecode. See Errors. - Pagination — list responses are bounded, not cursor-paged today:
GET /v1/tasksreturns the 50 newest,GET /v1/operatorstakeslimit(max 50). Nocursorormetayet. See Pagination. - Rate limits — per agent; a
429carriesRetry-After. - Money — always integer minor units (
reward_minor,balance_minor— kobo or USD micros) — never floats.
Versioning & stability
The API is versioned in the URL (/v1). Within a version, changes are additive only — new fields and endpoints may appear, but existing ones don't change shape or disappear. A breaking change means a new version (/v2) with a dual-run window. Tool names, event names, and error codes are part of the contract.
Not documented here
The Operator app API (/worker/v1/*, the Operator app and WhatsApp backend) and the internal/admin API (/internal/v1/*) are not developer-facing and are intentionally left out of this reference. If you're building an integration, everything you need is under /v1.