AEAT Sync

Spain's VeriFactu regulation requires every business invoice to be digitally registered with AEAT (Agencia Tributaria) in real time. Each invoice must be formatted as XML, cryptographically hashed, linked to the previous invoice in a tamper-proof chain, and submitted over AEAT's secure web service.

AEAT Sync handles all of this through a single REST API. You send us the invoice data, and we generate the XML, compute the hash chain, sign it with your digital certificate, submit it to AEAT, and notify you via webhook. This guide covers how to integrate it into your backend.

What is VeriFactu?

VeriFactu (short for Verificable Facturación) is the Spanish government's system for verified invoicing. Under Royal Decree 1007/2023, all invoicing software used by Spanish businesses must:

  • Generate invoices in a specific XML format defined by AEAT
  • Assign each invoice a unique, sequential number within a series
  • Compute a SHA-256 hash of each invoice, incorporating the hash of the previous invoice (hash chain)
  • Sign the XML with a qualified digital certificate
  • Submit the invoice to AEAT's web service immediately upon issuance
  • Store the full audit trail, including AEAT's response

Non-compliance can result in penalties. AEAT Sync ensures your invoices meet every requirement.

API Overview One endpoint, one API key, full compliance

Authentication

All requests require your API key in the X-API-Key header. Get your key from Settings after creating an account.

Header
X-API-Key: your_api_key_here

Submit an Invoice

Send a POST request to create and submit an invoice to AEAT:

POST /api/v1/invoice
{
  "issuer_nif":   "B12345678",
  "issuer_name":  "Your Company SL",
  "issue_date":   "2026-03-26",
  "items": [
    {
      "description": "SaaS subscription — March 2026",
      "quantity":    1,
      "unit_price":  "49.00",
      "vat_rate":    "21.00"
    }
  ]
}

Response

AEAT Sync returns the invoice immediately with a pending status. The background worker submits it to AEAT and sends a webhook when AEAT responds.

201 Created
{
  "invoice_number": "A-000042",
  "status":         "pending",
  "hash":           "a3f9d2e1...",
  "previous_hash":  "7b4c1a09...",
  "xml_url":        "/api/v1/invoice/42/xml"
}
How the Hash Chain Works Tamper-proof audit trail, zero effort on your side

VeriFactu requires each invoice to contain a cryptographic hash that incorporates the previous invoice's hash. This creates a chain where modifying or deleting any invoice would break the entire sequence — making fraud detectable.

Invoice #1 → hash(data₁) = H₁
Invoice #2 → hash(data₂ + H₁) = H₂
Invoice #3 → hash(data₃ + H₂) = H₃

AEAT Sync computes this automatically. You never need to track hashes — just send invoice data, and the chain is maintained for you.

What's included in the hash

  • Invoice number and series
  • Issue date and timestamp
  • Issuer NIF
  • Total amount and VAT breakdown
  • Previous invoice hash (or "0" for the first invoice)
Webhook Confirmations Know instantly when AEAT accepts or rejects

Register a webhook endpoint in your Webhooks settings. AEAT Sync will POST the result as soon as AEAT responds.

Webhook payload

POST to your endpoint
{
  "event":          "invoice.accepted",
  "invoice_number": "A-000042",
  "status":         "accepted",
  "hash":           "a3f9d2e1...",
  "issued_at":      "2026-03-26T10:45:00Z",
  "aeat_response":  "CSV-123456789"
}

Possible events

invoice.accepted AEAT confirmed the invoice. No further action needed.
invoice.rejected AEAT rejected the invoice. Check the error field for details.
invoice.retry Submission failed temporarily. AEAT Sync will retry automatically.

Webhook security

Every webhook request includes an X-Signature header — an HMAC-SHA256 of the request body using your webhook secret. Always verify this before processing.

Integration Examples

Python (requests)

submit_invoice.py
import requests

resp = requests.post(
    "https://staging.orionpathulda.io/api/v1/invoice",
    headers={"X-API-Key": "your_api_key"},
    json={
        "issuer_nif": "B12345678",
        "issuer_name": "Your Company SL",
        "issue_date": "2026-03-26",
        "items": [{
            "description": "Monthly subscription",
            "quantity": 1,
            "unit_price": "49.00",
            "vat_rate": "21.00"
        }]
    }
)
print(resp.json())  # {"invoice_number": "A-000042", ...}

cURL

Terminal
curl -X POST https://staging.orionpathulda.io/api/v1/invoice \
  -H "X-API-Key: your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "issuer_nif": "B12345678",
    "issuer_name": "Your Company SL",
    "issue_date": "2026-03-26",
    "items": [{
      "description": "Monthly subscription",
      "quantity": 1,
      "unit_price": "49.00",
      "vat_rate": "21.00"
    }]
  }'

Node.js (fetch)

submitInvoice.js
const resp = await fetch(
  "https://staging.orionpathulda.io/api/v1/invoice",
  {
    method: "POST",
    headers: {
      "X-API-Key": "your_api_key",
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      issuer_nif: "B12345678",
      issuer_name: "Your Company SL",
      issue_date: "2026-03-26",
      items: [{
        description: "Monthly subscription",
        quantity: 1,
        unit_price: "49.00",
        vat_rate: "21.00"
      }]
    })
  }
);
const data = await resp.json();
console.log(data);
Frequently Asked Questions
Do I need a digital certificate?

Yes. AEAT requires invoices to be signed with a qualified digital certificate (e.g., from FNMT). You upload your certificate in Settings, and AEAT Sync uses it to sign every invoice.

What happens if my API call fails?

AEAT Sync returns a standard HTTP error code with a descriptive message. The invoice is not created and nothing is submitted to AEAT. Fix the issue and retry.

Can I use AEAT Sync in sandbox/test mode?

Yes. Enable sandbox mode in Settings. Invoices are processed normally (XML generated, hash chain maintained) but are not actually submitted to AEAT's production endpoint.

What VAT rates are supported?

All Spanish VAT rates: 21% (general), 10% (reduced), 4% (super-reduced), and 0% (exempt). Set the vat_rate field per line item.

Is there a rate limit?

The Starter plan allows up to 20 invoices/month. Growth allows 500, Scale allows 5,000. See Pricing for details. Rate limits are applied per calendar month.