Getting started
Base URL, your first call, what a key reaches, and the no-sandbox note.
The Tabba Sales API lets your own systems operate the Sales module of one company: its customers and products, its invoices and recurring series, work registration, credit notes, and the revenue recognition and month-end accrual that read them. It is the same service the workspace calls. A write goes to the company's accounting system first, exactly as a write in the workspace does.
Base URL
https://www.tabba.io/api/v1Every endpoint lives under this prefix. Changes within /api/v1 are additive:
fields, endpoints and error codes are added, and none is renamed.
The contract is published as an OpenAPI 3.1 document at
https://www.tabba.io/api/v1/openapi.json. It takes no key. The
API reference is built from it.
Your first call
An admin or a bookkeeper of the company mints an API key in the company's settings,
under Developers (/workspace/settings/company/developers). The raw tba_… key is shown once, when
it is minted. Store it in your integration's secret manager and send it as a
bearer token.
curl https://www.tabba.io/api/v1/config \
-H "Authorization: Bearer $TABBA_API_KEY"const res = await fetch('https://www.tabba.io/api/v1/config', {
headers: { Authorization: `Bearer ${process.env.TABBA_API_KEY}` },
})
const config = await res.json()
console.log(config.company_name, config.base_currency)GET /config answers the company the key is bound to, the base currency
every figure of the company is stated in, and the two accounts the month-end
accrual moves. Both accounts are null until an admin has confirmed them.
What a key reaches
A key is bound to one connected company and has a scope. The company is on one of two accounting systems, and every endpoint behaves the same on either: see Accounting platforms.
| Scope | Methods | Use |
|---|---|---|
read | GET | Dashboards, reporting, a one-way sync out of Tabba |
read_write | all | Create and change records, register work, issue invoices |
A read key on a write endpoint is answered 403 with the code
SCOPE_INSUFFICIENT.
What the API covers
| Resource | What a key does with it |
|---|---|
| Config | Reads the company, its base currency and its accrual accounts |
| Users | Lists the people who can work in the company, and sets their external_id |
| Customers, products | Lists, creates, changes and deletes them, in the accounting system first |
| Invoices | Writes drafts, issues them, reads their revenue recognition |
| Recurring series | Creates a series, changes its lines, reads and issues its occurrences, ends it |
| Work registrations | Sets a person's hours and completion on a line |
| Credit notes | Credits an issued invoice in full or in part |
| Recognition, accruals | Reads what a customer has earned and billed, and a month's accrual entry |
There is no sandbox
There is no test mode and no test key. Build against a company you control, with a throwaway customer and product, and delete them when you are done.
Three facts make that safe to do:
- A draft invoice is Tabba's own record. Nothing is asked of the accounting system until the invoice is issued, and a draft is deleted without a trace in the books.
- A customer and a product are written to the accounting system at once. Delete the ones you made for testing.
- A new invoice has
auto_sendon unless you say otherwise. The daily run issues such an invoice on its issue date. Sendauto_send: falseon every invoice you create while testing.
Where to go next
- Quickstart: from a key to recognised revenue, in curl, Node and Python.
- Authentication: the key, its scope, the company it is bound to, rotation.
- Accounting platforms: what is identical on both accounting systems, and the few fields and refusals a platform decides.
- external_id and idempotency: how you address every record by your own key, and why a retried create makes nothing twice.
- Work registration: sending hours and completion into the figures the workspace shows.
- Issuance and auto-send: issuing an invoice yourself, or leaving it to the daily run.
- Webhooks: signed events for changes your integration did not make.
- Pagination: cursors, and following a list
with
updated_since. - Errors: the error envelope, every code, the rate limits.