Skip to content

Spain (TicketBAI pilot)

TicketBAI is a country-specific layer for eligible Spanish entities in Álava, Bizkaia, and Gipuzkoa. This guide covers the managed, allowlisted API pilot. Access is arranged with Space Invoices support; having an API key or a sandbox entity does not grant pilot access or certify that an authority has accepted an invoice. This pilot is separate from Spain’s AEAT VeriFactu workflow.

Before enabling an entity

Confirm the taxpayer’s Basque territory, Spanish issuer NIF, EUR currency, and the certificate or representative’s authority to sign and submit for that taxpayer. Have support confirm pilot access and producer software configuration. Before live use, complete the applicable software registration and representative or device authorization, then establish a real sandbox submission and response with the authority. A locally signed XML file or a ready response is not authority proof.

Choose the entity’s sandbox or live environment before issuing. The taxpayer identity, territory, environment, and Bizkaia ledger model cannot change after its first TicketBAI record. The same taxpayer cannot have two reserved installations in one environment.

Configure distinct annual number formats for invoices and credit notes. Numbers must remain unique within the fiscal year across these document families, including business units. For example, use INV-{yyyy}-{nnnnn} and CN-{yyyy}-{nnnnn} for the main entity, with separate unit formats such as INV-{yyyy}-{u}-{nnnnn} and CN-{yyyy}-{u}-{nnnnn}. Set these in the entity’s settings.number_formats and settings.unit_number_formats through PUT /entities/{id} before issuance. Do not rely on default formats that each produce the same number.

Configure TicketBAI

Settings require entity settings management permission. Certificate upload is a non-delegable operation: arrange it through an authorized account-level credential, not an entity-scoped delegated key. Record and XML reads require entity read permission; retry, annulment, and reconciliation require operational write permission.

Use your normal bearer token and x-entity-id on the following requests. The examples use the EU API origin and placeholders; never publish a real token, certificate, or passphrase. First prepare settings with enabled: false, then upload the taxpayer’s P12/PFX certificate (at most 1 MiB) and check readiness. The certificate must contain a matching private key and be currently valid. Uploading it does not establish representative authority.

Terminal window
curl --request PUT 'https://eu.spaceinvoices.com/fiscalization/ticketbai/settings' \
--header 'Authorization: Bearer <API_TOKEN>' \
--header 'x-entity-id: <ENTITY_ID>' \
--header 'Content-Type: application/json' \
--data '{"territory":"alava","enabled":false}'
curl --request POST 'https://eu.spaceinvoices.com/fiscalization/ticketbai/settings/certificate' \
--header 'Authorization: Bearer <API_TOKEN>' \
--header 'x-entity-id: <ENTITY_ID>' \
--form 'file=@/path/to/taxpayer.p12' \
--form 'passphrase=<P12_PASSPHRASE>'
curl 'https://eu.spaceinvoices.com/fiscalization/ticketbai/settings' \
--header 'Authorization: Bearer <API_TOKEN>' \
--header 'x-entity-id: <ENTITY_ID>'

For Bizkaia, select lroe_model: "240" for a company or "140" for an individual. Model 140 also requires the individual’s explicit given_name, surname1, and surname2 in lroe_individual; the API does not split the entity name. Activity epigraphs are supplied per model 140 invoice, not inferred from the business. These are alternative settings bodies:

{"territory":"bizkaia","lroe_model":"240","enabled":false}
{"territory":"bizkaia","lroe_model":"140","lroe_individual":{"given_name":"Ane","surname1":"Example","surname2":"Sample"},"enabled":false}

After support and the taxpayer confirm certificate authorization and the readiness blockers are cleared, enable the pilot with PUT /fiscalization/ticketbai/settings:

{"enabled":true,"authorization_confirmed":true}

Inspect ready and blockers in the returned settings. Live activation also depends on completed production registration; the API does not perform that registration.

Issue a supported document

Use the ordinary POST /invoices or POST /credit-notes endpoint. TicketBAI advance issuance and draft advance finalization return 422 until a complete advance correction lifecycle is supported. Draft advances may be saved, but cannot be issued in this pilot. The API creates and stores signed XML, an identifier and QR URL, then submits it through the selected territory’s adapter. The invoice response includes ticketbai.record_id, identifier, qr_url, and delivery status; the issued PDF displays the identifier and QR. Keep the document ID and record ID for later reads.

Use the ordinary routes for Spanish documents. All custom outgoing-document routes—including invoices, credit notes, advance invoices, estimates and delivery notes—reject Spanish entities, including draft creation. Country availability is exposed by country_rules.capabilities.custom_document_create on the entity. Custom invoice, credit-note and advance-invoice request bodies do not accept a ticketbai field.

Full invoices need a recipient identity and address, explicit financial line items and VAT, and the correct invoice-wide ticketbai.tax_treatment. For a Spanish recipient, give its NIF; for a foreign recipient, provide ticketbai.recipient_id_type and operation_type as well. Positive VAT defaults to taxable; zero-rate and untaxed documents require an explicit treatment and the applicable exemption or non-subject cause. vat_regime defaults to "01"; "02" is supported for eligible exports. Choose the actual tax treatment for the transaction.

The issue date must be today in Europe/Madrid. Use a single date_service to identify a different operation date if needed; it does not backdate the issue. Service ranges are outside this pilot. Replace the illustrative date below with the Madrid calendar date when sending. A long-running integration should refresh it after Madrid midnight.

Terminal window
curl --request POST 'https://eu.spaceinvoices.com/invoices' \
--header 'Authorization: Bearer <API_TOKEN>' \
--header 'x-entity-id: <ENTITY_ID>' \
--header 'Content-Type: application/json' \
--data-binary @invoice.json

