Skip to navigation

Webhooks

Let INFONITE tell your backend what happened, instead of asking it over and over
View as Markdown

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:

EventWhy you would want it
endedThe one you need. It is the only event that says the verification is over, and the only one delivered for a session that never got to produce anything.
partialOnly if you move early. It lets you act on what has arrived while a slow source is still answering — and it obliges you to treat those records as provisional.
consentThe audit moment: when your customer authorised the queries. Worth storing even if you do nothing with it.
startedA clock, not an instruction: it separates we sent them the link from they actually opened it.

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.

A delivery, as your server sees it
POST /webhooks/infonite?case=A-1029 HTTP/1.1
Host: api.yourcompany.com
Content-Type: application/json
Authorization: Bearer <the secret you gave us>
{
"event_class": "EsPublicAdministrationV1FlowEvent",
"app_id": "644599da47847b79c03cc94f",
"customer_id": "cust_esp_202406",
"session_id": "644599da47847b79c03cc950",
"producer": "es-public-administration",
"event": "es-public-administration:ended",
"date": "2026-08-28T09:14:02.881Z",
"status_code": "COMPLETED",
"status_message": null,
"data": { "…": "the results, or aes::… when encrypted" }
}

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.

event_class"EsPublicAdministrationV1FlowEvent"Required

Payload class discriminator — always this value for Spain Public Administration events.

app_idstringRequiredformat: "object-id"
customer_idstringRequired1-64 characters
session_idstringRequiredformat: "object-id"
producer"es-public-administration"Required
The flow that produced the event.
event"es-public-administration:ended"Required

The event type: ended.

status_codeenumRequired
Describes the status of a flow.
is_closedtrueRequired

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.

datestringOptionalformat: "datetime"
status_messagestring or nullOptional
dataobjectOptional

Verification results payload.

  • Documented decrypted, delivered encrypted. With an aes_key on the session this field is the aes::Base64(IV + Ciphertext + Tag) string of the object described here, which decrypts into FlowResultsSchema; 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.

VariableBecomes
{session_id}the session this event belongs to
{customer_id}the identifier you supplied at session creation
{app_id}your application
{producer}es-public-administration
{event}the full event name, e.g. es-public-administration:ended
{status_code}the session status at the time of the event

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:

At session creation
{
"customer_id": "cust_esp_202406",
"settings": {
"hooks_extra_data": {
"case": "A-1029",
"stage": "underwriting",
"session": "{session_id}"
}
}
}

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:

At session creationWhat the event carriesHow you get the results
With aes_keyThe full data, as aes::Base64(IV + Ciphertext + Tag)Decrypt it in your handler. Nothing else to call.
Without aes_keyNo data at all — the field is absentThe event is a notification: it tells you that something happened, and you fetch the records yourself over the authenticated API with Get Session Results.

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.