API docs
Basics
Everything the reference assumes, in one place. Read it once and every endpoint works the same way.
Authentication
Every request sends two headers: your API key, and the business it acts for.
Authorization: Bearer zulu_sk_...
Zulu-Business-Id: business_...
- Create keys in Zulu under Settings → API keys. A key is shown once, when you create it, so store it somewhere safe, like a password manager or your platform's secret store. The business ID is shown there too.
- A key acts with the role you give it. Give each key the smallest role that does the job. Keys can never manage the business, its team, roles or other keys.
- Keys need the Scale plan. On other plans, existing keys pause and requests return
402until the business is on Scale again. - If a key leaks, revoke it in Settings. It stops working immediately.
Requests
| Base URL | https://api.zulu.ie |
| Format | JSON in and out, except PDF and logo downloads |
| IDs | A prefix and a UUID, like invoice_9f8e7d6c-…. The prefix tells you what it is. |
Each kind of record has the same four methods on one path, for example /v1/catalog:
| Method | Send | You get |
|---|---|---|
GET |
Optional query: id, query, limit, after, before |
200 and a list |
POST |
The new record's fields | 201 and the saved record |
PATCH |
id and only the fields to change |
200 and the saved record |
DELETE |
id |
204, no body |
PATCH changes only the fields you send: leave a field out to keep it, or send null to clear it. Lists, like an invoice's items, are replaced whole.
Values
- Money is a string with up to 2 decimal places:
"120.50". This keeps amounts exact. Numbers are accepted, but responses always use strings. - VAT rates are strings between 0 and 1:
"0.135"is 13.5%. - Dates are
YYYY-MM-DD. Timestamps are ISO 8601 in UTC:2026-10-27T09:30:00.000Z. - Currencies are three-letter codes. The default is
EUR. - Unknown fields are rejected, so typos never pass silently.
Lists and paging
Lists return the newest first, a page at a time:
{ "data": [ ... ], "has_more": true }
| Query | Meaning |
|---|---|
limit |
How many to return: 1–50, default 20 |
after |
The next page: pass the last id you received |
before |
The previous page: pass the first id you received |
query |
Search the records' main text, ignoring case |
id |
Just that one record, in data |
Keep requesting with after while has_more is true.
Errors
Every error has the same shape. Show message to people and switch on code in your code.
{ "error": { "code": "not_found", "message": "VAT rate not found" } }
| Status | code |
What to do |
|---|---|---|
400 |
validation_error, bad_request |
Fix the request; the message says what's wrong |
400 |
export_too_large |
A PDF export covers more than 200 invoices; ask for a shorter date range |
401 |
unauthenticated |
Check the key. It may be revoked or expired. |
402 |
plan_limit |
The plan doesn't allow it, for example API keys below Scale |
403 |
forbidden |
The key's role is missing a permission |
404 |
not_found |
It doesn't exist, or belongs to another business |
409 |
conflict |
It breaks a rule, like issuing an invoice with no customer address. The message lists what's missing. |
429 |
rate_limited |
Too many requests or emails; wait and try again |
503 |
email_unavailable |
Email couldn't be sent; try again in a few minutes |
Limits
- Email: each invoice can be sent 5 times a day. On Scale, a business can email 1,000 recipients a day and 20,000 a month (fair use, raised on request).
GET /v1/business/planshows what's used. - PDFs: up to 30 a minute.
- Lists: up to 50 records a request.
When you reach a limit you get 429. Wait a little, then try again.