Conceptos
Lo que conviene entender antes de integrar: sobre qué empresa trabaja cada clave, cómo se identifican las facturas y cómo cambian de estado.
Empresa y API key
- Cada API key pertenece a una sola empresa. Todo lo que haces con ella ocurre en esa empresa.
- La empresa no va en la URL: la API la deduce de la clave. Consúltala con
GET /api/v1.1/company. GET /api/v1.1/companiesdevuelve una lista con un solo elemento, la empresa de la clave.- Una factura de otra empresa no existe para tu clave: la API responde
404coninvoice_not_found. - Para trabajar con varias empresas, usa una clave por empresa. Ver Multiempresa.
- Para emitir, la empresa tiene que estar verificada en la aplicación. Si no lo está, emitir responde
409concompany_not_verified.
Identificadores
- Cada factura tiene un
idopaco, hoy un UUID. Es el{invoice_id}de las rutas. external_ides tu referencia: elmeta.external_refque enviaste.- Guarda el
idtal como lo devuelve la API. No lo construyas ni lo deduzcas del número.
Formato canónico json_1_1
Las facturas se envían en JSON con tres bloques: schema_version, meta e invoice.
{
"schema_version": "json_1_1",
"meta": {
"direction": "issued",
"source_system": "mi-erp",
"external_ref": "PEDIDO-2026-001"
},
"invoice": {}
}
json_1_1es el único valor admitido. Cualquier otro,json_1incluido, se rechaza conunsupported_schema_version.- No envíes los campos que calcula el sistema, como
tax_category_codeotaxableen las líneas. La API responde422coninferred_fields_not_allowedy las rutas afectadas. - Las lecturas no devuelven el canónico, sino un resumen de la factura:
id,status,series,number, partes y totales.
El contenido de invoice está en Formato canónico.
Dirección: emitidas y recibidas
direction | Qué es | Recursos |
|---|---|---|
issued | Facturas que expide tu empresa | /drafts, /issued-invoices |
received | Facturas que tu empresa recibe de sus proveedores | /received-invoices |
- En una emitida, el emisor (
invoice.supplier) es tu empresa. En una recibida, el receptor (invoice.customer) es tu empresa. Si no coincide con la empresa de la clave, la API rechaza la factura. /received-invoicesguarda siempre la factura como recibida, diga lo que digameta.direction.- Pedir una recibida por
/issued-invoices/{invoice_id}, o al revés, responde404coninvoice_not_found.
Estados y ciclo de vida
El campo status de la respuesta usa estos valores:
status | Significa |
|---|---|
draft | Borrador de emitida. Aún no tiene número ni fecha de emisión. |
queued | En cola de una emisión en bloque lanzada desde la aplicación. |
issued | Emitida: ya tiene número y fecha. |
sent | Emitida y enviada por email. |
cancelled | Anulada. |
received | Recibida registrada. |
Qué operación cambia cada estado:
| Operación | Desde | Hasta |
|---|---|---|
PUT /api/v1.1/drafts | Nueva o draft | draft |
POST /api/v1.1/issued-invoices/{invoice_id}/issue | draft | issued |
POST /api/v1.1/issued-invoices/{invoice_id}/send | issued o sent | sent |
POST /api/v1.1/issued-invoices/{invoice_id}/cancel | issued o sent | cancelled |
POST /api/v1.1/issued-invoices/import | Nueva | issued |
PUT /api/v1.1/received-invoices | Nueva o received | received |
- Borrador con errores. Un borrador se guarda aunque tenga errores de validación y sigue en
draft.PUT /draftsrespondesuccess: falsecon la listaerrors, yPOST /drafts/{invoice_id}/validatedevuelveis_valid. - Recibida con errores. Una recibida guardada con errores bloqueantes aparece con
status: "draft"hasta que la corriges. - Estado que no admite la operación. La API responde
409coninvalid_status_transition. Sus camposfromytotraen el estado interno en mayúsculas (ISSUED,CANCELLED). - En cola. Emitir una factura en
queuedresponde409coninvoice_queued_for_bulk_issue. - Listado de emitidas.
GET /issued-invoicessinstatusincluye también borradores y facturas en cola. Filtra constatus=issuedsi solo quieres las emitidas. - Borrado. Un borrador se puede borrar. Una emitida, solo si la importaste y no está incluida en un modelo presentado; si no,
409coninvoice_delete_not_allowed.
Qué se envía a Hacienda al emitir o anular está en Cumplimiento.
Series y numeración
- En emitidas,
invoice.header.invoice_series_codees obligatorio. Sin él,422conmissing_required_fields. - Si la serie no existe, se crea al usarla. Una serie creada así numera con la forma
{SERIE}-{SEQ}: la serieAdaA-1,A-2,A-3… y el contador no se reinicia. - Las series que configuras en la aplicación pueden usar otra plantilla, con año, mes o ceros a la izquierda, pero siempre empiezan por el código de la serie.
- El número se asigna al emitir: el siguiente de la serie o, si tu programa numera, el que envías en el cuerpo de la emisión (ver Facturas emitidas). En un borrador no envíes
invoice_numberniissue_date: la API responde422coninvoice_number_not_allowedoissue_date_not_allowed. La fecha de emisión es la del día en que emites. - El número ya incluye la serie. En la respuesta,
serieses"A"ynumberes"A-1". No los concatenes. - Al importar una emitida fuera de Faxtal (
POST /issued-invoices/import) envías túinvoice_numbereissue_date, tal como figuran en la factura. La fecha no puede ser futura:422confuture_issue_date. - En recibidas, el número es el del proveedor y la serie es opcional.
Consultar las series: GET /api/v1.1/series devuelve las de tu empresa, con code (el valor que se envía en invoice_series_code), name, direction, prefix, template, is_default, is_corrective, is_active y current_seq. Filtros direction (issued o received) y active. Permiso de la clave: invoices:read.
current_seq es el último secuencial consumido: si numeras tú, te dice por dónde va la serie antes de emitir con tu número.
Paginación
Los listados se paginan con limit y offset:
curl -s "https://app.faxtal.com/api/v1.1/issued-invoices?status=issued&limit=100&offset=200" \
-H "X-API-Key: $FAXTAL_API_KEY"
Respuesta 200 resumida:
{
"items": [
{
"id": "00000000-0000-4000-8000-000000000001",
"direction": "issued",
"status": "issued",
"series": "A",
"number": "A-201",
"issue_date": "2026-09-14",
"external_id": "PEDIDO-2026-201",
"supplier_tax_id": "B10000008",
"supplier_name": "Empresa Demo S.L.",
"customer_tax_id": "B10000016",
"customer_name": "Cliente Prueba S.L.",
"currency": "EUR",
"total_base": 1000.0,
"total_tax": 210.0,
"total": 1210.0,
"created_at": "2026-09-14T10:15:00",
"updated_at": "2026-09-14T10:15:02",
"corrections": null
}
],
"total": 1250,
"limit": 100,
"offset": 200
}
- Para recorrer un listado entero, suma
limitaoffsethasta queoffsetalcancetotal. limitvale 50 si no lo indicas. Su máximo depende del recurso:
| Listado | Máximo de limit |
|---|---|
/issued-invoices, /drafts, /received-invoices | 500 |
/third-parties, /products, /verifactu/submissions | 200 |
- Un
limito unoffsetfuera de rango responde422conrequest_validation_error. - Algunos listados solo aceptan
limity devuelvenitemsytotal, sinoffset: las incidencias de VeriFactu, y las facturas pendientes y los vencimientos del SII. - Las facturas salen ordenadas por serie y, dentro de cada serie, de la fecha de emisión más reciente a la más antigua. Si entran facturas mientras recorres un listado, una página puede repetir o saltarse alguna.
Filtros
| Listado | Filtros |
|---|---|
GET /issued-invoices | status, series, customer_tax_id, number, external_ref, issue_date_from, issue_date_to |
GET /drafts | series, customer_tax_id, number, external_ref, issue_date_from, issue_date_to |
GET /received-invoices | status, series, supplier_tax_id, number, external_ref, issue_date_from, issue_date_to |
GET /third-parties | search, role (customer o supplier) |
GET /products | search |
seriesy los NIF buscan el valor exacto.- Las fechas van en formato
YYYY-MM-DDy el rango incluye los dos extremos. statusadmitedraft,issued,sent,cancelledyqueued. Un valor que no es un estado responde400conunknown_status.numberyexternal_reftambién buscan el valor exacto.
Recuperar una factura cuyo id has perdido
external_ref es tu propia referencia: la envías en meta.external_ref al crear el borrador y vuelve en external_id. Si tu sistema pierde el id de Faxtal, búscala por ella y recupéralo:
curl -G "https://app.faxtal.com/api/v1.1/issued-invoices" \
-H "X-API-Key: fctXXXXXXXX_YYYYYYYYYYYYYYYY" \
-d external_ref=PEDIDO-2026-001
Si lo que conservas es el número, usa number (el número completo, con la serie: A-1).
Idempotencia
La API no admite la cabecera Idempotency-Key. Para reintentar sin duplicar, apóyate en estas claves:
| Operación | Clave | Si repites la llamada |
|---|---|---|
PUT /drafts | meta.external_ref, obligatorio | Actualiza el borrador con esa referencia en lugar de crear otro. Si esa factura ya está emitida, 409 con invoice_update_not_allowed. |
POST /issued-invoices/{invoice_id}/issue | El invoice_id | No emite dos veces: la segunda llamada responde 409 con invalid_status_transition, también si envía invoice_number. |
POST /issued-invoices/import | Serie e invoice_number | Actualiza la factura importada. Si ya está incluida en un modelo presentado, 409. |
PUT /received-invoices | NIF del proveedor, número y año de la fecha de expedición | Actualiza la recibida existente. Si sus datos fiscales ya no admiten cambios, 409 con received_invoice_fiscally_locked. |
- En
PUT /drafts, sinmeta.external_refla API responde422conmissing_required_fields. Para actualizar un borrador concreto, añade?invoice_id=a la ruta. - Genera
meta.external_refdesde tu sistema, por ejemplo con el identificador del pedido, y guárdalo junto alidque devuelve la API. - Tras un timeout, consulta el estado antes de repetir una emisión:
GET /issued-invoices/{invoice_id}. - Los lotes (
POST /drafts/batch,POST /issued-invoices/import/batch) devuelvenaccepted,rejectedy el resultado de cada factura enitems.