First API Integration
By the end of this guide you’ll have:
- Created a workspace API key in the dashboard
- Verified the key with
_introspect - Created a customer and an invoice via the composite “issue and transmit” endpoint
Throughout, replace zk_live_... with your own key, and use https://api.getzutax.com for production or your staging URL during development.
Step 1 — Create an API Key
Section titled “Step 1 — Create an API Key”API keys are created in the ZUTAX dashboard under Workspace Settings → API Keys. Each key is shown once at creation; store it in your secret manager.
Recommended scopes for a typical ERP integration:
parties:read,parties:writeproducts:read,products:writeinvoices:read,invoices:write,invoices:transmit
Step 2 — Verify the Key
Section titled “Step 2 — Verify the Key”curl -sS https://api.getzutax.com/api/public/v1/_introspect \ -H "Authorization: Bearer zk_live_<prefix>_<secret>"Expected response:
{ "workspace_id": "8a1c7f6c-61a1-4d89-8d96-3f7e7747f0c0", "tenant_id": "...", "api_key_id": "...", "scopes": ["invoices:read", "invoices:write", "invoices:transmit", "parties:read", "parties:write"]}If you get 401, double-check the header (Authorization: Bearer ..., not X-API-Key). If scopes is missing what you need, regenerate the key with the right scopes.
Step 3 — Issue and Transmit an Invoice
Section titled “Step 3 — Issue and Transmit an Invoice”This single call upserts the customer party (using the external_id from your ERP), creates the invoice, signs the IRN, and submits it to the Access Point Provider.
curl -sS -X POST https://api.getzutax.com/api/public/v1/invoices/transmit \ -H "Authorization: Bearer zk_live_<prefix>_<secret>" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: $(uuidgen)" \ -d '{ "external_id": "ERP-INV-2026-00451", "currency": "NGN", "issue_date": "2026-05-02", "due_date": "2026-06-01", "customer": { "external_id": "ERP-CUST-9", "legal_name": "Acme Ltd", "tin": "12345678-1234" }, "lines": [ { "name": "Widget", "quantity": 10, "unit_price_naira": 1500, "vat_rate": 0.075 } ], "options": { "submit_to_pasca": true, "fail_fast": true } }'Successful response (abridged):
{ "invoice": { "id": "inv_...", "external_id": "ERP-INV-2026-00451", "status": "transmitted", "irn": "INV-2026-05-02-...", "pasca_dispatch_id": "dsp_..." }, "customer": { "id": "party_...", "external_id": "ERP-CUST-9", "created": true }, "steps_completed": ["party_upsert", "invoice_create", "validate", "irn_sign", "pasca_submit"], "elapsed_ms": 2340}Step 4 — Retry-Safe Re-runs
Section titled “Step 4 — Retry-Safe Re-runs”Re-issuing the same call with the same Idempotency-Key and the same body returns the cached response — no duplicate invoice, no duplicate party. Use this pattern in your integration’s retry loop.
If your transport fails between sending the request and receiving the response, retry with the same key. The server either processes it (first attempt) or returns the cached response (subsequent attempts).
Next Steps
Section titled “Next Steps”- Webhooks for receiving status updates
- Error Handling and Limits for production-readiness
- Endpoint Reference for the full surface