Invoice webhook
Optional. Take invoices into your own system instead of from a mailbox.
Give us a URL and an invoice is POSTed there when we send it to you, and a withdrawn one the same way. The PDF still goes by mail, and an invoice your endpoint did not take is still issued. Without a URL you get the mail and nothing else.
The request is signed exactly like a wallet callback: the same X-Agg-* headers, your current key, HMAC-SHA256 over timestamp + "." + raw_body. See Authentication.
| event | When |
|---|---|
| invoice.issued | An issued invoice is sent to you; the body is the document as it stands |
| invoice.voided | An invoice you were sent has been withdrawn; status is void and void_reason says why |
Payload
POST {your url}
{
"event": "invoice.issued",
"sent_at": "2026-09-21T18:52:47Z",
"invoice": {
"id": "5c1e…", "number": "INV-2026-09-0002", "status": "issued",
"period_from": "2026-09-01T00:00:00Z", "period_to": "2026-10-01T00:00:00Z", // period_to is exclusive
"issued_at": "2026-09-21T18:52:47Z", "due_at": "2026-10-05T18:52:47Z",
"revenue_base": "ggr",
"seller": { "name": "…", "registration_number": "…", "address": "…", "country": "…" },
"buyer": { "name": "…", "registration_number": "…", "address": "…", "country": "…" },
"lines": [{
"currency": "USD",
"revenue": 1250.00, "bonus": 0, "gross": 1250.00,
"rate_percent": 11, "amount": 137.50, "adjustment_amount": 0,
"tax_percent": 15, "tax_amount": 20.63, "total": 158.13,
"brands": [
{ "provider_code": "jili", "section_code": null, "ggr": 900.00, "bonus": 0, "revenue": 900.00, "gross": 900.00,
"rate_percent": 11, "amount": 99.00,
"band": { "ordinal": 1, "from_amount": 0, "to_amount": null, "basis_amount": 1250.00, "currency": "USD" } }
]
}],
"adjustments": [], // { "currency", "amount", "reason" }
"void_reason": null
},
"document": { "file_name": "INV-2026-09-0002.pdf", "content_type": "application/pdf", "content_base64": "JVBERi0…" }
}Invoice figures are JSON numbers
Every figure in the invoice (revenue, bonus, gross, amounts, tax, total, band amounts) is a JSON number in the line's currency. Parse with a decimal type. See Money.Delivery
- Answer 2xx to say you have it. The body is not read. Any other 4xx is a refusal and the document is not posted again on its own; ask us to resend it.
- 5xx, 429 and silence are retried: three attempts, 2 and 10 seconds apart, 15 seconds each. After that the delivery is journalled as failed and we resend by hand.
- Idempotent by
invoice.numberandevent: a resend carries the same document. - One line per currency, providers underneath. There is no total across currencies: we hold no exchange rates.
- document is the PDF the mail carries, base64 inline. Expect a body of a few hundred kilobytes.
