VeriFactu auto-submission
There is no "send to AEAT" button. Registration is created when the invoice leaves draft — the gates that decide whether it happens, how the records reach the AEAT in batches and without a retry cap, who remits them, and the manual levers that exist afterwards.
Integrators arriving from other invoicing platforms look for the operation that submits an invoice to the tax authority, do not find it, and assume the feature is missing. It is not missing: submission is not a step you perform. The registration is created as a consequence of issuing the invoice, and transmitted by a background pipeline.
This page is the answer to "why hasn't my invoice reached the AEAT?", which is almost always one of the gates below rather than a failure.
When this applies
To every invoice that leaves draft for a company whose VeriFactu activation is
effective. Concretely, the registration is created when the invoice is issued
— including invoices that are born already issued: correctives, F3
substitutes, recurring generations, and creations that pass options.issue_directly.
(Before API version 2026-10-01 an issued invoice is published as sent; see
Versioning.)
The draft stage is deliberately outside the mechanism. A draft has no
definitive number, no frozen recipient snapshot and no fiscal existence; nothing
is declared for it.
The gates, in the order they are evaluated
Instance kill-switch. A global flag can disable VeriFactu for the whole installation. It is an emergency switch, never an activation: on its own it enables nothing.
Per-company activation. This is the one you control. It defaults to off
for a newly created account — a fresh company does not register invoices until
someone activates VeriFactu. Effective activation is instance AND company
(BR-VFC-025).
Read it with
GET /v1/companies/{company}/verifactu/config:
the enabled field is already the effective value, not the raw company flag.
Operating mode. With activation on, a company still chooses between
transmitting and not transmitting. In no_verifactu mode the chained records are
still generated and stored locally — the mode changes transmission, not
chaining — and must be available for inspection, but nothing is sent in real
time (BR-VFC-018, RD 1007/2023 art. 16).
Historical-import bypass. Invoices loaded through the bulk import of
pre-adhesion history carry a transient flag that makes the VeriFactu handlers
return without creating any record (BR-INV-011,
BR-VFC-009). Without it, importing years of history would
declare thousands of registrations with issue dates from before the company
joined the system. The flag is forced by the importer and is not exposed on
the ordinary creation endpoints — you cannot set it from the public API.
Valid record data. Before an invoice is issued, Factuarea checks that the
registration it will generate fits the AEAT schema: a client name of at most 120
characters, a 9-character Spanish tax ID, an invoice number of printable ASCII
of at most 60 characters, at most 12 tax breakdowns, amounts that fit 12 integer
digits and 2 decimals. If something does not fit, the operation answers
422 verifactu_not_eligible with the field in error.param, the invoice is
not issued and no number is consumed. It applies to every operation that issues
an invoice and to every company with VeriFactu enabled. The table of fields is in
Unattended checkout invoicing.
A certificate that can present or sign the record. What is needed depends on the mode:
- VERI*FACTU mode. The certificate that remits the records authenticates the
connection with the AEAT; the record itself is not signed. If there is no usable
certificate, or no active representation in the third-party modes, the invoice
is issued and its record is generated, but it is born blocked
(
MISSING_CERTIFICATEorMISSING_REPRESENTATION) and is released when you upload the certificate or register the representation. See Blocked records. - NO VERI*FACTU mode with VeriFactu enabled. Every record is signed, and a record only
counts as generated once it is signed. Without a usable signing certificate the
operation that would generate it is rejected before it runs: issuing, and
also annulling, answer
422verifactu_not_eligiblewithsubcode: signing_certificate_unavailableanderror.paramcertificate,representationorsystem_certificate. The invoice stays as it was. It affects the eight operations that issue or annul (createwithoptions,issue,send,mark-sent,corrective,substitute-simplified,annulandvoid);can-annultells you beforehand. - VeriFactu disabled. Nothing changes: a company that has not enabled VeriFactu
issues and annuls without a certificate, even though its default mode is
no_verifactu.
Check has_active_certificate and presenter_certificate_status on the config
endpoint before going live.
If all six pass, the record is created, chained, and queued for transmission.
Whether the queue transmits automatically is itself an instance-level setting
surfaced read-only as auto_transmit on the config endpoint.
What the API sends
Nothing you write. There is no request body for "submit", and no field on
POST /v1/companies/{company}/invoices decides
whether a registration is created. What you do control is when the invoice is
issued, and everything follows from that:
- Create the invoice as a draft, then issue it with
POST /v1/companies/{company}/invoices/{id}/sendorPOST /v1/companies/{company}/invoices/{id}/mark-sent. - Or create and issue atomically by passing
options.issue_directlyon the create call. - Or, from a terminal that needs the QR in the same response, pass
options.register_verifactu: the registration is generated synchronously, before answering, and the response carries itshuellaand QR. If it cannot be generated (no certificate, no representation, clock drift) the invoice stays issued and the block answersstatus: failed; repeating the sameexternal_idcompletes it. See Unattended checkout invoicing.
Because the "create and issue in one call" path emits both a creation and an
issue event, two handlers race to create the same registration. The command is
idempotent by invoice: the second one detects the existing registration and
silently no-ops, so exactly one record exists per invoice
(BR-VFC-008). You do not need to de-duplicate on your side.
The one explicit escape hatch
There is an operation that forces the creation of a registration for an
already-issued invoice:
POST /v1/companies/{company}/invoices/{id}/verifactu,
scope verifactu:write. It creates the registration and enqueues its
transmission, answering 201 with the new record.
It exists for the case where an invoice was issued while a gate was closed — a certificate that had not been uploaded yet, for instance — and you want the registration once the gate opens. It is not a re-send:
| Situation | Answer |
|---|---|
| The invoice already has a registration | 422 verifactu_already_submitted |
| The invoice is still a draft, VeriFactu is disabled for the instance, or the certificate is missing, expired, revoked or has a mismatched tax ID | 422 verifactu_not_eligible |
| The invoice does not exist, or belongs to another company | 404 invoice_not_found |
curl -X POST https://api.factuarea.com/v1/companies/{company}/invoices/0197a2a8-4cf0-7a31-9a5e-3f2b8c1d6e42/verifactu \
-H "Authorization: Bearer fact_live_3pXnR2VbY7TcA9eFmN5z8KqW"The only manual levers on a record that already exists
Once the registration exists, exactly three operations act on it, and all are covered in VeriFactu submission states:
retry— schedules the batch of the company so that one failed record goes out in it, for technical failures. There is no limit of attempts.retry-blocked— reactivates every blocked record of the company at once, after you have fixed what blocked them. It answers202withdata.reactivated.subsanar— generates a new registration that corrects an accepted or a rejected one; the original is never edited.
There is no operation that re-transmits an accepted record or edits it in place.
Acceptance is terminal for that record by regulation; a data error in it is
corrected with a new registration (subsanar), or with a corrective invoice when
the invoicing rules require one.
How the records reach the AEAT
Remission follows article 16 of Order HAC/1177/2024. You do not drive it, but it explains what you will see on a record and in the statistics.
- Batches, in order. The records of a company are sent together, never mixed with another company's, up to 1,000 per submission and always in the order of the chain: a record never overtakes an earlier one. Only one submission per company is in flight at a time.
- Waiting time. In every answer the AEAT states how long to wait before the next
submission (
TiempoEsperaEnvio). Factuarea respects it (60 seconds if the AEAT has not stated one) unless 1,000 records are already waiting. A record created inside the window waits for the next submission. - Answer per record. Accepted, accepted with errors (the record is accepted, with
a warning) or rejected. The AEAT error
3000, "duplicate record", counts as confirmation that the AEAT received it. - Retries without a cap. If a submission gets no answer, a server fault or a
network failure, its records stay pending, flagged as an incident (
Incidencia=Sin the next submission), and are retried with growing intervals — 1, 5, 15, 30 minutes and then every 55 — so that no more than one hour passes between two attempts, with no maximum number of attempts, for as long as the incident lasts. Records generated meanwhile queue behind the earlier ones. - Pending alert.
GET /v1/companies/{company}/verifactu/statsreportspending_incident_count(records still waiting to reach the AEAT),blocked_incident_count(of those, the blocked ones) andoldest_pending_at. They are not affected by the filters of the query. The app shows the same alert, and the daily summary email includes it. - A record that does not fit the schema is generated
rejectedwithaeat_error_code: SCHEMA_INVALIDand never sent: the AEAT rejects a whole submission when one record breaks its schema, so it is caught before. You fix it with asubsanar, and it does not stop the rest of the chain. - Test companies (
fact_test_keys) never remit.
Blocked records
A record in error with no automatic retry is blocked: something needs your
action, it is not retried by itself and it holds back the records after it. The
record says so with is_blocked: true and the cause in block_reason:
block_reason | What is missing |
|---|---|
MISSING_CERTIFICATE | The company has no usable certificate (missing, expired, revoked or for another tax ID). |
MISSING_REPRESENTATION | The mode is a third-party one and there is no active representation of the kind it needs. |
PRESENTER_NOT_ENABLED | The AEAT says the presenter is not enabled (4112 or 3003). |
SUBMISSION_REJECTED | The AEAT rejected the submission for a client-side fault. |
aeat_error_code carries the last AEAT code (or the internal one). A fifth
cause, SYSTEM_CERTIFICATE_UNAVAILABLE (the certificate of Factuarea in the
third-party modes), is on Factuarea's side: it does not block and the record
retries by itself.
Fixing the cause releases the records on its own: uploading or activating a
certificate, registering a representation or changing the mode reactivates what
that event can free. To release them all by hand, call retry-blocked.
Remitting through Factuarea: third-party modes
By default each company remits with its own certificate. A company can instead
authorize Factuarea to remit in its name, so it does not hand over its certificate.
The mode is remission_mode, read in
GET /v1/companies/{company}/verifactu/config
and changed with
PATCH /v1/companies/{company}/verifactu/settings:
remission_mode | Who remits | Representation required |
|---|---|---|
own_certificate (default) | The company, with its certificate. | None. |
social_collaborator | Factuarea, as social collaborator. | The signed annex I of the Resolution of 18-12-2024 (social_collaboration_annex_i). |
power_of_attorney | Factuarea, under a power of attorney. | A power of attorney registered with the AEAT (aeat_power_of_attorney). |
social_collaborator is only available while the AEAT social-collaboration
agreement is in force on the instance: check social_collaborator_available. Until
then, power_of_attorney covers the same case.
The representation is registered with
POST /v1/companies/{company}/verifactu/representation
(multipart/form-data: kind, signer_name, signer_tax_id, granted_on, the
document PDF or, for a power of attorney, power_of_attorney_reference, and an
optional valid_until), read with
GET and
revoked with
DELETE
(optional reason of 3 to 500 characters). Things to know:
- Only one representation is current. Registering another revokes the previous
one, which stays in the history; the signed document is custodied encrypted and is
never returned. Revoking is final and returns the company to
own_certificate. - Validity.
valid_untilis the last day (Madrid time) and cannot exceed five years fromgranted_on. You are warned 30 and 7 days ahead and when it expires. An expired representation is still returned (is_expired: true) but no longer enables remission: new records are blocked withMISSING_REPRESENTATIONuntil you register a new one. - Mode follows representation. Registering a representation of the other kind
while the company remits through a third party moves the mode to the one that kind
needs, or back to
own_certificateif that mode cannot be used. Withown_certificate, registering changes nothing: switch withPATCH …/settings, which requires an active representation (422representation_required). - The issuer is always the company. Whoever presents the batch, every record carries the tax ID and name of the company, never those of the certificate.
- Presenter. In NO VERI*FACTU mode the records are signed with the certificate of the presenter: the company's own or, with an active representation, Factuarea's.
Every registration, revocation and expiry of a representation, and every mode change, is audited.
Activation is a commitment, not a toggle
Turning VeriFactu on is asymmetric, and an integration that treats it as a
reversible switch will hit a 422 in production.
Moving to the verifiable mode is always allowed. Moving back is blocked until
31 December of the year in which it was activated
(BR-VFC-001, BR-VFC-023, RD 1007/2023
art. 13). The integrity of a chain declared to the AEAT in real time cannot be
downgraded to self-certified software halfway through a tax year.
There is one deliberate escape: while the chain is still empty — the company has not emitted a single billing record in any state — the company may change its mind and go back, and the lock is cleared. The first record emitted, even a rejected or errored one, arms the lock until the end of the year. Turning off the per-company activation flag is blocked by the same guard, so it cannot be used to sidestep the commitment.
GET /v1/companies/{company}/verifactu/config exposes is_locked_until so you
can show this to your users before they commit.
Sandbox and production are not interchangeable
Each company operates against one AEAT environment, exposed as environment on
both the config object and every record. A CSV obtained against the AEAT test bed
is not a registration: test CSVs carry a recognisable prefix, and a
production database containing them means something was simulated that should
have been transmitted (BR-VFC-017).
The rule that protects you is that the system must never fall back to simulation
silently — an unreachable endpoint has to surface as a technical error state,
not as a fabricated acceptance. When you reconcile, treat the environment field
as part of the record's identity.
What appears on the PDF
Auto-submission itself adds nothing to the document; the printed artefact depends
on the existence of a record, not on how it was created. Once a record exists,
the invoice carries the legal QR block (BR-VFC-015), and the
legend under the code differs between the two operating modes: the short
VERI*FACTU mark in verifiable mode, and the full sentence stating the invoice
is verifiable at the AEAT electronic office in the other.
An invoice imported with the historical bypass has no record and therefore prints no QR. That is correct: pre-adhesion invoices are not verifiable at the AEAT.
What reaches the AEAT
One registration declaration per issued invoice, chained to the company's
previous record, plus an annulment declaration if the invoice is later annulled
(see Annul or correct). Nothing else is transmitted
as a consequence of issuing. Records go in the batches described in
How the records reach the AEAT, and a qualified simplified invoice
travels as an F1 marked with FacturaSimplificadaArt7273
(see Simplified or full invoices).
In no_verifactu mode nothing reaches the AEAT in real time at all; the company
keeps the local chain for inspection and the system periodically records
summaries of its own operational events, which the regulation treats as separate
evidence (BR-VFC-018).
Traceability
Derived from the domain rules of the Factuarea backend:
BR-VFC-001— adhesion to the verifiable mode is irrevocable until the end of the calendar year, with the empty-chain exception.BR-VFC-008— idempotency: one registration per invoice, even when the create-and-issue flow fires two events.BR-VFC-009— the historical-import bypass, seen from the VeriFactu side.BR-VFC-015— the QR block and its two legends.BR-VFC-017— the sandbox/production boundary and the prohibition on silent simulation.BR-VFC-018—no_verifactumode: local chain, event summaries, no real-time transmission.BR-VFC-023— the asymmetric mode switch and the year-end lock, including the guard that stops the activation flag from bypassing it.BR-VFC-025— per-company activation, default off, effective value asinstance AND company.BR-INV-011— the historical-import bypass, seen from the invoicing side.