Factuarea APIDevelopers
Contract

Unattended checkout invoicing

A paid simplified invoice from a kiosk in one idempotent call, VeriFactu remission through Factuarea, subsanación of accepted records and per-line errors. Three breaking changes: a line without a rate no longer takes 21 %, issuing is rejected when the record does not fit the AEAT schema, and in NO VERI*FACTU mode it needs a usable certificate. POST /v1/verifactu/records/{id}/retry no longer answers max_retries_exceeded.

3 October 2026 — A self-service terminal or a vending machine can now issue a paid simplified invoice, with its VERI*FACTU QR, in one idempotent call. Behind it there is a rework of how the records reach the AEAT, a way to remit through Factuarea instead of with your own certificate, and a stricter check before an invoice is issued. Read the three breaking changes first; the new and the updated operations are listed at the end.

Breaking changes

1. A line with no VAT rate no longer takes 21 %. A sales line with no tax_rate, no referenced tax and no product with a tax now takes the default VAT of the company for that kind of document. If the company has none, the call answers 422 missing_required_param with error.param: lines.N.tax_rate. It applies to invoices, quotes, proformas, delivery notes and recurring invoices, in the API v1 and in MCP. Send tax_rate explicitly, or set the default VAT.

2. An invoice whose record does not fit the AEAT is not issued. With VeriFactu enabled, an invoice that used to be issued and whose record failed afterwards now answers 422 verifactu_not_eligible before it is issued, with the field in error.param. No number is consumed. The limits are in the table below.

3. NO VERI*FACTU mode needs a usable certificate to issue and to annul. A company that has enabled VeriFactu in NO VERI*FACTU mode and has no usable certificate can no longer issue or annul until it uploads one: the operation answers 422 verifactu_not_eligible with subcode: signing_certificate_unavailable and error.param certificate, representation or system_certificate, and the invoice stays as it was. It does not affect companies with VeriFactu disabled — whose default mode is no_verifactu without being in the system — nor companies in VERI*FACTU mode, which are never blocked by it.

Also, POST /v1/verifactu/records/{record}/retry no longer answers max_retries_exceeded for billing records: there is no limit of attempts any more. The code still applies to the retry of an event in no_verifactu mode.

Unattended checkout in POST /v1/invoices

POST /v1/invoices and the MCP tool create_invoice gain, with the same semantics:

  • type: "F2": a simplified invoice, with no client (anonymous ticket) or with one (qualified simplified invoice, registered as F1 with FacturaSimplificadaArt7273).
  • prices_include_tax: the unit_price of every line is the final price, and the base is computed to the cent so the total equals the sum you sent ("any line, and split"; 422 amount_reconciliation_failed only when a withholding or a surcharge moves the total).
  • payment (method, paid_at, reference): the payment is registered after issuing.
  • operation_on: the day of the operation when it differs from issued_on.
  • options.register_verifactu: the VeriFactu alta is generated before responding. options.wait_for_pdf waits up to about 15 seconds for the A4 PDF.

The 201 carries three extra blocks, only for a checkout: verifactu (status, error_code, aeat_status, huella, qr_url, qr_png_base64, legend, csv), pdf (status, url, expires_at) and public_url. The guide is Unattended checkout invoicing, and what your terminal must declare is in Compliance of the integrator's component.

Resending by external_id never expires:

  • The same external_id with the same type and total answers 200 with Idempotent-Replayed: true and the invoice already issued, completing the alta and the payment if they were missing. A queued or delivered email is not sent again.
  • A different type or total answers 409 idempotency_key_reused, with error.type: idempotency_error, subcode: unattended_replay_mismatch and param: external_id.
  • Another request with the same external_id still running makes the second one wait up to 20 seconds, and then answer 409 resource_locked if the first has not finished; the same request can be sent again.

An email requested with no possible recipient (options.send_automatically without options.send_to and with no client email) is rejected before anything is created: 422 missing_required_param with error.param: options.send_to, no draft and no number. It also applies to the other POST /v1/invoices requests, which used to reject after creating the draft.

Issuing is rejected when the record does not fit

422 verifactu_not_eligible is returned by the operations that issue an invoice: POST /v1/invoices with options, POST /v1/invoices/{id}/issue, POST /v1/invoices/{id}/send and …/mark-sent when they issue, POST /v1/invoices/{id}/corrective and POST /v1/invoices/substitute-simplified. The invoice stays a draft and consumes no number; you fix the field and repeat. In bulk-create and bulk-status the rejection appears in failures[], per element.

