Create a draft invoice
A draft is Tabba’s own record: nothing is asked of the accounting platform until the invoice is issued. With an external_id no invoice carries, a draft is created and the answer is 201. With one a draft carries, that draft is replaced and the answer is 200: a line that states its id stays the line it was, with the work registered on it; a line that states none is new; a line the body leaves out is removed. Two creates under one external_id at the same moment make one invoice: the second is answered that invoice, or 409 CREATE_IN_PROGRESS with Retry-After while the first is still writing it. An external_id an ISSUED invoice carries is refused with INVOICE_ALREADY_ISSUED (409): an issued invoice is frozen, and a credit note is the way to change it.
Authorization
bearerAuth An API key minted by an admin under Settings → Developers. It is bound to one company and has a scope: read, or read_write. A request without one, or with one that is not ours, is answered 401 and metered by its client address (x-rate-limits).
In: header
Request Body
application/json
TypeScript Definitions
Use the request body type in TypeScript.
Response Body
application/json
application/json
application/json
application/json
application/json
application/json
application/json
application/json
application/json
application/json
application/json
curl -X POST "https://example.com/invoices" \ -H "Content-Type: application/json" \ -d '{ "external_id": "string", "customer": "string", "currency": "str", "lines": [ { "product": "string", "recognition_method": "value", "assignees": [ { "user": "string" } ] } ] }'{ "object": "invoice", "id": "string", "external_id": "string", "customer_id": "string", "customer_name": "string", "currency": "string", "issue_date": "string", "title": "string", "auto_send": true, "layout_ref": "string", "status": "draft", "issued_at": "string", "platform_ref": "string", "platform_number": -9007199254740991, "send_error": { "reason": "string", "platform_reason": "string" }, "series_id": "string", "period_from": "string", "period_to": "string", "created_at": "string", "updated_at": "string", "lines": [ { "id": "string", "product_id": "string", "product_number": "string", "product_name": "string", "recognition_method": "value", "agreed_value": 0, "hourly_rate": 0, "discount": 0, "description_override": "string", "position": -9007199254740991, "assignees": [ { "user_id": "string", "distribution_percentage": 0, "hourly_rate_override": 0, "removed": true } ], "series_line_id": "string" } ]}Read the revenue recognition of one invoice GET
Earned, billed, deferred revenue and work in progress, per line and for the invoice, in the company’s base currency. The figures are the month-end accrual’s own, stated as of the last day of `month`: the running month where it is left out.
Issue an invoice now POST
The accounting platform books and sends the invoice inside the request, and the answer is the issued invoice. It is the explicit "bill this one now": an invoice set not to send is issued all the same, and one with no 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. **A document that booked and could not be delivered is a 200** whose `send_error` says why: the invoice is in the books, so do not issue it again. Only a refusal to book is an error: `ISSUE_REFUSED` (422) carries the platform’s reason under `refusal`; `HOURLY_INCOMPLETE` and `NOTHING_TO_BILL` (409) say an hourly line is not ready; `CUSTOMER_GONE` and `PRODUCT_GONE` (409) that the platform no longer holds what the invoice names; `ISSUE_IN_PROGRESS` (409, with `Retry-After`) that another attempt holds the invoice. Metered against the `platform_write` bucket as well as `default`.