Factuarea APIDevelopers

Facturació des de terminals desatesos

Emet una factura simplificada ja cobrada des d'un terminal d'autoservei o una màquina expenedora en UNA sola crida idempotent, imprimeix el QR VERI*FACTU que retorna i sap què fer quan una crida falla, es repeteix o es rebutja.

Un terminal d'autoservei o una màquina expenedora venen, cobren i imprimeixen en segons, sense ningú que corregeixi un error. Aquesta guia explica com un terminal així fa servir POST /v1/invoices per crear, emetre, cobrar i registrar una factura simplificada en una sola crida, i què rep per poder imprimir-la.

L'arquitectura és la que l'AEAT admet per als terminals: un terminal més un sistema central que genera el registre de facturació i el retorna, de manera que el terminal imprimeix la factura amb el seu QR (FAQ de desenvolupadors de l'AEAT de 4 de desembre de 2025, secció 5). Factuarea és aquest sistema central. Genera, numera, encadena, signa quan el mode ho exigeix i remet tots els registres; el terminal no fa res d'això.

El programari que corre al terminal té obligacions pròpies, entre elles una declaració pròpia. Són a Compliment del component de l'integrador.

Quan aplica

A una empresa que emet factures simplificades des d'un dispositiu sense operador i té això:

  • Factures simplificades habilitades per a l'empresa. Si no, type: F2 respon 422 simplified_invoices_disabled.
  • VeriFactu activat, en qualsevol dels dos modes. Comprova-ho amb GET /v1/verifactu/config. Sense ell no es pot generar l'alta i el bloc verifactu respon status: failed amb error_code: verifactu_not_enabled.
  • Una API key amb invoices:write (i verifactu:read per seguir el registre). Comença amb una clau fact_test_: els registres d'una empresa de prova mai no es remeten a l'AEAT i el seu QR apunta al servei de preproducció de l'AEAT.
  • Una sèrie de factures simplificades. No cal que la triïs: sense series_id el tiquet es numera a la teva sèrie de simplificades per defecte, que es crea sola la primera vegada. Si envies un series_id, ha de ser d'una sèrie de factures simplificades (invoice_kind: simplified); amb una sèrie de factures completes la crida respon 422 series_invoice_kind_mismatch i no queda cap esborrany. GET /v1/series/default?document_type=invoice&invoice_kind=simplified retorna la sèrie que s'usarà.

Una factura simplificada només val per a operacions de fins a 3.000 € IVA inclòs i mai per a operacions intracomunitàries, amb inversió del subjecte passiu o d'exportació. Consulta Factures simplificades o completes.

La crida

Tot va en un únic POST /v1/invoices. Aquests són els camps que el converteixen en un cobrament desatès:

CampQuè fa
type: "F2"Factura simplificada. Sense client_id és un tiquet anònim; amb un, una factura simplificada qualificada.
external_idLa identitat d'aquesta venda. No caduca mai: és el que fa segur un reintent tardà.
prices_include_tax: trueCada unit_price és el preu final que va pagar el client, amb IVA inclòs.
paymentRegistra el pagament (method; paid_at i reference opcionals) per tot l'import després d'emetre.
options.register_verifactu: trueGenera l'alta VERI*FACTU abans de respondre.
options.wait_for_pdf: trueEspera fins a uns 15 segons el PDF A4.
options.send_automatically i options.send_toEnvia la factura per email un cop emesa.
operation_onEl dia en què es va fer l'operació, quan difereix d'issued_on.
curl -X POST https://api.factuarea.com/v1/invoices \
  -H "Authorization: Bearer fact_test_3pXnR2VbY7TcA9eFmN5z8KqW" \
  -H "Content-Type: application/json" \
  -d '{
        "type": "F2",
        "issued_on": "2026-06-01",
        "due_on": "2026-06-01",
        "external_id": "KIOSK-0042-20260601-000187",
        "prices_include_tax": true,
        "lines": [
          { "description": "Rentat exprés", "quantity": 1, "unit_price": 5.00, "tax_rate": 21 }
        ],
        "payment": { "method": "credit_card", "reference": "TPV-8841-000187" },
        "options": { "register_verifactu": true, "wait_for_pdf": true }
      }'