error.paramCause
client_idClient name missing or longer than 120 characters; a Spanish tax ID that is not 9 characters; a foreign identification longer than 20; a country the AEAT does not accept; a complete invoice with no client.
series_idThe invoice number has more than 60 characters or characters the AEAT does not accept.
original_invoice_idThe number of the corrected invoice is not admissible.
simplified_invoice_uuidsThe number of a substituted simplified invoice is not admissible.
company_nameCompany name longer than 120 characters. A missing company name is 422 business_rule_violation with the same param.
linesMore than 12 tax breakdowns, or an amount that does not fit 12 integer digits and 2 decimals.
totalA total, or its tax, that does not fit the format; an F2 above 3.000 €.
typeThe qualified-simplified mark with a type that does not admit it.

No usable signing certificate

For a company with VeriFactu enabled in NO VERI*FACTU mode, issuing and annulling are rejected before they run when the company cannot sign the record. The operations are POST /v1/invoices with options, …/issue, …/send, …/mark-sent, …/corrective, substitute-simplified, …/annul and …/void. error.param says what is missing:

error.paramWhat is missingWho fixes it
certificateThe company certificate is missing, expired, revoked, for another tax ID or unreadable.The company, in Settings → Digital certificate.
representationThe representation that lets Factuarea sign on the company's behalf is not active.The company: register it or change to its own certificate.
system_certificateThe certificate of Factuarea is unavailable.Factuarea.

GET /v1/invoices/{invoice}/can-annul anticipates it: can_annul is false and reasons carries the same message. In bulk-status the rejection appears in failures[], per element. See VeriFactu auto-submission.

Annulling a paid operation, tickets and the date of the operation

  • revert_collections — POST /v1/invoices/{invoice}/annul accepts it (false by default). With true, every live payment is reverted with the reserved reason issued_in_error and the invoice is annulled in one atomic operation. GET …/can-annul adds requires_collection_reversal and active_collections_amount. POST /v1/invoices/{invoice}/payments/{payment}/reversal rejects issued_in_error with 422 reversal_reason_reserved. POST …/void has no such option.
  • Ticket format — GET /v1/invoices/{invoice}/pdf and …/pdf-link accept format: a4 (default), ticket_80 or ticket_58. Each format is rendered and cached separately, and the ETag changes when the VeriFactu alta is created and with the format.
  • operation_on — the invoice resource returns it, and POST /v1/invoices, PUT /v1/invoices/{invoice} and POST /v1/invoices/bulk-create accept it. It cannot be later than issued_on (422 operation_date_after_issue_date) unless the first line that declares a regime_key uses 14 or 15, and a corrective invoice inherits it.
  • Payment detail — every entry of payments.detail in the invoice resource carries the five reversal keys (is_reversed, reversed_at, reversal_reason, reversal_reason_text and reversal_note), also for a payment still in force (false and null).
  • Proforma shipping — shipping_cost is an amount with VAT included. Converting the proforma to an invoice breaks it down into base and VAT and keeps the total.
  • Conversions — converting a quote, a proforma or a delivery note answers, only when there is something to warn about, warnings and warning_codes (zero_rate_line_without_exemption): a line stayed at 0 % with no exemption cause. It does not block. The warnings of the corrective invoice is now optional.

Errors that name the line

A domain error that comes from one line of a document carries line_index, the zero-based position of the line, next to param: error.line_index in the API (and as a root member of application/problem+json) and error.data.line_index in MCP. It is additive; param keeps naming the field exactly as before.

Corrective invoices and editing

  • Per-line nature in a corrective — POST /v1/invoices/{invoice}/corrective and the tool create_corrective_invoice accept unit, regime_key, exemption_reason and exemption_reason_text per line. A key you omit inherits the original line by index; a key you send replaces it; null means none.
  • Edits do not revalidate what was already saved. Editing a document of the five sales families no longer demands that the references already stored on it (catalog variant or presentation, configuration, options, linked expenses) are still active or existing. Changed or new references are validated as always. update_delivery_note in MCP validates like the API.
  • original_pack_data is returned and kept in the lines of proformas and delivery notes too, so it survives edits and the conversion to an invoice.

