Documentación de la API
Referencia completa: autenticación, endpoints, el objeto registro campo a campo, los estados de la remisión, el catálogo de errores de la AEAT y las diferencias entre preproducción y producción.
¿Cómo se integra VeriFactu por API?
Envías los datos de la factura a POST /v1/registros con tu clave en la cabecera Authorization. Recibes al instante la huella y la URL del QR para imprimir, y el resultado de la remisión a la AEAT te llega después por webhook o consultando el registro.
La remisión no es síncrona a propósito: la AEAT impone un control de flujo y hay que serializar por emisor para que la cadena de huellas no se bifurque.
Una clave por entorno. Las claves de prueba y las de producción no se pueden mezclar ni por accidente: apuntan a servicios distintos de la AEAT.
# Toda petición lleva la clave en la cabecera Authorization. # Las claves sk_test_ pegan contra preproducción de la AEAT; # las sk_live_ contra producción. Nunca se mezclan. Authorization: Bearer sk_test_9f2Kq... Content-Type: application/json Idempotency-Key: F2026-0184
| Método y ruta | Qué hace |
|---|---|
POST /v1/registros | Da de alta una factura y la encola para remisión. Devuelve huella y QR al instante. |
GET /v1/registros/{id} | Estado actual de un registro, con el CSV si ya fue aceptado. |
GET /v1/registros | Lista paginada. Filtros por emisor, estado, desde y hasta. |
POST /v1/registros/{id}/anulacion | Emite el registro de anulación. No borra: encadena un registro nuevo. |
GET /v1/registros/{id}/xml | El XML exacto que se remitió a la AEAT, para auditoría. |
GET /v1/registros/{id}/cadena | La cadena literal sometida a SHA-256. Lo primero que hay que mirar ante un rechazo. |
POST /v1/emisores | Alta de un NIF emisor, con su modalidad de remisión. |
GET /v1/emisores | Lista de emisores y su estado ante la AEAT. |
POST /v1/webhooks | Registra una URL de destino y devuelve el secreto de firma. |
GET /v1/exportacion | Registros y eventos en el XML estandarizado del reglamento. |
https://api.verifactu.co/v1todas las rutas cuelgan de aquí{
"id": "reg_9fK2mQ",
"objeto": "registro",
"emisor": { "nif": "B12345678", "nombre": "PELUQUERIA DEMO SL" },
"numero": "F2026/0184",
"fecha": "2026-08-26",
"tipo": "F1",
"impuesto": "IGIC",
"base": 100.00,
"cuota": 7.00,
"total": 107.00,
// Calculado por el motor — no se envía en la petición
"huella": "3C464DAF61ACB827C65FDA19F352A4E3BDC2C640E9E9FC4CC058073F38F12F60",
"huella_anterior": "A91C...E4",
"generado_en": "2026-08-26T10:15:00+01:00",
"qr": "https://www2.agenciatributaria.gob.es/wlpl/TIKE-CONT/ValidarQR?nif=...",
// Resultado de la remisión
"estado": "aceptado",
"csv": "A1B2C3D4E5F6",
"aeat": {
"estado_envio": "Correcto",
"codigo": null,
"descripcion": null
},
"intentos": 1,
"creado_en": "2026-08-26T10:15:00+01:00"
}| Campo | Qué es |
|---|---|
tipo | F1 completa · F2 simplificada · F3 · R1 a R5 rectificativas. |
impuesto | IVA, IGIC o IPSI. |
huella | SHA-256 en hexadecimal MAYÚSCULA del registro. |
huella_anterior | La del registro previo del mismo emisor. Vacía en el primero. |
generado_en | ISO-8601 con huso horario. Entra en la huella y se conserva: el XML declara el mismo instante. |
estado | pendiente · enviando · aceptado · aceptado_con_errores · rechazado · error. |
csv | Código Seguro de Verificación de la AEAT. Solo si el estado es aceptado. |
aeat.estado_envio | Literal de la AEAT: Correcto, AceptadoConErrores o Incorrecto. |
La distinción que gobierna todo: un rechazo de contenido no se arregla reintentando —solo esconde el problema— mientras que un fallo transitorio hay que reintentarlo o la factura se queda colgada sin que nadie se entere.
| Estado | Qué significa |
|---|---|
pendiente | Registro creado y en cola. Ya tienes huella y QR: puedes imprimir la factura. |
enviando | En curso hacia la AEAT. |
aceptado | La AEAT lo aceptó. Hay CSV. |
aceptado_con_errores | Aceptado pero con avisos. Suele ser el formato de importes: la AEAT recalculó la huella desde el XML y no le cuadró. |
rechazado | Rechazo de contenido. No se reintenta. Alguien tiene que corregir un dato. |
error | Agotados los seis reintentos de un fallo transitorio. Requiere revisión. |
# 422 Unprocessable Entity { "error": { "tipo": "rechazo_aeat", "codigo": "4104", "titulo": "El nombre del emisor no coincide con el censo", "accion": "Usa la razón social exacta que figura en Hacienda...", "quien": "emisor", "aeat": "<texto literal devuelto por la AEAT>", "doc": "https://verifactu.co/errores-aeat/4104/" } }
Nunca ocultamos el mensaje original de la AEAT: va siempre en error.aeat.
Para un código que no tenemos catalogado mostramos su texto literal en vez de inventar
una explicación.
| Preproducción | Producción | |
|---|---|---|
| Clave | sk_test_… | sk_live_… |
| Servicio de remisión | prewww1.aeat.es | www1.agenciatributaria.gob.es |
| Certificado de sello | prewww10.aeat.es | www10.agenciatributaria.gob.es |
| Validador del QR | prewww2.aeat.es | www2.agenciatributaria.gob.es |
La especificación es la fuente de la verdad, y la colección de Postman se genera a partir de ella: no pueden divergir. Si prefieres un cliente generado en tu lenguaje, genéralo desde el YAML en vez de esperar a que publiquemos un SDK.
| Fichero | Qué es |
|---|---|
| verifactu-v1.yaml | Especificación OpenAPI 3.1 completa: 10 operaciones, esquemas, ejemplos y catálogo de errores. |
| verifactu.postman_collection.json | Colección de Postman v2.1, ordenada según el quickstart. Guarda sola el id del registro entre peticiones. |
api_key y ejecuta la carpeta «Empezar aquí» de arriba abajo. Es el mismo recorrido que el quickstart.¿Hay OpenAPI y colección de Postman?
Sí, las dos se descargan desde esta misma página. La especificación es OpenAPI 3.1 y la colección de Postman se genera a partir de ella, así que no pueden divergir. Cada cambio queda anotado en el changelog.
¿Cómo se versiona la API?
La versión va en la ruta (/v1/). Dentro de una versión solo añadimos campos: nunca quitamos ni renombramos uno existente. Un cambio incompatible sería /v2/ y convivirían.
¿Hay límites de llamadas?
Sí, por clave, y la respuesta los declara en las cabeceras X-RateLimit. Pero el límite que de verdad manda no es el nuestro: es el TiempoEsperaEnvio que impone la AEAT en su control de flujo, y ese lo gestionamos nosotros por ti.
¿Puedo recuperar el XML exacto que se envió?
Sí, en GET /v1/registros/{id}/xml. Y la cadena literal que se sometió a SHA-256 en /cadena. Son las dos cosas que hay que poder mirar cuando algo no cuadra.
Última revisión normativa: Especificaciones de huella AEAT v0.1.2 Revisado por el equipo técnico de verifactu.co