Invopop Integration
Invopop is the third-party e-invoicing platform that handles GOBL validation, Verifactu XML generation, and submission to the Spanish Tax Authority (AEAT). This page documents the Invopop API as it relates to our integration, the GOBL document format, and common error scenarios.
For general context on what e-invoicing is and how it fits into Lavanda, see the E-Invoicing.
External documentation:
API Endpoints
Silo
https://api.invopop.com/silo/v1
Document storage
Transform
https://api.invopop.com/transform/v1
Workflow processing
All requests require a Bearer token:
Authorization: Bearer <api_token>Each workspace that uses e-invoicing has its own Invopop API token, stored encrypted in the workspace configuration.
Core Flow
The submission process has two steps, both using idempotent PUT requests with client-generated UUID v7 identifiers.
1. PUT /silo/v1/entries/{uuid} -> Store GOBL document
2. PUT /transform/v1/jobs/{uuid} -> Submit to Verifactu workflow
3. Webhook -> Receive result (accepted/rejected)Step 1: Store the Document
Upload a GOBL invoice to Invopop's Silo:
The entry UUID must be a UUID v7 (timestamp-based). Invopop rejects standard v4 UUIDs for the invoices folder.
Step 2: Trigger the Workflow
Submit the stored document to a Verifactu workflow:
The ?wait=30 parameter makes the call synchronous (up to 30 seconds), returning the result inline instead of requiring a separate poll.
Step 3: Receive Results
After a successful submission, the Silo entry is updated with stamps and metadata:
QR code URL
data.head.stamps[] where prv == "verifactu-qr"
A link customers can use to verify the invoice on AEAT's website
Verifactu hash
data.head.stamps[] where prv == "verifactu-hash"
Cryptographic proof of submission
Submission status
meta[] where src == "verifactu"
e.g. "Correcto"
Fetch the updated entry to read these fields:
Idempotency and Entry Updates
PUT is create-only. Once a Silo entry exists, a subsequent PUT returns 409 Conflict with the message "entry already exists with same id". This makes PUT safe to retry on network failures.
To update an existing entry's data (e.g. to fix an error before resubmission), use PATCH:
PUT
No
Creates entry
PUT
Yes
409 Conflict
PATCH
Yes
Updates entry data (even if signed or in error state)
DELETE
--
Not supported (returns 404)
Webhooks
Invopop sends webhook notifications when a workflow job completes. The payload contains:
The webhook only contains the silo_entry_id, not the full document. To get the result details (QR code, hash, status), fetch the entry from the Silo API using the silo_entry_id.
GOBL Document Format
GOBL (Go Business Language) is an open-source standard for structured financial documents. All invoices submitted through Invopop must be in GOBL JSON format.
Regular Invoice (F1)
Credit Note (R4)
Credit notes reference the original invoice via the preceding field. They use "type": "credit-note" and do not include the $addons field.
Multi-line Invoice with Mixed VAT Rates
When an invoice contains line items at different VAT rates, each rate appears as a separate entry in totals.taxes.categories[].rates[]:
Customer Identity Mapping
How a customer is represented in GOBL depends on their ID type and country.
Spanish Customers
Spanish customers with a tax ID (CIF for companies, NIF/DNI for individuals) use the tax_id field:
Foreign Customers
Foreign customers without a Spanish tax ID use the identities field with a Verifactu identity type extension:
Passport:
EU VAT number:
Verifactu Identity Type Codes
02
NIF-IVA
EU VAT number (non-Spanish)
03
Passport
Passport number
04
Official ID
National ID card from country of residence
05
Residence certificate
Spanish residence certificate
06
Other document
Other supporting identification (e.g. driver's license)
07
Not registered
Customer has no identification
Spain / Verifactu Reference
Document Types
F1
Regular invoice
Default (no type field)
R4
Credit note
"type": "credit-note"
Tax Extensions
Both line-level taxes and totals-level rates must include these extensions:
es-verifactu-op-class
S1 (standard taxable), S2 (exempt)
es-verifactu-regime
01 (general regime), 02 (simplified regime)
Bypass Mode
Bypass mode tells GOBL to preserve pre-calculated totals instead of recalculating them. It is activated by adding "$tags": ["bypass"] to the document.
Without bypass mode, GOBL recalculates VAT by grouping line items by rate, summing their net amounts, and applying the tax percentage to the aggregated total. This can cause small rounding differences (typically plus or minus one cent) compared to per-line-item calculations.
Note: We are planning to remove bypass mode from our integration by aligning our internal VAT calculations with GOBL's aggregation method. See the Reference for details on the rounding issue.
Known Issue: Bypass Mode Fails for Credit Notes
Regular invoice (F1)
Yes
Works
Credit note (R4)
Yes
Fails with verifactu.generate error
Credit note (R4)
No
Works
This is a known Invopop bug reported in January 2026. Credit notes submitted with $tags: ["bypass"] fail at the verifactu.generate step with an "unexpected data error". The workaround is to submit credit notes without bypass mode.
Error Recovery
When a workflow fails (e.g. rejected at verifactu.generate or verifactu.send), entries can be corrected and resubmitted:
Each resubmission requires a new job UUID. The entry UUID stays the same.
Constraint: Hash Chain Integrity
Once Verifactu has accepted a record, it becomes part of a cryptographic hash chain. Changing the invoice code and resubmitting fails with error 3002 ("No existe el registro de facturacion"). To correct an accepted invoice, issue a credit note instead.
Failed at verifactu.generate
Yes
Yes
Rejected by verifactu.send
Yes (likely)
Yes (likely)
Accepted by verifactu.send
No (error 3002)
Not tested
PATCH Behaviour on Signed Entries
PATCH updates the GOBL document and digest even on signed or error-state entries. The original signature in sigs is retained but stale. On resubmission, Invopop skips the "Sign Envelope" step ("already signed"), which does not block Verifactu processing.
Verifactu Cancellation
Rejected invoices are not counted as reported to Verifactu, so no cancellation is needed
To cancel an accepted invoice, a credit note must be issued (there is no "cancel" API)
Invopop workflows can be configured to auto-cancel rejected invoices in Verifactu
Supplier Registration
New suppliers must be registered with Invopop before submitting invoices:
Create the supplier as a Silo entry with the
org/partyGOBL schemaRun a registration workflow to register the supplier with the tax authority
This is typically a one-time setup step per workspace.
Common Errors
invalid check digit
Invalid Spanish CIF/NIF
Use a valid tax ID
invalid format
Malformed tax ID
Use the correct CIF/NIF format
request duplicated
Same supplier + invoice code combination
Use unique invoice codes per supplier
party is not registered
Supplier not registered with Invopop
Complete the supplier registration flow
unexpected data error
Bypass mode used with a credit note
Remove the bypass tag for credit notes
schema validation
Missing required fields in bypass mode
Include all ext fields on lines and totals rates
No existe el registro de facturacion (3002)
Resubmitting with a changed code after Verifactu accepted the original
Cannot change the code after acceptance; issue a credit note instead
entry already exists with same id
PUT on an existing Silo entry
Use PATCH to update, or use a new UUID
Best Practices
Generate UUID v7 identifiers client-side for idempotent retries (Invopop rejects v4 UUIDs)
Use
?wait=30on job creation to get the result synchronouslyCheck the
faultsarray in the job response even when the top-level status is "OK"Use unique invoice codes per supplier (Verifactu rejects duplicates)
Include tax extensions on both line-level taxes and totals-level rates
Use PATCH for corrections, not PUT (PUT cannot update existing entries)
Generate a new job UUID per retry while reusing the same entry UUID
Last updated