VeriFactu: remission, blocked records and subsanación

  • Remission by batches — the records of a company go to the AEAT in ordered batches of up to 1.000, respecting the waiting time the AEAT states, with the answer read record by record. Technical failures retry at least every hour with no cap. See How the records reach the AEAT.
  • POST /v1/verifactu/records/retry-blocked (new, scope verifactu:write) — reactivates every blocked record of the company and answers 202 with data.reactivated. The MCP tool is retry_blocked_verifactu_records.
  • Statistics — GET /v1/verifactu/stats adds pending_incident_count, blocked_incident_count and oldest_pending_at.
  • Record — adds is_blocked, block_reason, aeat_error_code (it includes SCHEMA_INVALID, a record that broke the AEAT schema and was never sent) and can_subsanar.
  • POST /v1/verifactu/records/{record}/subsanar — it now also admits accepted records and always generates a new record: data.id is the id of the new one, not of the record you sent. Subsanación errors are record_not_rejected, record_not_subsanable and requires_annulment. See VeriFactu record subsanación.
  • Remission by a third party — GET, POST and DELETE /v1/verifactu/representation (new) register, read and revoke the representation, with valid_until (at most five years), is_expired and a warning 30 and 7 days before it expires, and remission_mode in PUT /v1/verifactu/settings. The 201 of the registration carries remission_mode; revoking takes reason of 3 to 500 characters. GET /v1/verifactu/config adds remission_mode, social_collaborator_available, has_active_representation, active_representation_valid_until, active_representation_is_expired and presenter_certificate_status. The MCP tools are get_company_representation, register_company_representation and revoke_company_representation.
  • Declaración responsable — GET /v1/verifactu/declaracion-responsable returns the content of article 15: components, producer_address and signature_types, and system_name is now the name of the system and not its code.
  • Simplified invoices — a simplified invoice with the recipient's tax ID is registered as F1 with FacturaSimplificadaArt7273, and its corrective as R1 to R4; the corrective of an anonymous one stays R5.

Error codes: verifactu_not_eligible, signing_certificate_unavailable, resource_locked, idempotency_key_reused, unattended_replay_mismatch, amount_reconciliation_failed, reversal_reason_reserved, operation_date_after_issue_date, simplified_invoices_disabled, representation_required, invalid_representation and social_collaborator_unavailable.

Migration

  1. Send tax_rate on every sales line, or set the default VAT of the company for each kind of document, and handle 422 missing_required_param with lines.N.tax_rate.
  2. Handle 422 verifactu_not_eligible on every operation that issues an invoice: it is recoverable by fixing the field in error.param.
  3. If a company runs VeriFactu in NO VERI*FACTU mode, check it has a usable certificate (or an active representation) before issuing or annulling.
  4. Stop branching on max_retries_exceeded for billing records.
  5. If you call subsanar, read data.id as the new record.
  6. A terminal sends a stable external_id per sale and resends the same request until it gets a definitive answer.

New endpoints4

EndpointDescription
POST/v1/verifactu/records/retry-blockedRetry every blocked VeriFactu record
GET/v1/verifactu/representationRetrieve the active representation
POST/v1/verifactu/representationRegister a representation
DEL/v1/verifactu/representationRevoke the active representation

Updated endpoints32

EndpointDescription
POST/v1/invoicesCreate an invoice
PUT/v1/invoices/{invoice}Update an invoice
POST/v1/invoices/bulk-createBulk create invoices
GET/v1/invoicesList all invoices
GET/v1/invoices/{invoice}Retrieve an invoice
POST/v1/invoices/{invoice}/issueIssue an invoice
POST/v1/invoices/{invoice}/sendSend invoice by email
POST/v1/invoices/{invoice}/mark-sentMark an invoice as sent
POST/v1/invoices/{invoice}/correctiveGenerate corrective invoice
POST/v1/invoices/substitute-simplifiedSubstitute simplified invoices with full invoice
POST/v1/invoices/{invoice}/annulAnnul an invoice
POST/v1/invoices/{invoice}/voidVoid an invoice
GET/v1/invoices/{invoice}/can-annulCheck annulment eligibility
GET/v1/invoices/{invoice}/pdfDownload invoice PDF
GET/v1/invoices/{invoice}/pdf-linkGenerate temporary PDF link
POST/v1/invoices/{invoice}/payments/{payment}/reversalRevert an invoice payment
GET/v1/invoices/{invoice}/verifactuRetrieve invoice VeriFactu record
POST/v1/quotes/{quote}/convertConvert quote to invoice
POST/v1/proformas/{proforma}/convertConvert proforma to invoice
POST/v1/delivery_notes/{delivery_note}/convertConvert delivery note to invoice
GET/v1/verifactu/configRetrieve VeriFactu config
PUT/v1/verifactu/settingsUpdate VeriFactu settings
GET/v1/verifactu/statsGet VeriFactu stats
GET/v1/verifactu/recordsList VeriFactu records
GET/v1/verifactu/records/{record}Retrieve a VeriFactu record
POST/v1/verifactu/records/{record}/retryRetry VeriFactu transmission
POST/v1/verifactu/records/{record}/subsanarSubsanar a VeriFactu record
POST/v1/verifactu/records/find-by-csvFind a VeriFactu record by AEAT CSV
POST/v1/verifactu/records/find-by-huellaFind a VeriFactu record by hash
POST/v1/verifactu/records/find-by-invoice-numberFind a VeriFactu record by invoice number
GET/v1/verifactu/declaracion-responsableRetrieve the current declaración responsable
GET/v1/verifactu/declaracion-responsable/historyList declaración responsable history

On this page

Need a hand?Contact support