Estat: preview per a integradors. L'API funciona i està provada, i els preus i el repartiment de responsabilitats són a la pàgina del Motor API. L'alta encara no té botó: es fa parlant. Escriu-nos a hola@factuza.com i et donem accés a l'entorn de proves el mateix dia.
Això no és una llista d'intencions: cada exemple d'aquesta pàgina s'executa en cada passada de la bateria de proves (cas CU-K04). Si alguna cosa deixés de funcionar, la prova es posa vermella abans que ho descobreixis tu.
En aquesta pàgina
1. Per a qui és això
Per a programari que ja gestiona un negoci —un taller, una clínica, una acadèmia, un ERP vertical— i necessita que les seves factures compleixin VeriFactu sense reescriure la part fiscal. El teu sistema continua sent el que mana; Factuza hi posa la numeració, l'empremta encadenada, el PDF i la remissió.
El que no és: una passarel·la de signatura ni un simple generador de PDF. Cada factura que emets per aquí neix amb el seu registre de facturació, entra a la cadena d'empremtes i es remet a l'Agència Tributària igual que si l'haguessis emès des del web.
Una factura emesa no s'esborra. Entra a la cadena d'empremtes i allà es queda. El que existeix és anul·lar i rectificar, que són dues operacions diferents i totes dues deixen rastre. Prova a l'entorn de proves abans d'emetre de debò.
2. Autenticació: la clau d'API
Tota l'API accepta dues credencials: el testimoni d'una persona (el que fan servir el web i l'app) o una clau d'API, que és la d'una màquina. Per integrar, la teva és la segona.
Una clau es crea des de l'àrea de clients, a Claus d'API, amb el teu usuari d'administrador, i viatja en una capçalera:
X-Api-Key: fzk_x8Kq2vN...En crear-la tries què pot fer, i la diferència importa:
- Només emetre (el normal). Emet, anul·la i consulta dels obligats que ja estiguin donats d'alta.
- Emetre i donar d'alta obligats. A més pot incorporar clients nous amb
POST /emisores, que és el que necessites si el teu programa els dona d'alta pel seu compte.
Si la teva integració no incorpora clients, fes servir la primera: una clau que pot menys fa menys mal el dia que es filtri. I no es pot ampliar sobre la marxa — se'n crea una altra expressament.
Tres regles que convé saber abans
- La clau es veu una vegada. En crear-la te l'ensenyem sencera i no torna a aparèixer: a la nostra base només en queda l'empremta SHA-256. Si la perds, es revoca i se'n crea una altra — no hi ha «recuperar», i és a propòsit.
- Una màquina no gestiona màquines. Amb una clau no es poden crear ni revocar claus. Si una es filtrés, qui la tingui no es pot fabricar successores ni esborrar el rastre.
- Revocar és immediat. La petició següent que faci servir aquella clau rep un
401. No hi ha memòria cau ni finestra de gràcia.
La clau hereta els permisos del teu compte: els seus emissors, el seu pla i la seva llicència. No pot emetre a nom d'un NIF que no sigui el teu, i si la teva llicència caduca deixa d'emetre igual que el web — però continua podent consultar i descarregar el ja emès, perquè els teus llibres són teus.
3. La teva primera factura, pas a pas
Cinc crides. Totes amb la mateixa capçalera i cap amb testimoni d'usuari. L'exemple fa servir l'entorn de proves, on res no compta ni es remet a l'AEAT.
01Qui sóc?
Abans de res, amb quins emissors pot treballar la teva clau. D'aquí surten el NIF i el nom que van a la factura: no els teclegis, llegeix-los.
curl https://func-factuza-test-obt1.azurewebsites.net/api/emisores \ -H "X-Api-Key: $FACTUZA_CLAVE" { "emisores": [ { "emisorId": 2, "nif": "B87654323", "nombreRazon": "Pruebas Automaticas SL" } ], "plan": "PYME", "tope": 1, "usados": 1, "disponibles": 0 }
02La teva sèrie
El número de factura el posa el servidor, dins de la mateixa transacció que l'emet. Tu envies el serieId, no el número: teclejar-lo des de fora deixa la correlativitat en mans que ningú no s'equivoqui, i un forat a la numeració s'ha de justificar davant d'Hisenda.
curl https://func-factuza-test-obt1.azurewebsites.net/api/series \ -H "X-Api-Key: $FACTUZA_CLAVE" { "series": [ { "serieId": 1, "codigo": "PRU", "activa": true, "siguienteNumero": "PRU-2026/0457" } ], "total": 1 }
03Emetre
El cos mínim d'una factura normal (F1) amb una sola base i un sol tipus. Per a diverses línies o tractaments especials d'IVA, fes servir POST /facturas/detallada.
curl -X POST https://func-factuza-test-obt1.azurewebsites.net/api/facturas \ -H "X-Api-Key: $FACTUZA_CLAVE" \ -H "Content-Type: application/json" \ -d '{ "nifEmisor": "B87654323", "nombreEmisor": "Pruebas Automaticas SL", "numSerieFactura": "", // lo pone la serie "serieId": 1, "fechaExpedicion": "2026-08-23", "tipoFactura": "F1", "descripcion": "Primera factura por API", "nifDestinatario": "B12345674", "nombreDestinatario": "Cliente de pruebas SL", "baseImponible": 100, "tipoImpositivo": 21 }'
Respon 201 —s'ha creat un recurs, no és un 200— amb el número que ha posat la sèrie i l'empremta:
{ "facturaId": 457,
"numSerieFactura": "PRU-2026/0457",
"huella": "9F2A…64 caracteres en hexadecimal" }Sense empremta no és VeriFactu. Són 64 caràcters: el SHA-256 del registre, encadenat amb el de la factura anterior.
04El PDF
Amb el seu QR de comprovació, a punt per enviar-lo al client.
curl https://func-factuza-test-obt1.azurewebsites.net/api/facturas/457/pdf \ -H "X-Api-Key: $FACTUZA_CLAVE" -o factura.pdf
05Com vas de consum
Aquesta ruta sí que la pot cridar una màquina, al revés que les de gestió de claus: negar-te saber per on vas i després cobrar-te l'excés seria parar-te un parany.
curl https://func-factuza-test-obt1.azurewebsites.net/api/claves-api/consumo \ -H "X-Api-Key: $FACTUZA_CLAVE" { "desde": "2026-08-01…", "hasta": "2026-09-01…", "porEmisor": [ { "nif": "B87654323", "registros": 12, "incluidos": 3000, "restantes": 2988, "exceso": 0, "eurosDeExceso": 0 } ], "totalRegistros": 12, "peticionesPorMinuto": 120 }
4. Els quatre fluxos que importen
Emetre
POST /facturas per al cas simple, POST /facturas/detallada quan hi ha diverses línies, descomptes, o tractaments d'IVA diferents del normal (exempta, inversió del subjecte passiu, no subjecta per localització, suplert). Sense destinatari surt una simplificada (F2), que no dona dret a deduir l'IVA a qui la rep.
Anul·lar i rectificar — no són el mateix
POST /facturas/{id}/anulardeixa la factura anul·lada, no esborrada: genera el seu propi registre d'anul·lació, que també s'encadena i es remet.POST /facturas/{id}/rectificarcrea una factura nova que apunta a l'original, i l'original continua existint. És el que cal fer quan l'import o les dades estaven malament.
Una factura ja cobrada no s'anul·la. És una regla del motor, no de la interfície.
Despeses
POST /gastos dona d'alta una despesa deduïble, que queda pendent fins que algú la dona per bona amb POST /gastos/{id}/validar. Aquella aturada és deliberada: una despesa mal llegida que entra sola a la comptabilitat no es nota fins que arriba el trimestre.
Si tens la foto o el PDF i no les dades, POST /ocr/analizar retorna un esborrany amb el que ha llegit i una llista de què no es fia d'ell mateix. Mai no crea la despesa: la proposa.
Exportació legal
GET /exportar/registros?desde=&hasta= retorna els registres de facturació en el format estandarditzat de l'article 10 del RD 1007/2023: un XML per registre, la cadena d'empremtes en CSV i un manifest per poder recalcular-les i comprovar que ningú no ha tocat res. És el que cal poder lliurar en una inspecció.
GET /exportar/gestoria és una altra cosa diferent: el paquet còmode per a l'assessor, amb PDF i CSV.
Assabentar-te del que diu l'AEAT (webhook)
Quan emets, el 201 arriba en mil·lisegons, però el que et diu és «recollit i encuat». L'AEAT contesta després, i aquesta resposta —acceptat, acceptat amb errors, o rebutjat amb un codi— és la que decideix si la factura val. Pots consultar-la amb GET /facturas/{id}, però sondejar-les una a una és car i a la pràctica no ho fa ningú: els rebuigs es descobreixen setmanes més tard, quan ja n'has emès cent més amb el mateix error.
Així que digues-nos on avisar-te. Des d'Àrea → Claus d'API → Webhook, o per la mateixa API:
PUT /webhook
{ "url": "https://api.tu-erp.es/factuza/avisos" }
→ 200 { "secreto": "whsec_…", "cabecera": "X-Factuza-Firma" }
El secret es mostra una sola vegada. Desa'l: és amb el que comproves que l'avís ve de nosaltres. Si el perds, tornes a desar la URL i se'n genera un altre (canviar la URL sempre renova el secret, perquè el que signava cap al teu servidor anterior deixi de valer).
Cada avís arriba com un POST amb aquest cos:
{ "evento": "registro.remitido",
"registroId": 48211,
"numSerieFactura": "FA2026-0117",
"nifEmisor": "B12345674",
"resultado": "RECHAZADO",
"codigoError": "1103",
"descripcionError": "El NIF del destinatario no está identificado",
"csv": null,
"ocurridoUtc": "2026-08-25T09:14:03Z" }
resultado és ACEPTADO, ACEPTADO_ERRORES o RECHAZADO. Només avisem d'estats definitius: si l'AEAT no està disponible i cal reintentar, això és cosa nostra i no t'ho expliquem.
Com es comprova la signatura
La capçalera X-Factuza-Firma arriba així: t=1756112043,v1=<hex>. El v1 és l'HMAC-SHA256 de la cadena «<t>.<cos tal com va arribar>» amb el teu secret.
firmado = t + "." + cuerpoCrudo
esperado = hmac_sha256(secreto, firmado).hex()
valido = comparacionEnTiempoConstante(esperado, v1) and (ahora - t) < 300
Compara el temps també. La marca t va dins del que se signa precisament per això: sense comprovar-la, qualsevol que intercepti un avís te'l pot reenviar mesos després i la signatura continuarà quadrant. Rebutja el que arribi amb més de cinc minuts.
I fes servir el cos cru, abans d'analitzar el JSON: si el reserialitzes, l'ordre de les claus o els espais poden canviar i la signatura deixarà de coincidir.
Reintents i duplicats
Si el teu servidor no contesta un 2xx en deu segons, reintentem a 1, 5, 15, 60, 180, 360 i 720 minuts, i ens rendim al vuitè intent —unes vint hores—. A l'àrea veuràs cada lliurament i per què va fallar.
Cada reintent porta la mateixa capçalera X-Factuza-Entrega, així que si vas rebre l'avís però vas caure abans de contestar, el reconeixeràs per aquest número i no el processaràs dues vegades. Contesta 200 primer i fes la feina després: el que triguis dins de la petició compta contra aquests deu segons.
Dues coses que rebutgem i convé saber per endavant: la URL ha de ser https:// i apuntar a un servidor públic —un avís porta el NIF de l'obligat a dins, i comprovem l'adreça just abans de trucar—, i no seguim redireccions: si contestes un 302 ho comptem com a fallada. Apunta el webhook a la URL final.
5. Quotes i límits
| Límit | Quant | Què passa en passar-se'n |
|---|---|---|
| Registres per emissor i mes | 3.000 inclosos | Res no es bloqueja. L'excés es factura a 2 € per cada 1.000. |
| Peticions per minut | 120 | 429 amb capçalera Retry-After. |
L'asimetria és deliberada i val la pena entendre-la: un topall mensual que tallés l'emissió deixaria un obligat tributari sense poder complir la llei per un assumpte comercial nostre. Emetre una factura no és un caprici, té data. Així que el topall mensual avisa i es cobra.
El límit per minut sí que talla, i per un motiu diferent: un bucle trencat en una integració no és l'obligació legal de ningú — és una avaria que, sense fre, s'enduu per davant el servei dels altres. El 429 a més t'avisa que tens una fallada.
6. Errors
| Codi | Què significa |
|---|---|
400 | El cos no val. El missatge diu quin camp i per què, en castellà. |
401 | Sense credencial vàlida: falta la capçalera, la clau no existeix o està revocada. |
403 | El teu compte no pot fer això: rol insuficient, llicència no vigent, o un emissor que no és teu. |
404 | No existeix — o no és teu. Demanar el recurs d'un altre compte respon igual que demanar-ne un que no existeix, a propòsit. |
409 | Conflicte: ja existeix, o l'estat no permet l'operació (anul·lar una factura cobrada). |
429 | Massa peticions per minut. Espera el que digui Retry-After. |
Els errors porten un cos JSON amb error i, quan ajuda, un detalle. Estan escrits perquè s'entenguin sense conèixer les nostres entranyes: «No puedes emitir facturas a nombre de X. Tu cuenta emite como Y» diu què passa i com s'arregla.
7. Catàleg de rutes
El més utilitzat. Totes pengen de https://func-factuza-prod-obt1.azurewebsites.net/api en producció i de …-test-… en proves.
| Verb | Ruta | Què fa |
|---|---|---|
| get | /emisores | Amb quins emissors treballa la teva clau |
| get | /series | Les teves sèries i el número següent |
| post | /facturas | Emetre (base i tipus únics) |
| post | /facturas/detallada | Emetre amb línies i tractaments d'IVA |
| get | /facturas | Llistat, amb filtres |
| get | /facturas/{id}/pdf | El PDF amb el seu QR |
| post | /facturas/{id}/anular | Anul·lar (no esborra) |
| post | /facturas/{id}/rectificar | Rectificar (en crea una altra) |
| post | /facturas/{id}/email | Enviar-la per correu |
| get | /gastos | Despeses del període |
| post | /gastos | Alta de despesa (queda pendent) |
| post | /gastos/{id}/validar | Donar-la per bona |
| post | /ocr/analizar | Llegir una foto o un PDF (proposa, no crea) |
| get | /maestras/destinatarios | Els teus clients |
| get | /resumen | Facturat, trimestre i pendent de cobrament |
| get | /modelos/{modelo} | Esborrany del 303, 130, 390… |
| get | /exportar/registros | Exportació legal de l'art. 10 |
| get | /claves-api/consumo | El teu consum del mes |
| get | /webhook | On t'avisem i com van anar els últims lliuraments |
| put | /webhook | Posar o canviar la URL (torna el secret un cop) |
| delete | /webhook | Deixar d'avisar |
Hi ha més de seixanta rutes en total —pressupostos, recurrents, cobraments, sèries, usuaris—. Si te'n falta alguna, escriu-nos i et diem si existeix.
8. Descarregar l'especificació
El catàleg complet —62 rutes, 82 operacions— en OpenAPI 3.0, per carregar-lo a Postman, Insomnia o el generador de clients que facis servir:
Descarregar factuza-api.yaml versió 1.0.0 · 25-ago-2026
Es genera del codi, no s'escriu a mà. Les rutes surten del mateix motor i les descripcions del comentari que cadascuna té al costat, i hi ha una prova que posa la suite en vermell si publiquem una API amb rutes que no són al fitxer. És la garantia que el que et descarregues és el que hi ha servit.
Ho diem perquè aquí hi havia abans un fitxer escrit a mà que descrivia 19 rutes quan el motor ja en servia 62, amb noms que no existien. Es va retirar.
Dues coses que l'especificació encara no porta, dites abans que les trobis a faltar: els esquemes dels cossos de petició i resposta —que són en aquesta pàgina, amb exemples que s'executen a cada passada de la suite— i la col·lecció de Postman.
9. Compromís de versió
- No treiem ni reanomenem camps d'una resposta sense avisar amb 90 dies. Afegir camps nous sí que pot passar en qualsevol moment: el teu client ha d'ignorar els que no conegui.
- No canviem el significat d'un camp existent. Si alguna cosa ha de significar una altra cosa, serà un camp nou.
- Els codis d'estat són part del contracte. Si avui una operació respon
201, continuarà responent201. - Els canvis que trenquen s'anuncien per correu als comptes amb clau viva, no només en una pàgina que cal anar a mirar.