Documentación de la API

Referencia de inicio rápido. Ejemplos de cada endpoint — sin entrar en el detalle interno.

Autenticación. Todas las peticiones usan tu API key como Bearer token:
Authorization: Bearer eck_live_xxxxxxxxxxxx
Content-Type: application/json
Base URL única: https://api.apifactu.es. El entorno lo decide la clave: eck_live_ factura de verdad, eck_test_ usa el entorno de pruebas sin efecto fiscal.

Guías por plataforma

Si tu tienda está en una de estas plataformas no necesitas programar: instala el plugin y pega tu clave. Si integras desde tu propio código, usa una de las librerías o la API REST de más abajo.

WooCommercePlugin para WordPress · disponible PrestaShopMódulo para 1.7 y 8 · disponible ShopifyApp alojada · próximamente PythonLibrería apifactu · disponible JavaScript / TypeScriptLibrería @apifactu/sdk · disponible MagentoExtensión · próximamente OpenCartExtensión · próximamente OdooMódulo · próximamente

Emitir una factura

POST/v1/invoices

Crea y envía una factura a Verifactu/TicketBai. Idempotente con X-Idempotency-Key.

# Petición
POST /v1/invoices
X-Idempotency-Key: WOO-1042
{
  "referencia": "WOO-1042",
  "serie": "A",
  "tipo_factura": "F1",
  "base_imponible": 100.00,
  "tipo_iva": 21,
  "nif_receptor": "12345678Z",
  "nombre_receptor": "Cliente SL"
}

# Respuesta 202
{
  "id": "d4e1…",
  "status": "QUEUED",
  "numero_completo": "A-2026-0042",
  "total": "121.00",
  "qr_url": "https://…",
  "huella": "2B8BD34D…",
  "fiscal_invoice_id": "8f3a1c…"
}

Guarda el id: es el que necesitan anular y rectificar. Al repetir la misma petición se devuelve exactamente esta respuesta, con "idempotent": true.

Consultar facturas

GET/v1/invoices

Listado paginado con filtros (status, serie, fecha_desde, fecha_hasta, receptor_nif).

# Petición
GET /v1/invoices?status=ACCEPTED&page=1&per_page=20

# Respuesta 200
{
  "data": [ { "id": "…", "numero_completo": "A-2026-0042",
              "status": "ACCEPTED", "total": "121.00",
              "qr_url": "https://…", "aeat_csv": "AEAT-…" } ],
  "pagination": { "page": 1, "per_page": 20, "total": 87, "pages": 5 }
}
GET/v1/invoices/stats

Conteos por estado y totalizaciones: importe facturado, anulado y rechazado, por serie y por mes. Acotable con fecha_desde, fecha_hasta (fecha de expedición) y serie: lo que necesita un cierre de caja o de trimestre.

# Petición
GET /v1/invoices/stats?fecha_desde=2026-01-01&fecha_hasta=2026-03-31

# Respuesta 200
{ "total": 87, "accepted": 80, "rejected": 2, "cancelled": 5,
  "totales": { "facturas_validas": 80, "importe_facturado": "9640.20",
               "facturas_anuladas": 5, "importe_anulado": "412.00",
               "facturas_rechazadas": 2, "importe_rechazado": "99.00" },
  "por_serie": [ { "serie": "A", "facturas": 85, "importe_facturado": "9640.20", "importe_anulado": "412.00" } ],
  "por_mes":   [ { "mes": "2026-01", "facturas": 30, "importe_facturado": "3100.00", "importe_anulado": "0.00" }, … ] }
GET/v1/invoices/{id}

Detalle de una factura: estado fiscal, QR, hash y CSV de la AEAT. En TicketBAI incluye además identificador_tbai (el código TBAI-… que debe imprimirse en texto en la factura) y qr_legend (la leyenda que acompaña al QR).

# Respuesta 200
{ "id": "…", "numero_completo": "A-2026-0042", "status": "ACCEPTED",
  "qr_url": "https://www.agenciatributaria.es/…", "aeat_csv": "AEAT-…",
  "huella": "A3F2…" }
GET/v1/invoices/{id}/qr

Imagen PNG del QR de verificación fiscal, lista para imprimir o incrustar.

# Respuesta 200  Content-Type: image/png  (binario)

Anular y rectificar

POST/v1/invoices/{id}/cancel

Anula una factura: genera el registro de anulación y lo remite a Hacienda. Si la factura todavía no se había enviado, se cancela sin registro.

# Petición
{ "motivo": "pedido cancelado" }

# Respuesta 202
{ "id": "…", "status": "CANCELLATION_PENDING", "submission_status": "CANCELLATION_PENDING" }
POST/v1/invoices/{id}/rectify

Emite una factura rectificativa sobre {id}: S sustituye la original, I rectifica por diferencias (importes negativos para devoluciones). Con varios tipos de IVA, indica lineas.

# Petición
{ "tipo_rectificativa": "I", "amount": -20.00, "motivo": "devolución parcial" }

# Respuesta 202
{ "id": "…", "numero_completo": "R-2026-0007", "tipo_rectificativa": "I",
  "rectifies_numero_completo": "A-2026-0042", "status": "QUEUED" }

