Webhooks
A session does not finish when your customer closes the widget. It finishes when the last public administration answers — which can be seconds later, or minutes later when the CIRBE report from the Banco de España is involved. So the browser leaving is not the end, and the redirect that takes your customer home is not a completion signal.
Polling for the real moment — the synchronous way — means asking a question whose answer is “not yet” almost every time. A webhook is the asynchronous way and it inverts that: you register an HTTPS URL, and INFONITE sends your backend an HTTP POST the moment something happens, server to server, long after the tab is closed if that is when it happens.
Webhooks are the recommended integration. Everything they deliver is also available by polling Get Session State and Get Session Results — keep that path as your fallback, not as your design.
Answer 2xx the moment the payload reaches you, whatever your own processing does with it afterwards: any other status is recorded as a failed delivery even though the event arrived. Answering a delivery has the handler to copy.
The four events
Each event is delivered independently, and you subscribe to as many or as few as you need. Every one carries the same envelope, so a single handler can serve all four.
The status_code of es-public-administration:ended says who ended the session — your customer, you or the platform: see How a session ends.
Which ones you actually need
Two of the four earn their place in most integrations, and the other two are shortcuts:
partial is an early view, never a conclusion. It exists so you can move before the slow sources answer, and the records in it may still change, be corrected, or appear for the first time. Branch on is_closed, exactly as described in knowing when a session is finished.
Both result events carry messages — situations the session detected while it ran, beside the records themselves. The one worth routing is flag: it never means something failed on our side, it means the session found something about the person or the data that deserves a decision on yours. What the session tells you, beside the data.
What a delivery looks like
A single POST, with a JSON body, to the URL you registered.
The envelope is identical across the four events — only event, status_code and data differ:
Fired when the session finishes — successfully with the complete results, or on cancellation/failure with the reason.
Payload class discriminator — always this value for Spain Public Administration events.
The event type: ended.
Always true: the session is over and its results are final — however it ended, and whether
data travels or not. This field, not the widget closing, is what says a session is finished.
Verification results payload.
- Documented decrypted, delivered encrypted. With an
aes_keyon the session this field is theaes::Base64(IV + Ciphertext + Tag)string of the object described here, which decrypts intoFlowResultsSchema; without one it is not in the body at all and the results are read from the session results endpoint instead. - On a session that ended without results — cancelled, timed out, failed — it carries the
reason instead, as
WebhookEndedErrorPayload.
customer_id is yours. It is the identifier you supplied at Create Session, so a handler can route the event to the right case in your system without a lookup table.
The destination URL
The rules a destination has to satisfy — HTTPS, a public host, no query string of its own — are the same for every product and live in Receiving Webhooks. What is specific here is what the platform writes into the path: any of these is replaced with the event’s own value before the call is made.
So https://api.yourcompany.com/webhooks/infonite/{session_id} arrives as …/webhooks/infonite/644599da47847b79c03cc950, and your router can pick the session out of the path.
Sending your own context along
hooks_extra_data is a dictionary you set per session, in settings at Create Session. Every key and value in it is appended to the delivery as query parameters, and the same template variables work inside them:
Your endpoint then receives ?case=A-1029&stage=underwriting&session=644599da47847b79c03cc950. It is the natural place for the correlation id of whatever process opened the session.
hooks_extra_data is not a place for secrets. Query strings end up in access logs, proxies and browser-adjacent tooling. Authentication belongs in the header.
Encrypted payloads — and what travels without a key
We never send personal data over a webhook unless you gave us a key to encrypt it with.
The aes_key you supply at Create Session is not an option that improves the payload — it is the condition for there being a payload at all.
That gives you two delivery modes, and choosing between them is an architectural decision, not a preference:
The rest of the envelope — session_id, customer_id, event, status_code, date — is always in clear, in both modes. It carries no personal information, so you can route, log and deduplicate an event without decrypting or fetching anything.
The keyless mode is a perfectly good design, and for some architectures it is the better one: the sensitive records never touch your webhook endpoint at all, they are pulled over an authenticated call from the service that is going to process them. What is not a design is expecting data to be there without a key — it will not be.
Configuring your webhooks
Endpoints and rules are per APPLICATION, and they are the same two pieces for every product on the platform: Receiving Webhooks is where they are explained, with the Console screens and the test delivery. A flow’s rules are bound to the flow as the producer, not to the engines it runs.
The hooks/list, hooks/update and hooks/delete endpoints are gone. They configured nothing for a long time before they were removed; the Console is where webhooks are configured.