VeriFactu Software · how to integrate VeriFactu into a SaaS

How to integrate VeriFactu into SaaS or custom software with an architecture you can maintain

The most resilient integration does not copy fiscal rules into the SaaS. It separates commercial facts from the fiscal layer, uses REST or SDK from the backend only, validates with preflight, preserves identity with eventId/Idempotency-Key, persists recordId and reconciles instead of reissuing. Connector Contract Suite v2 turns those rules into executable tests for third-party adapters.

A robust SaaS integration should treat VeriFactu as a server-to-server layer. Define a stable eventId per operation, choose direct InvoiceIntent or MappingProfile, run preflight and only then call issue with a deterministic Idempotency-Key. Persist recordId as soon as it is returned. Once recordId exists, every later sync must query that record even when the previous error was retryable; never fall back to issue. Keep API keys, certificates, tenant, installation and environment out of the frontend. To validate a custom adapter, implement preflight/source, send with eventId, status(recordId) and sync, then run Connector Contract Suite v2 in CI. The suite checks the integration contract but does not certify regulation or enable AEAT production.

KairosethOctober 7, 202610 min readUpdated · October 7, 2026Versión en español

Product teams often approach a fiscal integration like any other external API: create a client, send an invoice and wait for a response. That is insufficient when the operation has durable identity and a timeout does not prove that the server failed to process it. It is also dangerous if the client gains authority over tenant, certificate or fiscal classification merely because those concepts can be represented in JSON. Puente VeriFactu defines a stricter contract. Custom software keeps business facts; the server resolves organization, installation, sourceSystem and secrets; preflight separates validation from side effects; Idempotency-Key protects creation; recordId becomes the reference after creation; and sync must reconcile rather than reissue as a fallback. The server-side SDK reduces boilerplate and Connector Contract Suite v2 turns these invariants into a CI gate independent of the source framework, ERP or language.

1. Start by designing domain boundaries rather than copying endpoints

The correct integration point begins with one question: what does your product know with certainty? It normally knows who buys, what is invoiced, commercial amounts and taxes, currency, and number and date when those already belong to its workflow. It also has an internal identifier that can become eventId. Those facts should come from the source software because another system cannot reconstruct them more reliably. Fiscal tenant, installation, certificate, environment, permissions and sensitive classifications, on the other hand, should not arrive as arbitrary decisions from a client merely because it owns an API key.

This boundary prevents two fiscal engines from emerging. If every SaaS implements its own XML, hashing, transport and response rules, every change requires coordinating product and regulatory releases. Centralizing that layer in the bridge lets the client team keep a comparatively stable integration contract. The application can evolve its model, UI and business logic while the adapter continues supplying coherent facts. The goal is not to hide fiscal behavior but to assign each responsibility to the component that can govern and audit it best.

2. The VeriFactu client belongs in the backend, never in browser code

The SDK explicitly states server-side-only usage. Connector API keys must never reach browser JavaScript, public applications or contexts that do not need to hold secrets. In a typical architecture, the frontend sends an action to the product backend; the backend authenticates the user, checks functional permission and calls Puente VeriFactu using the credential associated with the installation. The response is then transformed into product state that can be displayed without exposing sensitive material.

This pattern simplifies rotation and permission separation. Each installation can have different credentials and the server selects the correct one from authenticated business context. It also isolates workers and queues: a job processing invoices does not need to expose its token to the UI. In a multi-tenant product, fiscal tenant should not be derived directly from an organizationId sent by the frontend; that identity must come from credentials and server-side relationships. Security and multitenancy belong to the contract rather than being optional hardening added later.

3. Use the SDK for convenience but design against the REST contract

The SDK exposes preflight, preflightMapped, issue, issueMapped and getFiscalRecord. It also normalizes errors into PuenteVerifactuError. This avoids repeating authentication, parsing and basic error-handling code in every Node integration. Architecture should not depend on SDK-specific magic, however: REST API v1 is the source of the contract. A team using another language can reproduce the same semantics with HTTP and a small adapter. That neutrality matters for Java ERP systems, .NET services, Python applications or backends that do not want another dependency.

