> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://infonite.dev/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://infonite.dev/_mcp/server.

# Receiving Webhooks

Everything INFONITE does takes as long as an institution takes. Polling for the moment it finishes means asking a question whose answer is *"not yet"* almost every time — for every run you have in flight.

A webhook inverts it: you register an HTTPS URL, and **we send your backend an HTTP `POST` the moment something happens**. This page is how you set that up. What each product sends you is on its own page, linked at the end.

---

## Two pieces, on purpose

#### An endpoint

A **destination**: a name, the HTTPS URL, and optionally one authentication header. Stated once, reused by every event that delivers to it — so rotating a domain is one edit, not six.

#### A rule

A **binding**: *this event goes to that endpoint*. One rule per event, each with its own on/off switch — pausing one while you rework a handler never touches the others.

**With no rules configured, nothing is delivered** — and nothing else changes: polling still works and your executions and sessions run exactly the same. Webhooks are how you stop asking, not a condition for anything to work.

---

## Setting it up

#### Open your application's settings

Console → your application → **App settings** → the **Webhooks** tab. Everything below happens on that one screen: endpoints at the top, rules underneath — and the number beside the tab is how many events are bound right now.

![Application Settings in the Console, with the Webhooks tab selected: the Endpoints card above and the Rules card below](https://fdr-prod-docs-files-public.s3.us-east-1.amazonaws.com/infonite.docs.buildwithfern.com/97ac311520856b78350398f109720fc6dfa7c5d33d055ed76617cbc1aa18f73d/docs/assets/shots/console-webhooks-where-light.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIA6KXJSKKNFOCF7G4B%2F20260914%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260914T090028Z&X-Amz-Expires=604800&X-Amz-Signature=efc46121fa8d62797f8bd19a0ef91ea1ff803c46ba0a185a6829066d416ae435&X-Amz-SignedHeaders=host&x-amz-checksum-mode=ENABLED&x-id=GetObject)![Application Settings in the Console, with the Webhooks tab selected: the Endpoints card above and the Rules card below](https://fdr-prod-docs-files-public.s3.us-east-1.amazonaws.com/infonite.docs.buildwithfern.com/d4ea03307156094c1db7a209cbb5dd0fa08c503f7f450d3e0e0e672e501d9117/docs/assets/shots/console-webhooks-where-dark.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIA6KXJSKKNFOCF7G4B%2F20260914%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260914T090028Z&X-Amz-Expires=604800&X-Amz-Signature=cc67492d3be2d88b2bb8e116a896a607f8d1d5d17dbb40f8915db8a006693dfe&X-Amz-SignedHeaders=host&x-amz-checksum-mode=ENABLED&x-id=GetObject)

#### Register an endpoint

**New endpoint**, then a name (how the rules will refer to it) and the public **HTTPS** URL. If you add an authentication header, its name is lowercase — `authorization` or a custom `x-…` — and **its value is write-only**: it is stored encrypted and nothing in the platform will ever read it back to you. Keep your own copy when you mint it.

![The Endpoints card in the Console, with the New endpoint button highlighted](https://fdr-prod-docs-files-public.s3.us-east-1.amazonaws.com/infonite.docs.buildwithfern.com/17725306447d55b7997fe64a4e1b551fbc9149744ef0c2d75c6f2ba940b58040/docs/assets/shots/console-webhook-new-endpoint-light.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIA6KXJSKKNFOCF7G4B%2F20260914%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260914T090028Z&X-Amz-Expires=604800&X-Amz-Signature=1191839994f73081e7be5a6323aad269cb618a35c3169b4d5bcb4ba2dfaf54cc&X-Amz-SignedHeaders=host&x-amz-checksum-mode=ENABLED&x-id=GetObject)![The Endpoints card in the Console, with the New endpoint button highlighted](https://fdr-prod-docs-files-public.s3.us-east-1.amazonaws.com/infonite.docs.buildwithfern.com/36db049ec2f1d08d82eace69ee2d41d9989550d3718c2f7c65dc8aee4084fa92/docs/assets/shots/console-webhook-new-endpoint-dark.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIA6KXJSKKNFOCF7G4B%2F20260914%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260914T090028Z&X-Amz-Expires=604800&X-Amz-Signature=28235b01e40e021d97aff06433f205e8b836e21f59101d65e3ffa51db1d6be21&X-Amz-SignedHeaders=host&x-amz-checksum-mode=ENABLED&x-id=GetObject)

#### Send a test delivery

Every endpoint has a **test** action. Use it before you bind anything: it proves the URL resolves, that TLS is valid and that your handler answers `2xx` — and it shows you the status we got back. A destination that fails the test will fail every real event.

**If the test never arrives, look at your firewall first.** Every delivery leaves from a fixed set of addresses, and the Console lists them on this same screen — [allow-list them](#where-the-calls-come-from) and try again.

![The same card, with the test-delivery action on an endpoint highlighted and its last result beside it](https://fdr-prod-docs-files-public.s3.us-east-1.amazonaws.com/infonite.docs.buildwithfern.com/dc32845e565b5ffeed583a47fbf3a11fc60ac62f22a9c02e1ea2b22738405b92/docs/assets/shots/console-webhook-test-delivery-light.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIA6KXJSKKNFOCF7G4B%2F20260914%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260914T090028Z&X-Amz-Expires=604800&X-Amz-Signature=e406903fea0f8bc3cf7dc27d8a95effc2262a973fded33a648dc1c7daad68f65&X-Amz-SignedHeaders=host&x-amz-checksum-mode=ENABLED&x-id=GetObject)![The same card, with the test-delivery action on an endpoint highlighted and its last result beside it](https://fdr-prod-docs-files-public.s3.us-east-1.amazonaws.com/infonite.docs.buildwithfern.com/15c4b238fbcd0dba96f9214f14987686e6c80f5c284b50f80ec2a301213d39b0/docs/assets/shots/console-webhook-test-delivery-dark.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIA6KXJSKKNFOCF7G4B%2F20260914%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260914T090028Z&X-Amz-Expires=604800&X-Amz-Signature=f4b7b756e28428fe3ed8d595bc4f2bd1ef77c7e2616edb0bf736fc68ac38398e&X-Amz-SignedHeaders=host&x-amz-checksum-mode=ENABLED&x-id=GetObject)

#### Bind the events you want

**New webhook rule** opens a choice before anything else, and it is the one that matters: an **engine rule** or a **flow rule**.

* An **engine rule** binds an execution event — `started`, `action_required`, `auth_ended`, `ended` — and fires on [direct executions](/direct-executions/overview).
* A **flow rule** binds one flow's own events, and fires on that flow's sessions.

Then the event, and the endpoint it delivers to. The table keeps the two families apart for the same reason: they are two vocabularies, not one list. Leave a rule disabled while your handler is not ready — it changes nothing else.

![The Rules card in the Console: one row per event, each bound to an endpoint and with its own enabled switch](https://fdr-prod-docs-files-public.s3.us-east-1.amazonaws.com/infonite.docs.buildwithfern.com/a67496270248f2cbaf7400984f55609e35e7eebdd04324b315795c8349df0f76/docs/assets/shots/console-webhook-new-rule-light.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIA6KXJSKKNFOCF7G4B%2F20260914%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260914T090028Z&X-Amz-Expires=604800&X-Amz-Signature=418c64e87fa6c4291cb76ce2234ac34f125d65f9a8e29b6c6e87807dcff23173&X-Amz-SignedHeaders=host&x-amz-checksum-mode=ENABLED&x-id=GetObject)![The Rules card in the Console: one row per event, each bound to an endpoint and with its own enabled switch](https://fdr-prod-docs-files-public.s3.us-east-1.amazonaws.com/infonite.docs.buildwithfern.com/39447c4ae532e1cd912018e4eecf1647e7850f9cd23be837c458adef8d88ff6f/docs/assets/shots/console-webhook-new-rule-dark.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIA6KXJSKKNFOCF7G4B%2F20260914%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260914T090028Z&X-Amz-Expires=604800&X-Amz-Signature=a6d07f21ba84f3a336a695bdccd220246482844948d39ea35c31d49cb416f537&X-Amz-SignedHeaders=host&x-amz-checksum-mode=ENABLED&x-id=GetObject)

**Engine rules fire on direct executions only.** When an engine runs *inside* a flow, the flow's own rules deliver the events and the engine rules do not — otherwise one journey would notify you twice, from two vocabularies.

---

## The destination URL

#### HTTPS, to a public host

Plain `http`, private addresses (`10.x`, `192.168.x`, `172.16-31.x`), loopback and link-local ranges, and internal-only names are all refused. A stored destination is fired from inside our platform, so it has to prove it points at the open internet.

#### No query string of its own

Register `https://api.yourcompany.com/webhooks/infonite`, not `…/infonite?source=x` — a query string you store is removed. The query string belongs to the delivery, which fills it in at send time, and a stored one could only collide with it.

#### Template variables, if you want them in the path

Parts of the URL can be replaced with the event's own values before the call is made — `{customer_id}`, `{event}`, `{app_id}` and more. **The list is per product**, because the values are: [direct executions](/direct-executions/webhooks#the-destination-url), [Spain Public Administration](/flows/spain-public-administration/webhooks#the-destination-url).

---

## Authenticating the call

Your endpoint is on the public internet, so it needs to know that a delivery really came from us.

#### Pick the header

Either `authorization` or a custom `x-…` header of your own — `x-webhook-token`, `x-infonite-signature`, whatever your gateway already understands. Headers the delivery itself owns (`Host`, `Content-Type`) cannot be overridden.

#### Mint the secret

You choose the value, up to 1024 characters: a bearer token, a shared secret, an API-gateway key. It is stored **encrypted**, and it is **write-only** — you can replace it, never read it.

#### Verify it on arrival

Reject anything that does not carry it, with a constant-time comparison. A `401` from you is a delivery failure on our side, which is exactly what you want for a forged call.

**Never put credentials in the URL.** A destination like `https://user:pass@api.yourcompany.com/hook` is refused: it would be stored in clear and shown on every read, which is the opposite of what the encrypted header beside it is for. The same goes for a token in a query parameter.

### Where the calls come from

**Deliveries leave our platform from a fixed set of addresses.** If your endpoint sits behind a firewall that only accepts known sources, allow-list all of them:

| Environment        | IP address       |
| :----------------- | :--------------- |
| **Production**     | `57.130.72.231`  |
| **Production**     | `51.75.169.100`  |
| **Pre-production** | `151.80.208.150` |

The same list is in the **Console**, on your application's Webhooks screen, beside the endpoints — that copy is the one to trust if this page and that screen ever disagree, and it is where a change would appear first.

**An allow-list is a filter, not an authentication.** It proves a request came from INFONITE's infrastructure — not that it came from your application, and not that the body was not tampered with. Keep the endpoint's authentication header: the allow-list narrows who can reach you, the header proves who is calling.

---

## Answering a delivery

| Rule                                   | Why                                                                                                                                                                 |
| :------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Any `2xx` means "received"**         | The body of your response is not read. Do not put a decision in it.                                                                                                 |
| **The call times out after 5 seconds** | Acknowledge first, process afterwards. Queue the payload and return — do not score, decide or write to a slow system inside the request.                            |
| **Treat every handler as idempotent**  | The same event may reach you more than once. Key your side effects on the id in the payload plus the event name.                                                    |
| **Do not design around a retry**       | If your endpoint was down when an event fired, the reliable recovery is to ask: the state endpoint of the product is the authoritative answer, whatever you missed. |

---

## What each product sends you

#### [Direct Executions](/direct-executions/webhooks)

Four events about a run: started, a challenge to answer, the login resolved, and the execution finished. They never carry records — the data is read back over an authenticated call.

#### [Spain Public Administration](/flows/spain-public-administration/webhooks)

Four events about a session: opened, consent signed, partial results, and finished. These **can** carry the records, encrypted, when the session was created with a key.