Conventions

What is true of every endpoint, so the module pages do not repeat it.

Base URLs

EnvironmentBase URL
Productionhttps://api.goterrago.com
Staginghttps://terra-stg.goterrago.com

Paths on this site are absolute and include their version segment — /v3/….

Authentication

Every endpoint marked bearer expects a short-lived access JWT:

Authorization: Bearer <access_token>

Tokens are minted by POST /v3/auth/login and refreshed by POST /v3/auth/refresh. They are audience-bound: a token minted for one surface is refused on another, and each endpoint below lists the audiences it admits.

Audiences

The audience says which client a token was issued to. It is not a role — it is the app.

AudienceClient
driver_appTerraGo driver handset
beneficiary_appWaaS operator / beneficiary app
parent_appParent app
pass_appTerraPass venue handset
pass_webTerraPass operations console
pass_fanTerraPass fan portal (ticket holders)
admin_webBack-office dashboards

Capabilities

Where an endpoint shows capability, the token's holder must also carry that grant. Capabilities are resolved per user from their tenant role, or from the bundle their staff_kind implies, and they are enforced on the route — a client that hides a button is a courtesy, not a control. A caller without the grant gets 403 missing_capability.

Device fingerprint

Endpoints marked device require the caller to identify the physical unit:

X-Device-Fingerprint: <stable client-minted id, 16+ chars>

It must match the device the token was issued to — otherwise 401 device_mismatch, and a device an operator has blocked gets 403 device_blocked immediately.

Response envelopes

Every success is wrapped. A single record:

{ "data": { … } }

A collection, with its cursor:

{ "data": [ … ], "meta": { "next_cursor": "…", "has_more": true } }

Pass ?cursor= to continue. Cursors are opaque — do not parse them, and do not assume a page size.

Idempotency

Endpoints marked idempotency require a key:

Idempotency-Key: <uuidv7>

Mint it once, at the moment the action is taken, and send the same key on every retry. This is what makes a payment taken on a handset with no signal exactly one payment however many times the queue drains it.

Money and time

Money is always { "amount_minor": 12000, "currency": "KES" } — an integer in the currency's minor unit, never a float. Timestamps are ISO-8601 UTC with a Z. Identifiers are UUIDv7 strings.

Rate limits

Each endpoint shows the named limiter it runs under. Exceeding it answers 429 with a Retry-After header. The named limiters are shared — credential is per authenticated caller, login is per address and IP, and tap is sized for a door moving a person a second.

Compressed uploads

Batch endpoints accept Content-Encoding: gzip. The compressed body is capped at 1 MB and the decompressed body at 10 MB.