Designing against the contract also improves versioning. The adapter knows what it must guarantee —side-effect-free preflight, idempotent issue, recordId lookup and structured errors— rather than coupling to internal JavaScript details. The SDK can evolve while preserving that surface. Tests can mock the client or run Contract Suite against a test connector. Separating protocol from helper library keeps the ecosystem open to different technologies without sacrificing fiscal integration invariants.

4. InvoiceIntent and MappingProfile solve two different ownership scenarios

When your team fully controls the invoice model, generating InvoiceIntent makes the integration another typed contract. You can validate exact decimal serialization, expected date formats and stable sourceInvoiceId for every source operation. This is a clean option for new software or platforms with a well-defined domain. It does not mean the client decides everything: organization, installation, sourceSystem, environment and credentials remain server-resolved even when you build the canonical intent directly.

MappingProfile fits better when the product already has a stable payload that should not be transformed in every release or when several customers run variants of the same software. The server-side profile translates paths, can supply fixed configuration and creates an auditable adaptation point outside client code. preflightMapped and issueMapped reuse the same semantics as canonical mode. Choosing between them is a maintenance decision: both should converge on the same contract and invariants rather than creating different fiscal behavior depending on ingress format.

5. Preflight and structured errors should become part of the product model

Preflight determines whether an intent is valid before an operation is created. SaaS can use it when an invoice is closed, on a review screen or inside a job before issuance. If information is missing, the result should become a functional state such as blocked/requires-review rather than a generic network error. This makes fiscal validation observable in the product without forcing the frontend to understand internal bridge rules.

Normalized errors provide code, status, retryable, correlationId and sanitized details. The product can map known codes to user guidance and send correlationId to support. retryable does not mean retry forever: workers should use backoff and attempt limits, while non-retryable errors should leave the automatic queue. Logging the entire error object indiscriminately is also a bad idea. Client logs should retain minimal operational context and avoid complete payloads, API keys or information already protected by the fiscal layer.

6. Design eventId and idempotency as part of your domain, not as an HTTP detail

Connector Contract Suite v2 requires eventId before creating a new operation. That identifier should represent one concrete, repeatable intent: for example, issuance of an invoice with stable internal identity rather than the timestamp of an HTTP attempt. The adapter derives a deterministic Idempotency-Key so two deliveries of the same event are recognized as the same operation. Different source events —an original invoice and a corrective one, for example— need different identities even when they share an order or customer.

The design must survive retries and deployments. If a worker crashes and reads the same message again, eventId does not change. If a queue redelivers hours later, it still does not change. Generating a new UUID inside the handler would destroy the protection being built. Store eventId with the local reference and, once known, recordId as well. Support can then reconstruct source operation → eventId → idempotency → recordId without depending on temporary logs.

7. recordId changes the problem: after creation you are reconciling, not trying to issue

Once issue returns recordId, that identifier becomes the durable reference for the operation. status(recordId) queries normalized state and sync with recordId must be limited to that lookup. Contract Suite explicitly checks that preflight and issue are not executed on this branch. This prevents the most dangerous integration bug: interpreting temporary inability to query as permission to create a second operation.

Even if status fails with a retryable error, the solution is to retry reconciliation rather than restart issuance. The product can mark the invoice pending/review and continue later. The UX does not need to expose the full recordId to every user, but support should have controlled access to that reference and correlationId. Reconciliation makes a distributed workflow recoverable: state may be delayed while identity remains intact.

8. Turn adapter invariants into a CI gate with Connector Contract Suite v2

The suite does not depend on the source framework. The module under test exports a sample and createConnectorUnderTest function receiving client, profileId and sourceName. The connector offers four operations: preflight, send, status and sync. The runner checks side-effect-free preflight, required eventId, stable keys, distinct identities for distinct events, lookup by recordId, reconciliation without reissue, issue retry using the same key and source payload immutability. It also verifies that the adapter does not inject tenant, installation, certificates, passphrases, environment or permissions.

