Issuance and auto-send
Issuing an invoice is the act of freezing it, booking it into Dinero, and (optionally) sending it to the customer. Tabba supports two patterns.
Explicit issuance
Drive it from your integration when your workflow says "this project is done":
# One-time invoice.
curl -X POST https://www.tabba.io/api/v1/invoices/ext:proj_ACME_migration/issue \
-H "Authorization: Bearer $TABBA_API_KEY"
# A series occurrence.
curl -X POST https://www.tabba.io/api/v1/recurring-series/ext:acme-retainer/occurrences/2026-07-01/issue \
-H "Authorization: Bearer $TABBA_API_KEY"
Issuance is synchronous and idempotent — a timeout-then-retry returns the already-issued result, never double-books. Under the hood Tabba's Dinero GUID guard is the same one the UI's issue button rides.
auto_send (cron-driven)
Every invoice carries an auto_send boolean. When it's on, the Tabba
cron issues the invoice on its issue date without your involvement —
the exact same behaviour as the UI. Leave it on for hands-off retainers
and rely on the invoice.issued webhook to learn about the booking:
// Your webhook receiver.
app.post('/webhooks/tabba', (req, res) => {
const event = req.body
if (event.type === 'invoice.issued') {
await syncInvoiceToPmTool(event.data)
}
res.status(200).end()
})
Toggle it via the invoice PATCH:
curl -X PATCH https://www.tabba.io/api/v1/invoices/ext:proj_ACME_migration \
-H "Authorization: Bearer $TABBA_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "auto_send": false }'
Booked-but-send-failed (200 + send_error)
Issuance in Tabba is two steps: book the invoice into Dinero, then
send it to the customer (email or EAN). Booking is what makes the
invoice real. If booking succeeds but the send fails (e.g. Dinero's
email service returns 5xx), you get a 200 with a send_error:
{
"object": "invoice",
"id": "…",
"status": "issued",
"dinero_invoice_guid": "…",
"send_error": {
"code": "SEND_FAILED",
"message": "Dinero email service unavailable."
}
}
Do not retry issue — the invoice is already booked. Retry the send
instead (POST /invoices/{ref}/resend; slice-forthcoming). This
prevents the classic "retry the failure and double-book" trap.
Editing after issue
Once an invoice has a dinero_invoice_guid, it is frozen. Attempting to
edit it returns 409 INVOICE_ALREADY_ISSUED. Raise a credit note
instead — full or partial — via POST /credit-notes.
Bulk issuance
Bulk issuance stays cron-driven: auto_send=true on a set of
invoices lets Tabba issue them together on the issue date. The API is
single-item only. If you need to issue N invoices, iterate — each call
is idempotent so re-runs are safe.