Tabba.
IntegrationerSådan virker TabbaFeatures

© 2026 Tabba. Alle rettigheder forbeholdes.

Udviklere
Udviklere
Udviklere
Getting startedQuickstartAuthenticationAccounting platformsexternal_id and idempotencyWork registrationIssuance and auto-sendWebhooksPaginationErrors

Andre spor

AI clientsAPI reference

Issuance and auto-send

Issuing an invoice yourself or leaving it to the daily run, a booked invoice that was not delivered, and credit notes.

A draft invoice is Tabba's own record. Issuing it makes the accounting system book the invoice and send it to the customer. From then on the invoice is frozen. There are two ways to issue, and both behave the same on either accounting system: see Accounting platforms.

Issue it yourself

Call the issue endpoint when your own workflow says the invoice is ready:

# A one-time invoice.
curl -X POST https://www.tabba.io/api/v1/invoices/ext:crm:acme-migration/issue \
  -H "Authorization: Bearer $TABBA_API_KEY"

# An occurrence of a recurring series, named by the first day of its period.
curl -X POST https://www.tabba.io/api/v1/recurring-series/ext:crm:acme-retainer/occurrences/2026-07-01/issue \
  -H "Authorization: Bearer $TABBA_API_KEY"

The two endpoints are POST /invoices/{ref}/issue and POST /recurring-series/{ref}/occurrences/{period}/issue. Neither takes a body.

The accounting system books and sends the invoice inside the request, and the answer is the issued invoice: status is issued, and issued_at, platform_ref and platform_number are set. It is the explicit "bill this one now": an invoice with auto_send off is issued all the same, and one with no issue_date, or a date ahead, is dated today.

Sending it again is safe. An invoice that is issued already is answered as it stands, 200, and nothing is booked twice. While another attempt holds the invoice, the answer is 409 with ISSUE_IN_PROGRESS and a Retry-After header: send the same request again after the interval.

An occurrence that is not an invoice yet is made one, and that invoice is issued. {period} is the first day of the occurrence's billing period, as GET /recurring-series/{ref}/occurrences states it.

Both endpoints are counted against the platform_write rate limit as well as the default one: see Errors.

Leave it to the daily run

Every invoice carries an auto_send boolean. While it is on, the daily run issues the invoice without your involvement:

  • An invoice with an issue_date is issued on that date.
  • An invoice with no issue_date is issued once all the work on it is complete.

auto_send is on for a new invoice unless the create says otherwise. Subscribe to the invoice.issued event to learn of the booking:

// Your webhook receiver, after the signature is verified.
app.post('/webhooks/tabba', async (req, res) => {
  const event = req.body
  if (event.type === 'invoice.issued') {
    await syncInvoiceToYourSystem(event.data)
  }
  res.status(200).end()
})

To turn it off on a draft, replace the draft with PUT /invoices/{ref} and auto_send: false. The body of a PUT is the whole draft: a line that states its id stays the line it was, and a line the body leaves out is removed. Left out, auto_send stays as it is.

Booked, and not delivered

Issuing is two things: the accounting system books the invoice, and it sends it to the customer. Booking is what puts the invoice in the books. When the booking succeeds and the delivery is not known to have succeeded, the answer is still 200, with a send_error:

{
  "object": "invoice",
  "id": "…",
  "status": "issued",
  "issued_at": "2026-07-01T08:00:00.000Z",
  "platform_ref": "…",
  "platform_number": 1042,
  "send_error": {
    "reason": "…",
    "platform_reason": "…"
  }
}

reason is a code of Tabba's own, and platform_reason is the accounting system's own words, where it gave any. On an invoice whose delivery succeeded, send_error is null.

Do not issue it again. The invoice is in the books, and a second call answers the same invoice. The invoice.send_failed event is sent beside invoice.issued for such an invoice. Following up on the delivery is done in the workspace: the API has no endpoint that sends a booked invoice again.

When an invoice is not issued

Anything short of issued is an error with its own code, never a 200:

