tributs

API v1

Tributs API

Read your companies' data from Power BI, Excel (Power Query) or your own program. The API is read-only and sees exactly what you see in Tributs, with your permissions.

  • Read-only
  • REST + JSON
  • OpenAPI 3.1
  • Bearer token
On this page
  1. Overview
  2. Quickstart
  3. Authentication
  4. Pagination and sync
  5. Resources
  6. Conventions
  7. Rate limits
  8. Errors
  9. Excel and Power BI
  10. AI assistants
  11. Security
  12. Versions

Overview

The API has two addresses. The accounts address, accounts.tributs.ad, tells you which companies you can access. Each company serves its data at its own address, and that data only travels through there.

Any user can create a token, whatever their role. If you work at an advisory firm, a single token reads every one of your clients' companies.

Base URLFor
https://accounts.tributs.ad/api/v1The companies your token can access
https://{slug}.tributs.ad/api/v1One company's data

Quickstart

  1. Create a token

    In Tributs, open your profile → External connections → Create token. Name it after the program that will use it and choose when it expires: 30 days, 90 days or 1 year. The token (trb_…) is shown once: copy it then.

  2. Find your companies

    Each company comes with its api_base_url, which is where you read its data.

    cURL
    curl https://accounts.tributs.ad/api/v1/companies \
      -H "Authorization: Bearer $TRIBUTS_TOKEN"
    200 OK · application/json
    {
      "data": [
        {
          "id": "acme.tributs.ad",
          "name": "Acme Serveis SL",
          "display_name": "Acme",
          "role": "advisor",
          "status": "active",
          "api_base_url": "https://acme.tributs.ad/api/v1"
        }
      ],
      "next_cursor": null,
      "has_more": false
    }
  3. Read data

    Lists come in pages. This example fetches every sales invoice since 1 January.

    curl "https://acme.tributs.ad/api/v1/sales_invoices?date_from=2026-01-01&limit=200" \
      -H "Authorization: Bearer $TRIBUTS_TOKEN"

Authentication

Send the token in the Authorization header of every request, with the Bearer scheme. All requests go over HTTPS.

HTTP
Authorization: Bearer trb_…
  • The token is opaque: trb_ followed by 40 characters. Tributs stores only a fingerprint (SHA-256), so nobody can see it again.
  • It always expires: after 30 days, 90 days or 1 year. We email you 7 days before and on the day it expires.
  • Renew issues a new key for the same token. The old key stops working within a minute; the name and history are kept.
  • You can revoke it at any time. Revocation reaches every company within 60 seconds.
  • It acts as you: in each company it has exactly your role and your permissions.
  • Up to 10 live tokens per person. Use one token per program.

The company stays in control

Each company's owner sees in the settings who reads it through the API, with which token and when, and can cut access for everyone or for one person.

Pagination and sync

Lists return data, next_cursor and has_more. While has_more is true, call again with the same parameters plus cursor=<next_cursor>. limit goes from 1 to 200 (50 by default, and at most 50 with expand=lines).

Items are ordered by last change, oldest first. To keep a copy in sync, store the updated_at of the last item and pass it as updated_since next time: you get what changed since then. Store items by id, replacing the ones you have, because those from the last second can come back.

A cancelled document comes back with status: "cancelled". Drafts don't appear: only issued documents are returned. Bank transactions are the exception, because they are never issued: they always appear, and a manual one that gets deleted disappears without notice.

Invoices and bank transactions accept sort: total_desc (largest invoices first), issue_date_desc (latest invoices first) and date_desc (latest bank lines first). A cursor is only valid for the sort it was issued with. With include=totals, the first page (the call without cursor) carries totals: how many items match the filters and what they add up to, across every page. Cancelled ones are counted apart and add nothing to the sums.

Resources

Every resource answers GET. The full contract, with every field and parameter, is the OpenAPI 3.1 file each company serves without a token at {api_base_url}/openapi.json. Import it into Postman or generate a client from it.