invoice.json (illustrative date; replace before sending):

{
"date": "2026-09-25",
"date_service": "2026-09-24",
"currency_code": "EUR",
"customer": {
"name": "Example Customer SL",
"country_code": "ES",
"tax_number": "B76543214",
"address": "Calle Ejemplo 1",
"post_code": "01001",
"save_customer": false
},
"items": [{
"name": "Consulting service",
"quantity": 1,
"price": 100,
"taxes": [{"rate": 21}],
"save_item": false
}],
"ticketbai": {"tax_treatment": "taxable"}
}

For a Bizkaia model 140 invoice, add "lroe_income_epigraphs": ["123"] to ticketbai, replacing 123 with the taxpayer’s real IAE activity epigraph. Model 240 must omit this field. The pilot supports at most ten explicit epigraphs on a model 140 invoice.

Correct with a credit note or annul an invoice

A TicketBAI credit note is a difference rectification (I). Create it through POST /credit-notes with positive line quantities and prices, an applicable ticketbai.rectification_code (R1–R5), and linked_documents containing the original TicketBAI invoice ID. Its signed fiscal amounts are negative, and the original invoice identity is included. Use the credit-note endpoint for a paid invoice or a business correction; a credit note does not itself refund a customer.

{
"date": "2026-09-25",
"currency_code": "EUR",
"customer": {"name":"Example Customer SL","country_code":"ES","tax_number":"B76543214","address":"Calle Ejemplo 1","post_code":"01001","save_customer":false},
"items": [{"name":"Consulting correction","quantity":1,"price":100,"taxes":[{"rate":21}],"save_item":false}],
"linked_documents": ["<ORIGINAL_INVOICE_ID>"],
"ticketbai": {"rectification_code":"R1","tax_treatment":"taxable"}
}

For an unpaid ordinary invoice issued in error whose original TicketBAI delivery is confirmed, use the existing invoice void route, POST /invoices/{invoiceId}/void, with a nonblank reason of 1–500 characters after trimming. This creates a signed annulment and returns the voided invoice with ticketbai.cancellation.record_id, ticketbai.cancellation.status, and ticketbai.cancellation.reason. The invoice remains void if annulment delivery is pending, rejected, or uncertain, and repeating the request reuses the same cancellation. This does not create a credit note automatically. Paid or credited invoices, credit notes, and advances are ineligible; use a credit note for an eligible business correction or paid invoice.

Terminal window
curl --request POST 'https://eu.spaceinvoices.com/invoices/<INVOICE_ID>/void' \
--header 'Authorization: Bearer <API_TOKEN>' \
--header 'x-entity-id: <ENTITY_ID>' \
--header 'Content-Type: application/json' \
--data '{"reason":"Invoice issued in error"}'

If the original issue delivery is unknown, reconcile it before attempting to void the invoice. The void response contains a cancellation record ID; use that ID to read, retry, or reconcile the cancellation as described below.

Track delivery and recover safely

Read GET /fiscalization/ticketbai/records/{id} for the immutable identity, status, authority codes and receipt, retryable, recent attempts, and the last authority query. For an issued invoice, use ticketbai.record_id; for its annulment, use ticketbai.cancellation.record_id from the voided invoice response. GET /fiscalization/ticketbai/records/{id}/xml downloads that record’s signed XML bytes. Preserve the original signed XML when seeking support; later delivery attempts do not regenerate it.

StatusWhat to do
pending or sendingRead the record again; a pending record may wait behind an unresolved earlier chain record.
acceptedRetain the record and receipt.
accepted_with_errorsInspect authority codes and resolve the reported issue with support.
rejectedInspect the codes. Only an explicit Álava or Gipuzkoa service-unavailable 006 rejection is eligible for the stored-byte retry route.
unknownDo not resend. Reconcile against the authority or contact pilot support.

POST /fiscalization/ticketbai/records/{id}/retry has no body. It can resend stored bytes for a pending record or the explicit 006 case above; accepted, unknown and other rejected records are not retryable. POST /fiscalization/ticketbai/records/{id}/reconcile also has no body and performs a bounded authority query. A query that does not find the record does not authorize a resend. Automated live Gipuzkoa query is currently unavailable because its production endpoint has not been verified; use the authority portal and pilot support instead. Bizkaia query searches at most three pages, and ambiguous or exhausted results remain unknown.

For a rejected tax-data record, export and retain the original signed XML, then work with pilot support to choose an official Zuzendu SUBSANAR/MODIFICAR filing or a rectifying credit note as appropriate. Zuzendu preparation and submission are not automated by this pilot. A confirmed rejected record, an already received record, and an uncertain network outcome require different handling; never relabel one as another. Support may attach a manual attestation backed by separately retained authority evidence. manual_resolution on a record identifies that human resolution, not an automated authority response.

Current boundaries and official references

This pilot does not cover advance invoices, advanced VAT regimes, withholding, compound taxes, mixed tax treatments on one invoice, payable-rounding corrections, service-date ranges, applying an advance to a final invoice, or the full Batuz ledger workflow. Simplified invoices are limited to supported eligible cases up to EUR 400. Do not use a supported JSON shape alone as a tax-eligibility decision.

One Spanish fiscal regime per entity

An entity can use either TicketBAI or VeriFactu. Enabling a second provider, or issuing through it after the other provider has created fiscal records, returns HTTP 409. Disabling the first provider does not clear its fiscal history. Contact support to review a regime transition. Separate workshop entities may use different providers.

Custom outgoing document routes are unavailable for Spanish entities, including drafts. Use the standard document routes so the applicable fiscal checks run.