The command returns stable JSON with suiteVersion 2 and exit code 1 on failure, so it can plug directly into GitHub Actions or another CI. This creates an objective definition of compatibility with the Puente contract. Not every commercial claim follows from that gate, however: passing the suite does not prove a specific ERP version works, replace a smoke test for that platform or certify regulatory compliance. It should be combined with product-specific business tests and the separate fiscal gate.

9. Webhook is an alternative ingress, not different fiscal semantics

Event-driven products can use POST /v1/webhooks/{profileId}. The event is protected with HMAC-SHA256 and an anti-replay timestamp and keeps idempotent identity. The secret belongs to the installation and profileId selects the authorized mapping. The payload cannot grant itself another tenant. This route integrates platforms that already provide configurable outbound webhooks without requiring a complete REST client inside the product.

The downstream pipeline is still the same: mapping, preflight, idempotency, creation and state. Identity rules therefore remain unchanged. If the provider repeats a webhook, the event should have the same identifier. If the application changes content while retaining identity, it should be detected as conflict rather than silently overwriting an operation. Once recordId exists, the system returns to reconciliation. Choosing webhooks changes ingress mechanics, not contract discipline.

10. Design observability and support without copying secrets or XML into your logs

A distributed integration needs traceability. The product should relate its invoiceId to eventId, recordId, normalized state, last error and correlationId. That set makes it possible to identify the operation being processed and contact support using a common reference. There is no need to copy AEAT XML, certificates or full remote messages into SaaS logs. Doing so increases data exposure and creates two places where security and retention policies must be managed.

The interface can present simplified states —pending, accepted, requires review— while keeping technical details in a support view. For automatic jobs, retry counts and state age help detect stuck queues. A recordId that remains pending for too long should trigger reconciliation or alerting rather than another issue call. Correct observability reinforces the same boundaries as idempotency: separating identity, state and transport prevents destructive decisions made simply because information is temporarily incomplete.

11. Define a technical release gate before the regulatory production gate

An adapter should reach product production only after passing domain tests, Contract Suite, realistic preflight, retry and recovery scenarios. When it integrates a specific platform, add a smoke test for that version or capability before declaring support. Credential rotation and temporary Kairoseth Fiscal unavailability should also be tested. A queue or circuit breaker can protect UX, but unavailability must never turn into repeated creation using new identities.

A different gate comes afterwards: the fiscal gate. Before AEAT production, current regulatory sources, taxpayer identity, certificate, configuration, evidence and explicit authorization are reviewed. The software product can be technically ready while fiscal production remains closed. Keeping both gates separate prevents a software release from accidentally becoming a regulatory decision. Connector Contract Suite supplies integration evidence; the production gate supplies the corresponding fiscal and operational decision.

KEY TAKEAWAYS

What to remember

  • Integrate from the backend: API keys, certificates, tenant and environment never belong in browser code.
  • Choose InvoiceIntent or MappingProfile according to model ownership while preserving the same preflight and issue semantics.
  • eventId and Idempotency-Key must represent durable identity; do not regenerate keys on every retry.
  • Once recordId exists, sync only reconciles; a status failure never authorizes a new issue.
  • Run Connector Contract Suite v2 in CI and communicate its scope accurately: integration contract, not regulatory certification.

SOURCES AND TRACEABILITY

Sources used

product-evidencePuente VeriFactu SDK

Kairoseth / Puente VeriFactu

product-evidenceConnector Contract Suite v2

Kairoseth / Puente VeriFactu

product-evidenceAPI v1 contract

Kairoseth / Puente VeriFactu

officialSpanish Tax Agency — invoicing software systems and VERI*FACTU

Agencia Estatal de Administración Tributaria

EXTERNAL RESOURCES

Official Spanish Tax Agency SIF / VERI*FACTU portal

Official technical information, FAQs and regulatory references.

INTERNAL LINKS

NEXT STEP

VeriFactu Software

VeriFactu software for connecting WooCommerce, PrestaShop, ERP, CRM, Excel, custom software and local systems to Spain's VeriFactu framework without replacing the source system.