CodeStatusWhat happened
HOURLY_INCOMPLETE409A person on an hourly line is below full completion
NOTHING_TO_BILL409The invoice has no hours to bill
CUSTOMER_GONE, PRODUCT_GONE409The accounting system no longer holds what the invoice names
ISSUE_IN_PROGRESS409Another attempt holds the invoice. Retry after Retry-After
MISSING_LINES422The invoice has no lines
INVALID_SEND_METHOD, MISSING_EINVOICE_DETAILS422The customer cannot be sent to. Checked before anything is booked
ISSUE_REFUSED422The accounting system refused to book. refusal says why
PLATFORM_API_ERROR502The accounting system could not be reached, or did not answer

Issuing an occurrence adds three: OCCURRENCE_SKIPPED (409) for an occurrence an admin skipped, OCCURRENCE_HAS_NO_LINES (422) where the series states no line for the occurrence, and OCCURRENCE_OUT_OF_RANGE (404) for a date that starts no period of the series.

Changing an issued invoice

An issued invoice is frozen. PUT /invoices/{ref} and DELETE /invoices/{ref} on one are refused with INVOICE_ALREADY_ISSUED (409). To take it back, in full or in part, raise a credit note:

curl -X POST https://www.tabba.io/api/v1/credit-notes \
  -H "Authorization: Bearer $TABBA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "external_id": "crm:acme-migration:credit-1",
    "invoice": "ext:crm:acme-migration",
    "kind": "partial",
    "credit_date": "2026-07-15",
    "lines": [
      { "invoice_line_id": "5f8e2c1a-3b4d-4e6f-8a9b-0c1d2e3f4a5b", "amount": 2500 }
    ]
  }'
  • kind: "full" takes the whole invoice back and reads no lines. It is refused with INVOICE_ALREADY_CREDITED once anything was credited.
  • kind: "partial" names lines of the invoice, each by no more than is left on it. Amounts are before VAT. Too much is a VALIDATION_FAILED with AMOUNT_EXCEEDS_BILLED under the line.

The accounting system books and sends the credit note inside the request, and the answer is the booked credit note, 201. As with an invoice, a credit note that was booked and not delivered is a success that carries a send_error.

The external_id is the credit note. Sending the request again under the same external_id books nothing twice: a booked credit note is answered as it stands, 200, and an attempt that did not book is taken up again. An invoice that is not issued is refused with INVOICE_NOT_ISSUED. While another credit of the invoice is running the answer is CREDIT_IN_PROGRESS with Retry-After, and an attempt the accounting system gave no definite answer to holds every other credit of the invoice back (CREDIT_ATTEMPT_UNRESOLVED) until the request that made it is sent again.

GET /invoices/{ref}/credit-notes lists the credit notes booked against an invoice. There is no endpoint that reads a single credit note.

Occurrences of a recurring series

An occurrence of a recurring series is read as an invoice once it is one: GET /invoices lists it with its series_id, period_from and period_to. It is written through its series. PUT /invoices/{ref} and DELETE /invoices/{ref} on an occurrence are refused with INVOICE_IS_OCCURRENCE (409): change its lines through PATCH /recurring-series/{ref}/lines/{lineRef} instead.

A series created through the API is active at once, and the occurrences whose period has started are invoices from that moment.

Issuing many invoices

There is no batch endpoint. To issue several invoices, call the issue endpoint for each: every call is safe to repeat, so a run that stopped halfway can be started again from the top. The platform_write limit bounds how fast one key can issue.

For invoices that should go out on a date without a call from you, leave auto_send on and let the daily run issue them.

Work registration

Setting a person's hours and completion on a line, as totals.

Webhooks

Signed, at-least-once events for changes your integration did not make.

På denne side

Issue it yourselfLeave it to the daily runBooked, and not deliveredWhen an invoice is not issuedChanging an issued invoiceOccurrences of a recurring seriesIssuing many invoices

Denne side

Vis som MarkdownÅbn i Claude