EndpointContentsFilters
GET/companies Companies your token can read, with each one's api_base_url. Call it on accounts.tributs.ad. —
GET/contacts Customers and suppliers, with tax id, country and roles. role
GET/sales_invoices Issued sales invoices, credit notes and daily till summaries. expand=lines adds lines and IGI per rate. date_fromdate_tostatustypecontact_idmin_totalsortinclude=totalsexpand=lines
GET/purchase_invoices Purchase invoices and credit notes, with the deductible IGI actually supported. date_fromdate_tostatustypecontact_idmin_totalsortinclude=totalsexpand=lines
GET/journal_lines General ledger lines: date, account, debit, credit, contact and source document. date_fromdate_toaccountcontact_iddocument_id
GET/accounts Chart of accounts (PGC of Andorra), with number, name and parent account. —
GET/bank_transactions Bank statement lines and their reconciliation status. date_fromdate_tostatusbank_account_idmin_amountsortinclude=totals
GET/bank_accounts The company's bank accounts with two balances: the one in the books (ledger_balance) and the one on the bank statement (statement_balance). date
GET/receivables What each customer owes as of today: open amount, overdue, due within 30 days and advances not yet applied. Largest balance first by default; sort=oldest_due puts the oldest due date first. min_amountsortinclude=totals
GET/payables What is owed to each supplier, with the same fields as receivables. min_amountsortinclude=totals
GET/obligations Tax calendar: model, period, deadline, status and the result of the saved return. GET /obligations/{id} adds the figures box by box. yearstatusdeadline_fromdeadline_to
GET/payroll_summary Payroll totals per month: payslips and people, gross, net, social security (CASS) of the workers and of the company, tax withheld (IRPF and IRNR) and total cost, with salaried staff and partners apart. No names. month_frommonth_to
GET/payslips Payslips, one per person and month: gross, net, social security, tax withheld with its rate, company cost, status and payment date. include=totals adds the sums. month_frommonth_toemployee_idstatuskindsortinclude=totals
GET/employees People on the payroll: name, start and end dates, contract type, job category and tax residence. statuskind
GET/reports/profit_and_loss Profit and loss by heading, with the detail per account. date_fromdate_toperiodicityperiods
GET/reports/balance_sheet Balance sheet by heading, with the detail per account. dateperiodicityperiods
GET/reports/trial_balance Trial balance: opening balance, debit, credit and closing balance per account. date_fromdate_to

Paginated lists (every resource except companies, bank_accounts, payroll_summary and the reports) also accept limit and cursor, and updated_since except receivables and payables, which are balances as of today.

To read a single document: GET /sales_invoices/{id}, /purchase_invoices/{id}, /contacts/{id} and /obligations/{id} (by id or by code, such as M900-T2-2026). Invoices always come with their lines, and the obligation with the figures of the saved return (filing): result and breakdown by box for forms 900, 910, 800, 805, 810, 700, 710, 310 and 311. Withholding forms come as totals, without the list of payees.

Not in v1: PDFs and attachments, quotes, delivery notes, subscriptions and projects. For the people on the payroll, the tax id, the social security number, the IBAN and the birth date are never returned.

Example response

GET /sales_invoices?limit=1
{
  "data": [
    {
      "id": "FV-26-00042",
      "number": "FV-26-00042",
      "type": "invoice",
      "status": "unpaid",
      "issue_date": "2026-10-02",
      "due_date": "2026-11-01",
      "contact_id": "Grup Ferrer SL",
      "contact_name": "Grup Ferrer SL",
      "contact_tax_id": "L712345A",
      "currency": "EUR",
      "net_amount": 1200.0,
      "tax_amount": 54.0,
      "total": 1254.0,
      "outstanding_amount": 1254.0,
      "document_currency": "EUR",
      "document_total": 1254.0,
      "exchange_rate": 1.0,
      "credited_invoice_id": null,
      "project_ids": [],
      "description": "Consultoria setembre",
      "disbursements_amount": 0.0,
      "created_at": "2026-10-02T08:14:03Z",
      "updated_at": "2026-10-02T08:14:03Z"
    }
  ],
  "next_cursor": "WyIyMDI2LTEwLTAyIDEwOjE0OjAzIiwiRlYtMjYtMDAwNDIiXQ",
  "has_more": true
}

Conventions

  • JSON, with field names in English and in snake_case.
  • Statuses are stable codes (paid, unpaid, cancelled…) and are never translated.
  • Accounting dates are YYYY-MM-DD (Andorra calendar). Moments are RFC 3339 in UTC.
  • Amounts are numbers with 2 decimals in EUR, the currency of the books. Foreign-currency documents also carry document_currency, document_total and exchange_rate.
  • Report headings are identified by their code (Decret 479/2025). The label is the official Catalan text.
  • The figures of a tax form are identified by their box number (box); the label is in Catalan, the language of the official form.

Rate limits

60 requests per minute per token and company, in bursts of up to 20. A report counts as 5, and so do bank_accounts, receivables, payables and payroll_summary. Because the limit is per company, a firm can read many companies in parallel; on top of that, each IP address has an overall ceiling of 600 requests per minute.