Per ordre, el sistema crea l'esborrany, l'emet (número definitiu), registra el pagament, genera l'alta amb la seva huella i el seu QR, envia l'email si ho vas demanar i prepara el PDF. La resposta és la factura més tres blocs:

{
  "data": {
    "id": "019e5b7a-3c1d-7a52-b4e1-6f2d9c8a1e37",
    "type": "F2",
    "number": "S-2026-001",
    "status": "paid",
    "subtotal": 4.13,
    "total_vat": 0.87,
    "total": 5,
    "verifactu": {
      "status": "registered",
      "error_code": null,
      "aeat_status": "pending",
      "huella": "98F790A3765977E437FAEBCF1EF9B2B0B3462174D3D36375047185223BDE4308",
      "qr_url": "https://www2.agenciatributaria.gob.es/wlpl/TIKE-CONT/ValidarQR?nif=B12345678&numserie=S-2026-001&fecha=01-06-2026&importe=5.00",
      "qr_png_base64": "iVBORw0KGgo…",
      "legend": "VERI*FACTU",
      "csv": null
    },
    "pdf": {
      "status": "ready",
      "url": "https://app.factuarea.com/api/pdf/materialized?company=01931b3e-1111-7a2e-9a8b-3c5d6e7f8a01&expires=1780389734&type=invoice&uuid=019e5b7a-3c1d-7a52-b4e1-6f2d9c8a1e37&signature=67d5ef33797d2d164f32bd6ceedeeefd82ceed8cb2f04023db5a5e5c926fe085",
      "expires_at": "2026-06-02T10:42:14+02:00"
    },
    "public_url": "https://app.factuarea.com/d/019e5b7a-3c1d-7a52-b4e1-6f2d9c8a1e37"
  }
}

(El recurs de factura porta totes les seves claus habituals; aquí només es mostren les rellevants.) Els tres blocs apareixen només en un cobrament desatès, és a dir, en una petició amb type: F2, un bloc payment o options.register_verifactu. Els llistats, el detall i els webhooks mai no els porten.

  • verifactu és el que imprimeixes. status és registered quan l'alta existeix i failed quan la factura està emesa però no s'ha pogut generar l'alta. aeat_status és l'estat del registre davant l'AEAT: la transmissió a l'AEAT segueix el seu propi curs, per lots, després que hagis respost al client. csv és null fins que l'AEAT l'accepta.
  • pdf és el PDF A4. ready vol dir que està materialitzat i url el serveix a l'instant; pending vol dir que la generació és a la cua i la URL respon 404 durant uns segons. Mai no és un error.
  • public_url és la pàgina des de la qual el client descarrega la factura.

Preus amb IVA inclòs

Un terminal sap el que ha pagat el client, no la base neta. Amb prices_include_tax: true cada unit_price és el preu final i Factuarea calcula la base al cèntim perquè el total de la factura sigui igual a la suma dels imports que has enviat. Un tiquet de 5,00 € al 21 %, per exemple, es factura amb una base de 4,13 € més 0,87 € d'IVA, i el total és exactament els 5,00 € cobrats.

