Skip to content

Request Conventions

Public-API URLs are flat — no tenant or workspace IDs in the path:

https://api.getzutax.com/api/public/v1/{resource}

Examples:

POST /api/public/v1/parties
POST /api/public/v1/invoices/transmit
GET /api/public/v1/invoices/by-external-id/{external_id}

The workspace and tenant scope are derived from the API key — you never need to look them up or pass them in.

| Header | Required for | Notes | |---|---|---| | Authorization: Bearer <api_key> | every call | See Authentication | | Content-Type: application/json | POST / PATCH | Required for JSON bodies | | Idempotency-Key: <uuid-or-stable-string> | every write (POST, PATCH) | See “Idempotency” below |

Every write supports a client-supplied external_id per resource. Use it to:

  • De-duplicate retries — a re-POST with the same external_id returns the existing record without creating a duplicate
  • Look up the resource later without storing the ZUTAX UUID: GET /api/public/v1/{resource}/by-external-id/{external_id}

external_id is unique per workspace per resource type; it must not be reused.

Every write endpoint requires an Idempotency-Key header. Recommended value: a stable derivation of your source-system ID, e.g.:

sha256(your_workspace_id || resource_type || your_record_id || your_record_version)

Behavior within a 24-hour window:

  • Same key + same body → server returns the original cached response
  • Same key + different body → server returns 409 Conflict
  • New key → server processes the request and caches the response

Keys persist for 24 hours and are workspace-scoped.

Where supported, list endpoints accept:

  • page (default 1)
  • page_size (default 20, max 100)

Typical paginated response:

{
"items": [],
"total": 0,
"page": 1,
"page_size": 20,
"pages": 0
}

Where supported:

  • sort_by
  • sort_order (asc or desc)
  • resource-specific filters (status, search, external_id, etc.)

Refer to the Endpoint Reference for the exact query parameters each endpoint accepts.

All monetary values in public-API request and response bodies are in naira (Decimal). The backend stores billing amounts in kobo internally, but no kobo value ever crosses the API boundary.

Field names ending in _naira (e.g., unit_price_naira, total_naira) are explicit reminders. Locale is always en-NG.

Most write endpoints accept ?async=true, which returns:

{ "job_id": "j_...", "status": "queued", "result_url": "/api/public/v1/jobs/j_..." }

with HTTP 202. Poll GET /api/public/v1/jobs/{job_id} or wait for the configured webhook callback.