Tabba.
IntegrationerSådan virker TabbaFeatures

© 2026 Tabba. Alle rettigheder forbeholdes.

Udviklere
Udviklere
Udviklere
Overview
Take a line of a recurring series away DELETEList the recurring series of the company GETRead one recurring series GETList the occurrences of a recurring series GETRead one occurrence of a recurring series GETChange the header fields of a recurring series that the body states PATCHChange a line of a recurring series PATCHCreate a recurring series POSTCancel a recurring series POSTAdd a line to a recurring series POSTIssue an occurrence of a recurring series now POST

Andre spor

AI clientsREST API
Recurring series

Create a recurring series

A series is Tabba’s own record: nothing is asked of the accounting platform until one of its occurrences is issued. With an external_id no series carries, the series is created active with its lines, each complete, and the answer is 201; occurrences whose period has started are invoices at once. With one a series carries, the header of that series is saved, as PATCH saves it, and the answer is 200: **its lines are left as they are**. A line has a timeline of its own, so the lines of a series that exists are changed through the line routes. Two creates under one external_id at the same moment make one series.

POST
/recurring-series

Authorization

bearerAuth
AuthorizationBearer <token>

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/recurring-series" \  -H "Content-Type: application/json" \  -d '{    "external_id": "string",    "customer": "string",    "currency": "str",    "frequency": "monthly",    "start_date": "string",    "lines": [      {        "product": "string",        "recognition_method": "value",        "assignees": [          {            "user": "string"          }        ]      }    ]  }'
{  "object": "recurring_series",  "id": "string",  "external_id": "string",  "customer_id": "string",  "customer_name": "string",  "currency": "string",  "frequency": "monthly",  "start_date": "string",  "end_date": "string",  "issue_timing": {    "mode": "start-of-period"  },  "title": "string",  "use_billing_period_as_title": true,  "layout_ref": "string",  "status": "draft",  "cancelled_at": "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        }      ]    }  ]}

Change a line of a recurring series PATCH

The fields the body states are changed as the line stands on the occurrence `period` names. With `this-and-following` the change holds from that occurrence on, and earlier occurrences keep what they had. With `this-only` it holds on that occurrence alone, and what the line says before and after is left as it is. The result is what the same edit in the workspace leaves: on the first occurrence every change is the line’s own start, whatever scope is asked; the product always holds from the occurrence on; and a field an occurrence already states for itself stays its own until that field is changed for this and following. A one-time line is on one occurrence, so either scope changes it in place. The same change sent again writes nothing more. Refused with `OCCURRENCE_ISSUED` (409) when the occurrence is issued, and with `SERIES_LINE_NOT_FOUND` (404) when the line is not on that occurrence.

Cancel a recurring series POST

A series ends by date, and there is no cancelled state. The series bills up to `end_date`: the un-issued occurrence the date falls in is cut and prorated, un-issued occurrences past it are dropped, and what was ISSUED past it is handed back with credit notes. Those are queued and booked by a job, so the request does not wait for the platform: `credit_notes` lists each as it stands (`queued`, or `booked`). An end set to today or to a day that has passed issues the occurrences that are due at once. A series that has not started and has issued nothing is deleted instead (`discarded`). The body may be left out. **Sending the same end date again is safe**: it is never refused, writes no second credit note, states the credit notes as they now stand, and has any that is not booked attempted again. A different date is refused once the end has passed (`END_HAS_PASSED`) or a credit note has settled it (`END_CREDITED`).

external_id*string

Your own key. A create under one that is taken saves the header of that series and leaves its lines.

Length1 <= length <= 255
customer*string

The customer billed: its id, or ext:<external_id>.

Length1 <= length <= 255
currency*string

ISO 4217.

Length3 <= length <= 3
frequency*string

Value in

  • "monthly"
  • "quarterly"
  • "semi-annual"
  • "annual"
start_date*string

YYYY-MM-DD.

end_date?|

YYYY-MM-DD, inclusive, or null for no end.

issue_timing?||

Left out: at the start of each period.

title?|null
use_billing_period_as_title?boolean
layout_ref?|null
lines*array<>

The recurring lines the series starts with: at least one, each complete. Not read for a series that exists.

Itemsitems <= 200