L'arrodoniment poques vegades permet un repartiment exacte: 0,60 € al 21 % no té cap base que doni 0,60 € com a línia solta. La regla és qualsevol línia, i partir:

  1. La línia amb la base més gran absorbeix els cèntims; si no pot, es prova cadascuna de les altres de més gran a més petita base, sense tocar mai les línies d'import zero.
  2. Si cap no pot, la línia més gran es parteix en dues: l'original, amb la seva quantitat, el seu descompte i els seus vincles, i una línia complementària d'una unitat amb la mateixa descripció i el mateix IVA, la base de la qual és la més petita possible (d'1 a 3 cèntims). El total continua sent exactament el que vas cobrar.
  3. Només es rebutja amb 422 amount_reconciliation_failed, sense emetre res, una diferència més gran que un cèntim per línia, que ja no és arrodoniment. Una retenció o un recàrrec d'equivalència la poden provocar.

En aquest mode no s'admeten línies de catàleg (product_id).

IVA d'una línia. La línia pren el tax_rate que envies, després l'impost al qual fa referència, després l'impost del seu producte i, si no n'hi ha cap, l'IVA per defecte de l'empresa per a factures. Si l'empresa tampoc no en té, la crida respon 422 missing_required_param amb error.param: lines.2.tax_rate i error.line_index: 2, l'índex de la línia que cal corregir, començant per zero. L'API mai no endevina un tipus. Els errors de domini que neixen d'una línia porten line_index de la mateixa manera (un preu absent, una causa d'exempció fora del seu catàleg…); és additiu, i param continua anomenant el camp.

Mostrar el QR

El QR és el QR tributari de l'AEAT: s'ha de mostrar com exigeix l'AEAT (Ordre HAC/1177/2024 i l'especificació del QR de l'AEAT). A la pràctica:

  • Mida i marge. Entre 30 × 30 mm i 40 × 40 mm, amb almenys 2 mm de marge en blanc al voltant. Factuarea imprimeix 30 mm als tiquets.
  • Nivell. Nivell de correcció d'errors M. qr_png_base64 ja és de nivell M i no porta prefix data:. Si la teva impressora necessita una altra resolució, escala la imatge per un factor enter o sense interpolació perquè els mòduls es mantinguin nítids.
  • Rètol i llegenda. El rètol QR tributario: va a sobre del codi i la legend que arriba a la resposta va a sota: VERI*FACTU en mode verificable, o la frase «Factura verificable en la sede electrónica de la AEAT» en l'altre. La llegenda fa servir una lletra no més petita que la de la resta de dades de la factura.
  • Lloc. Al principi del document, juntament amb les dades de la factura.
  • No l'alteris. Imprimeix qr_png_base64 i huella exactament com arriben. No els recalculis ni canviïs imports, número o data després de la resposta.

Si prefereixes no maquetar el rebut tu mateix, demana a Factuarea que te'l doni ja formatat per a un rotllo tèrmic:

curl "https://api.factuarea.com/v1/invoices/019e5b7a-3c1d-7a52-b4e1-6f2d9c8a1e37/pdf?format=ticket_80" \
  -H "Authorization: Bearer fact_live_3pXnR2VbY7TcA9eFmN5z8KqW" \
  --output ticket.pdf

format és a4 (el valor per defecte, amb la plantilla de l'empresa), ticket_80 (rotllo de 80 mm) o ticket_58 (rotllo de 58 mm). Un tiquet es maqueta per a una impressora de rebuts: QR de 30 mm, sense banda de capçalera ni peu, i una alçada ajustada al contingut. Cada format es genera i es desa a la memòria cau per separat, així que demanar un tiquet mai no canvia l'A4 i la URL signada de l'A4 continua funcionant. El tiquet porta el contingut obligatori d'una factura simplificada, la data de l'operació quan difereix i el destinatari quan la factura és qualificada. L'ETag canvia quan es crea l'alta, així que un PDF descarregat abans (sense QR) mai no es revalida com a vigent. Consulta GET /v1/invoices/{invoice}/pdf.

Reintents, repeticions i concurrència

La xarxa perdrà la resposta d'una crida que sí que va tenir èxit. Per a això hi ha external_id, que és independent de la capçalera Idempotency-Key: no caduca i es desa a la factura. Consulta Idempotència.

  • Reenvia la mateixa petició, amb el mateix external_id. Si la factura ja està emesa, l'API respon 200 amb Idempotent-Replayed: true i aquesta factura. Abans de respondre completa el que faltava: emet un esborrany que mai no es va emetre, registra el pagament si encara hi ha saldo i genera l'alta. No crea res de nou ni consumeix un altre número.
  • L'email no s'envia dues vegades. Una repetició no torna a enviar per email una factura l'email de la qual ja és a la cua o lliurat.
  • Una factura cancel·lada o anul·lada no rep pagament ni alta; la repetició només n'informa de l'estat.
  • Una altra venda, un altre external_id. Si el tipus o el total de la repetició difereixen de la factura emesa, la resposta és 409 idempotency_key_reused amb subcode: unattended_replay_mismatch i param: external_id. És un error en com construeixes els identificadors: no en reutilitzis mai cap.
  • Dues peticions simultànies amb el mateix external_id produeixen una factura, un número i un pagament. La segona espera fins a 20 segons la primera i després la repeteix. Si la primera no ha acabat, la segona respon 409 resource_locked amb param: external_id i no ha escrit res: envia la mateixa petició una altra vegada passats uns segons.

Tria un external_id per venda que sigui estable i únic a l'empresa, com idCaixer-data-seqüència (KIOSK-0042-20260601-000187), i desa'l abans de la primera crida.

Quan verifactu.status és failed

La factura està emesa i numerada, però no s'ha pogut generar la seva alta. verifactu.error_code diu per què: certificate_missing, certificate_expired, certificate_revoked, certificate_nif_mismatch, clock_drift_exceeded, representation_required, system_certificate_unavailable o verifactu_not_enabled.

El terminal no ha de lliurar encara el document com a factura vàlida: tracta la venda com a pendent, conserva el seu external_id, resol la causa (un certificat, una representació, l'activació de VeriFactu de l'empresa) i reenvia la mateixa petició. La repetició genera l'alta i respon 200. Consulta Alta automàtica a VeriFactu.

Errors que el terminal ha de tractar

RespostaSignificatQuè fer
200 + Idempotent-Replayed: trueLa venda ja estava emesa.Imprimeix el que arriba, si el primer intent no va imprimir.
409 idempotency_key_reused (unattended_replay_mismatch)L'external_id pertany a un altre tipus o total.Corregeix el generador d'identificadors. No reintentis.
409 resource_lockedUna altra petició amb el mateix external_id continua en curs.Envia la mateixa petició una altra vegada d'aquí a uns segons.
422 missing_required_param, param: options.send_toEs va demanar un email sense destinatari (sense client, o amb un client sense email).No s'ha creat res. Envia un send_to o no demanis l'email.
422 missing_required_param, param: lines.N.tax_rateUna línia no té IVA i l'empresa no en té un per defecte.Envia tax_rate o configura l'IVA per defecte.
422 simplified_invoices_disabled / simplified_invoice_not_allowedLes simplificades no estan habilitades, o l'import supera 3.000 € IVA inclòs.Habilita-les a l'empresa, o emet una factura completa (F1).
422 amount_reconciliation_failedEls imports amb IVA inclòs no poden sumar exactament.Revisa l'IVA, els descomptes, la retenció i el recàrrec de cada línia.
422 verifactu_not_eligibleAlguna cosa no cap al registre de l'AEAT, o l'empresa no pot signar.Consulta les dues taules següents. No s'emet res i no es consumeix número.
429 i 5xxLímit o fallada transitòria.Reintenta amb espera creixent, amb el mateix external_id.

422 verifactu_not_eligible: una dada no cap al registre

Abans de confirmar l'emissió, Factuarea comprova que l'alta que generarà la factura és vàlida per a l'AEAT. Si no ho és, la factura no s'emet, no consumeix número i te'n recuperes corregint la dada i repetint la crida. error.param la nomena, amb el vocabulari de l'API:

error.paramQuè no cap
client_idNom del client absent o de més de 120 caràcters; un NIF espanyol que no té 9 caràcters; una identificació estrangera de més de 20; un país que l'AEAT no admet; una factura completa sense client.
series_idEl número de la factura té més de 60 caràcters o caràcters que l'AEAT no admet (només ASCII imprimible, i ni ", ', <, > ni =).
original_invoice_idEl número de la factura rectificada no és admissible.
simplified_invoice_uuidsEl número d'una factura simplificada substituïda per una F3 no és admissible.
company_nameLa raó social de l'empresa té més de 120 caràcters. Una raó social absent és 422 business_rule_violation, amb aquest mateix param.
linesMés de 12 desglossaments fiscals diferents, o una base, quota o recàrrec que no cap en 12 xifres enteres i 2 decimals.
totalUn total, o la seva quota, que no cap al format; una F2 per sobre de 3.000 €.
typeLa marca de factura simplificada qualificada amb un tipus que no l'admet.

422 verifactu_not_eligible: l'empresa no pot signar

Només per a una empresa que té VeriFactu activat en mode NO VERI*FACTU i no té un certificat utilitzable. En aquest mode cada registre de facturació se signa, i un registre només compta com a generat quan està signat, així que la factura no s'emet ni s'anul·la. L'error porta subcode: signing_certificate_unavailable i el motiu a error.param:

error.paramQuè faltaQui ho resol
certificateEl certificat de l'empresa és absent, caducat, revocat, emès per a un altre NIF o il·legible.L'empresa: Configuració → Certificat digital.
representationLa representació que permet a Factuarea signar en nom seu no està activa (modes de remissió per tercer).L'empresa: registrar-la, o passar al seu propi certificat.
system_certificateEl certificat de Factuarea no està disponible.Factuarea. Reintenta d'aquí a uns minuts; si persisteix, contacta amb suport.

La factura queda exactament com estava (un esborrany sense número) i no es consumeix res. Una empresa amb VeriFactu desactivat, o en mode VERI*FACTU, mai no es bloqueja per aquesta regla.

Conversions, devolucions i errors

  • Devolució de la mercaderia o dels diners. Una devolució és una factura rectificativa: POST /v1/invoices/{invoice}/corrective. La rectificativa d'una factura simplificada anònima es registra com a R5; la d'una de qualificada, com a R1 a R4 amb la mateixa marca. Consulta Factures rectificatives.
  • El client demana una factura completa de tiquets ja emesos: POST /v1/invoices/substitute-simplified agrupa les factures simplificades sota una F3.
  • Una operació emesa per error i ja cobrada, com un cobrament duplicat: POST /v1/invoices/{invoice}/annul amb revert_collections: true reverteix tots els pagaments vigents i anul·la la factura en una sola operació atòmica. Si falla algun pas no es reverteix res. És una anul·lació, no un reemborsament: si el client ha de recuperar els seus diners, emet una rectificativa. can-annul t'ho anticipa (requires_collection_reversal, active_collections_amount).
  • La conversió d'un pressupost, una proforma o un albarà en factura respon amb warnings i warning_codes (zero_rate_line_without_exemption) quan una línia queda al 0 % sense causa d'exempció, perquè aquests documents no la modelen. No bloqueja: la factura és un esborrany i fixes la causa abans d'emetre-la.

Sense connexió no hi ha factura

No hi ha cap mode sense connexió. Un terminal sense connexió no emet: la factura existeix només quan l'API ha respost, perquè el seu registre de facturació s'ha de generar de manera simultània o immediatament anterior a la seva emissió (RD 1007/2023, art. 9). El terminal no ha d'imprimir un document com a factura amb una numeració pròpia per enviar-la després. Què fer amb una venda que no es pot facturar en aquell moment és una decisió de l'operador, aliena a Factuarea.

En aquesta pàgina

Et donem un cop de mà?Contactar amb suport