Visión general
La API tiene dos direcciones. La de cuentas, accounts.tributs.ad, te dice a qué empresas tienes acceso. Cada empresa sirve sus datos en su propia dirección, y esos datos solo pasan por allí.
Cualquier usuario puede crear un token, sea cual sea su rol. Si trabajas en una gestoría, un solo token lee todas las empresas de tus clientes.
| URL base | Para |
|---|---|
https://accounts.tributs.ad/api/v1 | Las empresas a las que tiene acceso el token |
https://{slug}.tributs.ad/api/v1 | Los datos de una empresa |
Inicio rápido
-
Crea un token
En Tributs, abre tu perfil → Conexiones externas → Crear token. Ponle el nombre del programa que lo usará y elige cuándo caduca: 30 días, 90 días o 1 año. El token (
trb_…) se muestra una sola vez: cópialo en ese momento. -
Encuentra tus empresas
Cada empresa llega con su
api_base_url, que es donde leerás sus datos.cURLcurl 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 } -
Lee datos
Las listas se entregan por páginas. Este ejemplo trae todas las facturas de venta desde el 1 de enero.
curl "https://acme.tributs.ad/api/v1/sales_invoices?date_from=2026-01-01&limit=200" \ -H "Authorization: Bearer $TRIBUTS_TOKEN"import os import requests BASE = "https://acme.tributs.ad/api/v1" HEADERS = {"Authorization": f"Bearer {os.environ['TRIBUTS_TOKEN']}"} params = {"date_from": "2026-01-01", "limit": 200} invoices = [] while True: response = requests.get(f"{BASE}/sales_invoices", headers=HEADERS, params=params, timeout=30) response.raise_for_status() page = response.json() invoices += page["data"] if not page["has_more"]: break params["cursor"] = page["next_cursor"]const BASE = "https://acme.tributs.ad/api/v1"; const headers = { Authorization: `Bearer ${process.env.TRIBUTS_TOKEN}` }; const invoices = []; let cursor; do { const url = new URL(`${BASE}/sales_invoices`); url.searchParams.set("date_from", "2026-01-01"); url.searchParams.set("limit", "200"); if (cursor) url.searchParams.set("cursor", cursor); const response = await fetch(url, { headers }); if (!response.ok) { const problem = await response.json().catch(() => ({})); throw new Error(problem.title ?? `HTTP ${response.status}`); } const page = await response.json(); invoices.push(...page.data); cursor = page.has_more ? page.next_cursor : undefined; } while (cursor);
Autenticación
Envía el token en la cabecera Authorization de cada petición, con el esquema Bearer. Todas las peticiones van por HTTPS.
Authorization: Bearer trb_… - El token es opaco:
trb_seguido de 40 caracteres. Tributs guarda solo una huella (SHA-256), así que nadie puede volver a verlo. - Caduca siempre: a los 30 días, a los 90 días o al año. Te escribimos 7 días antes y el día que caduca.
- Renovar genera una clave nueva para el mismo token. La anterior deja de funcionar en menos de un minuto; el nombre y el historial se conservan.
- Puedes revocarlo cuando quieras. La revocación llega a todas las empresas en 60 segundos como máximo.
- Actúa como tú: en cada empresa tiene exactamente tu rol y tus permisos.
- Hasta 10 tokens activos por persona. Usa un token para cada programa.
La empresa tiene el control
El propietario de cada empresa ve en los ajustes quién la consulta por API, con qué token y cuándo, y puede cortar el acceso a todos o a una persona concreta.
Paginación y sincronización
Las listas devuelven data, next_cursor y has_more. Mientras has_more sea true, vuelve a llamar con los mismos parámetros y cursor=<next_cursor>. limit va de 1 a 200 (50 por defecto, y 50 como máximo con expand=lines).
Los elementos salen ordenados por el último cambio, del más antiguo al más reciente. Para mantener una copia al día, guarda el updated_at del último elemento y pásalo como updated_since la próxima vez: te llega lo que ha cambiado desde entonces. Guarda los elementos por id, sustituyendo los que ya tienes, porque los del último segundo pueden volver a salir.
Un documento anulado vuelve a salir con status: "cancelled". Los borradores no salen: solo hay documentos emitidos. La excepción son los movimientos bancarios, que no se emiten: salen siempre, y uno manual que se elimine desaparece sin aviso.
Las facturas y los movimientos bancarios aceptan sort: total_desc (las facturas de más importe primero), issue_date_desc (las facturas más recientes primero) y date_desc (los movimientos más recientes primero). Un cursor solo vale para el orden con el que se pidió. Con include=totals, la primera página (la llamada sin cursor) lleva totals: cuántos elementos cumplen los filtros y cuánto suman, contando todas las páginas. Los anulados se cuentan aparte y no suman.
Recursos
Todos los recursos responden a GET. El contrato completo, con cada campo y cada parámetro, es el fichero OpenAPI 3.1 que cada empresa sirve sin token en {api_base_url}/openapi.json. Impórtalo en Postman o genera un cliente a partir de él.
| Endpoint | Contenido | Filtros |
|---|---|---|
GET/companies | Empresas que puede leer el token, con el api_base_url de cada una. Se llama en accounts.tributs.ad. | — |
GET/contacts | Clientes y proveedores, con NRT, país y roles. | role |
GET/sales_invoices | Facturas de venta emitidas, abonos y cierres de caja. expand=lines añade las líneas y el IGI por tipo. | date_fromdate_tostatustypecontact_idmin_totalsortinclude=totalsexpand=lines |
GET/purchase_invoices | Facturas de compra y abonos, con el IGI deducible efectivamente soportado. | date_fromdate_tostatustypecontact_idmin_totalsortinclude=totalsexpand=lines |
GET/journal_lines | Líneas del libro diario: fecha, cuenta, debe, haber, tercero y documento de origen. | date_fromdate_toaccountcontact_iddocument_id |
GET/accounts | Plan de cuentas (PGC de Andorra), con número, nombre y cuenta padre. | — |
GET/bank_transactions | Movimientos bancarios y su estado de conciliación. | date_fromdate_tostatusbank_account_idmin_amountsortinclude=totals |
GET/bank_accounts | Cuentas bancarias de la empresa con dos saldos: el de los libros (ledger_balance) y el del extracto del banco (statement_balance). | date |
GET/receivables | Lo que debe cada cliente a día de hoy: pendiente, vencido, a 30 días y anticipos sin aplicar. Por defecto, el saldo mayor primero; sort=oldest_due, el vencimiento más antiguo. | min_amountsortinclude=totals |
GET/payables | Lo que se debe a cada proveedor, con los mismos campos que receivables. | min_amountsortinclude=totals |
GET/obligations | Calendario fiscal: modelo, período, plazo, estado y el resultado de la declaración guardada. GET /obligations/{id} añade las cifras casilla a casilla. | yearstatusdeadline_fromdeadline_to |
GET/payroll_summary | Totales de nóminas por mes: nóminas y personas, bruto, neto, CASS del trabajador y de la empresa, retenciones (IRPF e IRNR) y coste total, con asalariados y socios por separado. Sin nombres. | month_frommonth_to |
GET/payslips | Nóminas, una por persona y mes: bruto, neto, CASS, retención con su tipo, coste de empresa, estado y fecha de pago. include=totals añade las sumas. | month_frommonth_toemployee_idstatuskindsortinclude=totals |
GET/employees | Personas de la plantilla: nombre, alta y baja, tipo de contrato, categoría y residencia fiscal. | statuskind |
GET/reports/profit_and_loss | Cuenta de pérdidas y ganancias por epígrafes, con el detalle por cuenta. | date_fromdate_toperiodicityperiods |
GET/reports/balance_sheet | Balance por epígrafes, con el detalle por cuenta. | dateperiodicityperiods |
GET/reports/trial_balance | Balance de sumas y saldos: saldo inicial, debe, haber y saldo final por cuenta. | date_fromdate_to |
Las listas paginadas (todos los recursos salvo companies, bank_accounts, payroll_summary y los informes) aceptan además limit y cursor, y updated_since salvo receivables y payables, que son saldos a día de hoy.
Para leer un solo documento: GET /sales_invoices/{id}, /purchase_invoices/{id}, /contacts/{id} y /obligations/{id} (por id o por code, como M900-T2-2026). Las facturas llegan siempre con sus líneas, y la obligación con las cifras de la declaración guardada (filing): resultado y desglose por casilla de los modelos 900, 910, 800, 805, 810, 700, 710, 310 y 311. Las retenciones salen en totales, sin la relación de perceptores.
Fuera de la v1: PDF y adjuntos, presupuestos, albaranes, suscripciones y proyectos. De las personas de la plantilla nunca salen el NRT, el número de afiliación a la CASS, el IBAN ni la fecha de nacimiento.
Ejemplo de respuesta
{
"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
} Convenciones
- JSON, con nombres de campo en inglés y en
snake_case. - Los estados son códigos estables (
paid,unpaid,cancelled…) y no se traducen. - Las fechas contables van en formato
YYYY-MM-DD(calendario de Andorra). Los momentos, en RFC 3339 y UTC. - Los importes son números con 2 decimales en EUR, la moneda de los libros. Los documentos en moneda extranjera llevan también
document_currency,document_totalyexchange_rate. - Los epígrafes de los informes se identifican por su
code(Decret 479/2025). El texto es el oficial en catalán. - Las cifras de un modelo tributario se identifican por el número de casilla (
box); la etiqueta va en catalán, la lengua del formulario oficial.
Límites
60 peticiones por minuto por token y empresa, con ráfagas de hasta 20. Un informe cuenta como 5, igual que bank_accounts, receivables, payables y payroll_summary. Como el límite es por empresa, una gestoría puede leer muchas empresas a la vez; además, cada dirección IP tiene un techo global de 600 peticiones por minuto.
| Cabecera | Significado |
|---|---|
RateLimit-Limit | Peticiones por minuto: 60. |
RateLimit-Remaining | Peticiones que aún puedes hacer ahora mismo. |
RateLimit-Reset | Segundos hasta que el contador se recupera del todo. |
RateLimit-Policy | La política aplicada: 60;w=60;burst=20. |
Retry-After | Con 429 y 503: segundos que hay que esperar antes de reintentar. |
Errores
Los errores siguen el RFC 9457 (application/problem+json). El campo code es estable y type enlaza con la fila correspondiente de esta tabla.
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"
} | Estado | code | Qué hacer |
|---|---|---|
| 401 | missing_token | Añade la cabecera Authorization: Bearer trb_…. |
| 401 | invalid_token | El token no existe o está mal copiado. Revísalo o crea uno nuevo. |
| 401 | expired_token | El token ha caducado. Crea uno nuevo desde tu perfil. |
| 401 | revoked_token | El token está revocado. Crea uno nuevo desde tu perfil. |
| 403 | no_access | No tienes acceso a esta empresa. |
| 403 | permission_denied | Tu rol en esta empresa no permite leer este recurso. |
| 403 | api_access_disabled | El propietario ha cortado el acceso por API a la empresa. |
| 403 | api_access_blocked | El propietario te ha cortado el acceso por API a esta empresa. |
| 403 | company_suspended | La suscripción de la empresa no está activa. |
| 400 | invalid_parameter | Un parámetro no es válido. El campo detail dice cuál. |
| 404 | not_found | El recurso o el documento no existe. |
| 405 | method_not_allowed | La API solo admite GET. |
| 429 | rate_limited | Espera los segundos que indica Retry-After. |
| 500 | internal_error | Error por nuestra parte. Vuelve a intentarlo; si se repite, escríbenos. |
| 503 | maintenance | La empresa está en mantenimiento. Vuelve a intentarlo pasados los segundos de Retry-After. |
| 503 | auth_unavailable | No hemos podido comprobar el token. Vuelve a intentarlo en unos segundos. |
Excel y Power BI
En Excel o en Power BI, crea primero un parámetro de texto TributsToken con el token (Administrar parámetros). Después abre Obtener datos → Consulta en blanco → Editor avanzado y pega esta consulta, que recorre todas las páginas.
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 Si te pide credenciales, elige Anónimo: el token ya viaja en la cabecera. La URL base fija y los parámetros en Query permiten actualizar el informe desde el servicio de Power BI; allí, en las credenciales del origen de datos, marca «Skip test connection» (omitir la prueba de conexión), porque esa prueba se hace sin token y recibe un error 401. El token queda guardado dentro del fichero: usa uno solo para este informe y comparte el fichero solo con quien pueda ver estos datos.
Asistentes de IA
Un asistente de IA (ChatGPT, Claude y otros programas compatibles con MCP) puede leer los mismos datos que esta API, con las mismas consultas y tus permisos. No hace falta token: entras con tu usuario de Tributs.
- En el asistente, añade un conector personalizado con la dirección
https://mcp.tributs.ad/mcp, con el/mcpdel final. - Se abre una pantalla de Tributs: entra con tu usuario y pulsa Permitir.
- En Ajustes → Perfil → Conexiones externas ves la aplicación conectada y le puedes retirar el permiso.
- Solo lectura. Cada recurso de esta página es una consulta del asistente, y los que se añadan le llegan solos.
- Límite propio: 120 peticiones por minuto por conexión, con ráfagas de hasta 40, además del límite de la API por empresa.
- Claude Code, Cursor o un programa propio que hable MCP también pueden entrar con un token: envíalo como
Authorization: Bearer trb_…a la misma dirección. - Lo que el asistente lee pasa por el proveedor que tú has elegido, con sus condiciones.
Seguridad
Cómo protegemos el acceso:
- Solo lectura: ningún endpoint de la API modifica datos.
- Tributs solo guarda una huella del token (SHA-256).
- Caducidad obligatoria, y revocación y renovación desde el perfil.
- Cada empresa ve quién la lee, y el propietario puede cortar el acceso.
- Límites por token y empresa, y HTTPS en todas las peticiones.
Buenas prácticas
- Guarda el token en un gestor de secretos o en una variable de entorno. Mantenlo fuera del código, de los repositorios y de las aplicaciones que se ejecutan en el navegador.
- Crea un token para cada programa, con la caducidad más corta que te vaya bien.
- Envíalo siempre en la cabecera
Authorization: las URL quedan en los historiales y en los registros de los servidores. - Si crees que se ha filtrado, revócalo desde el perfil y crea uno nuevo. El prefijo
trb_facilita que los escáneres de secretos lo encuentren.
Para informar de una vulnerabilidad, escribe a security@tributs.ad.
Versiones
La versión va en la URL (/api/v1/). La v1 solo gana campos: un cambio incompatible va a la v2.