HeaderMeaning
RateLimit-LimitRequests per minute: 60.
RateLimit-RemainingRequests you can still make right now.
RateLimit-ResetSeconds until the counter is full again.
RateLimit-PolicyThe policy applied: 60;w=60;burst=20.
Retry-AfterWith 429 and 503: seconds to wait before retrying.

Errors

Errors follow RFC 9457 (application/problem+json). The code field is stable, and type links to the matching row of this table.

application/problem+json
HTTP/1.1 401 Unauthorized
Content-Type: application/problem+json

{
  "type": "https://tributs.ad/developers#errors-expired-token",
  "title": "Access token expired",
  "status": 401,
  "code": "expired_token",
  "instance": "/api/v1/sales_invoices"
}
StatuscodeWhat to do
401 missing_token Add the header Authorization: Bearer trb_….
401 invalid_token The token does not exist or was copied wrong. Check it or create a new one.
401 expired_token The token has expired. Create a new one from your profile.
401 revoked_token The token was revoked. Create a new one from your profile.
403 no_access You have no access to this company.
403 permission_denied Your role in this company cannot read this resource.
403 api_access_disabled The owner has turned off API access for the company.
403 api_access_blocked The owner has turned off your API access to this company.
403 company_suspended The company's subscription is not active.
400 invalid_parameter A parameter is not valid. The detail field says which one.
404 not_found The resource or document does not exist.
405 method_not_allowed The API only accepts GET.
429 rate_limited Wait the seconds given in Retry-After.
500 internal_error An error on our side. Try again; if it persists, write to us.
503 maintenance The company is under maintenance. Try again after the seconds in Retry-After.
503 auth_unavailable We could not check the token. Try again in a few seconds.

Excel and Power BI

In Excel or Power BI, first create a text parameter TributsToken holding the token (Manage parameters). Then open Get data → Blank query → Advanced editor and paste this query, which walks through every page.

Power Query (M)
let
    Token = TributsToken,
    Base = "https://acme.tributs.ad/api/v1/sales_invoices",
    GetPage = (cursor as nullable text) =>
        Json.Document(Web.Contents(Base, [
            Query = if cursor = null
                then [limit = "200"]
                else [limit = "200", cursor = cursor],
            Headers = [Authorization = "Bearer " & Token]
        ])),
    Pages = List.Generate(
        () => GetPage(null),
        each _ <> null,
        each if _[has_more] then GetPage(_[next_cursor]) else null
    ),
    Rows = List.Combine(List.Transform(Pages, each _[data])),
    Invoices = Table.FromRecords(Rows)
in
    Invoices

If asked for credentials, choose Anonymous: the token already travels in the header. The fixed base URL with parameters in Query lets the Power BI service refresh the report; there, in the data source credentials, tick “Skip test connection”, because that test runs without the token and gets a 401 error. The token is saved inside the file: use one just for this report and share the file only with people who may see this data.

AI assistants

An AI assistant (ChatGPT, Claude and other programs that support MCP) can read the same data as this API, with the same queries and your permissions. No token is needed: you sign in with your Tributs user.

  1. In the assistant, add a custom connector with the address https://mcp.tributs.ad/mcp, including the final /mcp.
  2. A Tributs screen opens: sign in with your user and press Allow.
  3. Under Settings → Profile → External connections you see the connected app and can withdraw its permission.
  • Read-only. Every resource on this page is a query the assistant can run, and new ones reach it on their own.
  • Its own limit: 120 requests per minute per connection, with bursts of up to 40, on top of the API limit per company.
  • Claude Code, Cursor or your own program that speaks MCP can also sign in with a token: send it as Authorization: Bearer trb_… to the same address.
  • What the assistant reads goes through the provider you chose, under its terms.

What you can ask and what the company sees

Security

How we protect access:

  • Read-only: no API endpoint changes data.
  • Tributs only stores a fingerprint of the token (SHA-256).
  • Mandatory expiry, with revocation and renewal from your profile.
  • Each company sees who reads it, and its owner can cut access.
  • Limits per token and company, and HTTPS on every request.

Best practices

  • Keep the token in a secrets manager or an environment variable. Keep it out of code, repositories and apps that run in the browser.
  • Create one token per program, with the shortest expiry that works for you.
  • Always send it in the Authorization header: URLs end up in browser history and server logs.
  • If you think it leaked, revoke it from your profile and create a new one. The trb_ prefix makes it easy for secret scanners to find.

To report a vulnerability, write to security@tributs.ad.

Versions

The version is in the URL (/api/v1/). v1 only gains fields: a breaking change goes to v2.