Corregir ante la Hacienda Foral de Gipuzkoa (Zuzendu y Osatu)

Solo para facturas bajo TicketBAI Gipuzkoa. La Diputación no admite reenviar un fichero rechazado por el servicio de alta: se corrige por el servicio Zuzendu (Orden Foral 16/2022). Fuera de Gipuzkoa estos endpoints responden 409.

POST/v1/invoices/{id}/zuzendu

SUBSANAR una factura REJECTED (pasa a ACCEPTED si la Diputación la recibe) o MODIFICAR una ACCEPTED con datos erróneos que no exijan rectificativa. Se pueden corregir descripcion, nif_receptor, nombre_receptor, direccion_receptor y codigo_postal_receptor; los importes y la numeración, no. Respuesta síncrona.

# Petición
{ "accion": "SUBSANAR",
  "correcciones": { "direccion_receptor": "Kale Nagusia 1", "codigo_postal_receptor": "20001" } }

# Respuesta 200
{ "id": "…", "status": "ACCEPTED", "submission_status": "ACCEPTED",
  "zuzendu": { "servicio": "ZUZENDU_ALTA", "accion": "SUBSANAR", "estado": "RECIBIDO",
               "codigo": null, "descripcion": null, "csv": "TBAI-…" } }

estado: RECIBIDO, RECIBIDO_CON_ERRORES (avisos de consolidación en codigo/descripcion), RECHAZADO o ERROR (no hubo respuesta: reintenta).

POST/v1/invoices/{id}/zuzendu-anulacion

Subsana un fichero de anulación rechazado (factura en CANCELLATION_REJECTED). Si la Diputación lo recibe, la factura pasa a CANCELLED. Sin cuerpo.

POST/v1/invoices/{id}/osatu

Complementa una factura ACCEPTED con otros datos tributarios que el fichero de alta no lleva: referencia_externa, inmuebles[] (situacion 1-4, referencia_catastral), importe_transmision_inmuebles_sujeto_iva, nif_representante, nif_representante_contraparte, num_registro_acuerdo_facturacion, entidad_sucedida (nif, nombre), cupon, hidrocarburos_extraidos_deposito.

# Petición
{ "otros_datos_tributarios": { "inmuebles": [ { "situacion": "2", "referencia_catastral": "…" } ] } }
GET/v1/invoices/{id}/zuzendu

Histórico de envíos Zuzendu y Osatu de la factura. disponible es false fuera de Gipuzkoa.

POST/v1/ticketbai/consulta

KONTSULTA: lo que la Diputación tiene registrado del emisor (solo lectura). ejercicio, periodo (01…12, 1T…4T, 0A) y un filtro opcional por serie, número, fechas de expedición/operación/presentación, estado_envio (3 aceptado, 2 con errores), estado_registro (C correcto, A anulada, R rechazada, M modificada, Z rectificada), mostrar_errores y pagina.

# Petición
{ "ejercicio": 2026, "periodo": "1T", "filtro": { "estado_registro": "C", "pagina": 1 } }

# Respuesta 200
{ "resultado": { "existen_registros": true, "registros_totales": 42, "paginas_totales": 1, "error": null },
  "facturas": [ { "serie": "A-2026", "numero": "7", "fecha_hora_expedicion": "15-02-2026 10:12:03",
                  "importe_total": "121.00", "identificador_tbai": "TBAI-…", "estado": "C",
                  "errores": { "ticketbai": [], "sii": [] } } ] }
GET/v1/ticketbai/consulta/ultima

Última factura del emisor registrada en Gipuzkoa (ultima_factura), útil para comprobar el encadenamiento tras una incidencia.

Todas estas operaciones están también en el panel del comerciante (Actividad → detalle de la factura y «Consulta en la Diputación»), que usa esta misma API.

Webhooks

POST/v1/webhooks

Registra una URL para recibir el estado fiscal. Cada entrega va firmada con HMAC-SHA256 en X-Fiscal-Signature.

# Petición
POST /v1/webhooks
{ "url": "https://mitienda.es/webhook",
  "events": ["invoice.accepted", "invoice.rejected"] }

# Evento entregado
{ "event": "invoice.accepted", "numero_completo": "A-2026-0042",
  "submission_status": "ACCEPTED", "qr_url": "https://…", "aeat_csv": "AEAT-…" }

API keys

GET/v1/keys

Lista tus API keys (prefijo enmascarado; el secreto solo se muestra al crearlas).

# Respuesta 200
[ { "id": "…", "key_prefix": "eck_live_a1…", "environment": "live", "is_active": true } ]

Errores y reintentos

CódigoSignificadoAcción del cliente
202Aceptada y encoladaEspera el webhook de confirmación
422Validación (p.ej. tipo de IVA inválido)Corrige el payload — no reintentes
429Límite de tasaReintenta respetando Retry-After
5xxError transitorioReintenta con backoff exponencial

Las librerías oficiales de Python y JavaScript/TypeScript gestionan los reintentos y la idempotencia por ti.

Crear cuenta y obtener API key →