> 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.

# Webhooks

An execution takes as long as an institution takes. Polling for the end means asking a question whose answer is *"not yet"* almost every time — and asking it for every execution you have in flight.

A webhook inverts it: you register an HTTPS URL, and **INFONITE sends your backend an HTTP `POST` the moment something happens**. You do nothing until there is something to do.

**These events carry no data. That is the design.**

An execution event tells you *that* something happened — it never carries extracted records. The data is read back over an authenticated call, which is what keeps somebody's financial life off a public endpoint. If you are looking for the encrypted-payload delivery, that is a [flow](/flows/spain-public-administration/webhooks) feature, and it exists there because a flow session has a key you supplied.

**Setting one up is the same for every product**: an endpoint (the destination) and a rule (this event goes there), configured once per application in the Console. [Receiving Webhooks](/guides/webhooks) is the whole procedure — including the authentication header, the URL rules and the test delivery. This page is about **what this product sends you**.

---

## The four events

Every one carries the same envelope, so a single handler can serve all four. You subscribe to as many or as few as you need.

#### [Execution started](/api-reference/direct-executions/direct-executions-api/webhooks/webhook-direct-executions-started)

`started` — the execution left the queue and the engine began working. Start your own clock here.

#### [The source needs an answer](/api-reference/direct-executions/direct-executions-api/webhooks/webhook-direct-executions-action-required)

`action_required` — something only your customer can give. **The execution is waiting for you** — see [Challenges and MFA](/direct-executions/challenges).

#### [Login resolved](/api-reference/direct-executions/direct-executions-api/webhooks/webhook-direct-executions-auth-ended)

`auth_ended` — the login finished, one way or the other. It separates *we could not get in* from *we got in and the extraction failed*.

#### [Execution finished](/api-reference/direct-executions/direct-executions-api/webhooks/webhook-direct-executions-ended)

`ended` — the run is over. **Subscribe to this one even if you subscribe to nothing else.**

The name in each card — `started`, `action_required`, `auth_ended`, `ended` — is the value that arrives in the `event` field of the payload, and the one a webhook rule is bound to.

### Which ones you actually need

Two of the four earn their place in most integrations, and the other two are shortcuts. It is worth deciding on purpose rather than subscribing to everything:

| Event             | Why you would want it                                                                                                                                                                                                                                                                                                                                                           |
| :---------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `ended`           | **The one you need.** It is the only event that says the results are there, the only one delivered for an execution that never got to run, and the only one we re-deliver if your endpoint was down.                                                                                                                                                                            |
| `action_required` | **The one you cannot replace with patience.** The execution expires unless you answer, on the institution's timetable. Without it you are polling every few seconds for every run in flight.                                                                                                                                                                                    |
| `auth_ended`      | A **shortcut to the verdict on the credentials**, seconds into a run that may take minutes — and, with tokenization, the proof that a stored access still works: `AUTH_OK` is also the moment a `ticket` becomes exchangeable, because the credentials behind it are stored only when the login succeeds. Redundant if you can wait for `ended`, where the same reason arrives. |
| `started`         | A **clock**, not an instruction. It separates queue time from working time, which is what you need when somebody asks why a case is slow.                                                                                                                                                                                                                                       |

---

## What a delivery looks like

A single `POST`, with a JSON body, to the URL you registered.

**`A delivery, as your server sees it`**

```http title="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": "ExecutionEvent",
  "app_id": "4aa3dcbab3287e2385bb5cec",
  "customer_id": "my-customer-1",
  "execution_id": "6aa3d8b418d1c5dc9a8e3d36",
  "session_id": "5aa3dca503e37e6809539a58",
  "external_execution_id": "case-A-1029",
  "locking_hash": "v1.36GHPwfDK-UUZGG4fb1jtg",
  "producer": "engine",
  "event": "ended",
  "date": "2026-09-12T10:17:31+00:00",
  "customer_available": true,
  "status_reason": "COMPLETED",
  "status_message": null
}
```

### Schema (`WebhookEndedPayload`)

The execution is over and nothing else will change on it. `status_reason` says how it ended —
`COMPLETED`, `PARTIAL` when some features answered and others did not, or the reason it could
not. This is the event that closes your case: read the results now.

```yaml
components:
  schemas:
    WebhookEndedPayloadProducer1:
      type: string
      enum:
        - accounts_read
        - cards_read
        - deposits_read
        - loans_read
        - credits_read
        - investment_accounts_read
        - funds_read
        - stocks_read
        - pensions_read
        - accounts_certificates
        - direct_debits_read
        - customer_information_read
        - source_contracts_read
        - cloud_resource_read
        - supplier_invoices_read
        - client_invoices_read
        - labor_check
        - public_pensions
        - public_document_verification
        - yearly_individual_tax
        - vehicles_data
        - driver_data
        - academic_data
        - properties_data
        - credit_registry_data
      description: |-
        Features allowed by an engine
        Allowed values are:
         - `accounts_read`: Financial Accounts Read
        - `cards_read`: Financial Cards Read
        - `deposits_read`: Financial Deposits Read
        - `loans_read`: Financial Loans Read
        - `credits_read`: Financial Credits Read
        - `investment_accounts_read`: Investment Accounts Read
        - `funds_read`: Investment Funds Read
        - `stocks_read`: Investment Stocks Read
        - `pensions_read`: Pension Plans Read
        - `accounts_certificates`: Account Certificates Read
        - `direct_debits_read`: Direct Debits Read
        - `customer_information_read`: Customer Information Read
        - `source_contracts_read`: Source Contracts Read
        - `cloud_resource_read`: Cloud Provider Resource Read
        - `supplier_invoices_read`: Supplier Invoices Read
        - `client_invoices_read`: Client Invoices Read
        - `labor_check`: Labor Check
        - `public_pensions`: Public Pensions
        - `public_document_verification`: Public Document Verification
        - `yearly_individual_tax`: Yearly Taxes for Individuals
        - `vehicles_data`: Vehicles Data
        - `driver_data`: Driver Data
        - `academic_data`: Academic Data
        - `properties_data`: Properties Data
        - `credit_registry_data`: Credit Registry Data
      title: WebhookEndedPayloadProducer1
    WebhookEndedPayloadProducer:
      oneOf:
        - type: string
          enum:
            - engine
        - $ref: '#/components/schemas/WebhookEndedPayloadProducer1'
      description: >-
        Who raised it: `engine` for the execution itself — the login, typically
        — or the feature's own code when a single feature is what needs
        something. An `action_required` from the accounts reader says
        `accounts_read`, so you know what is being asked for before reading the
        form.
      title: WebhookEndedPayloadProducer
    WebhookEndedPayloadStatusReason0:
      type: string
      enum:
        - ACCEPTED
        - WAITING
        - RUNNING
        - ASYNC_WAIT
      description: |-
        Reasons for an ongoing status
        Allowed values are:
         - `ACCEPTED`: Execution has been accepted and is waiting to be processed
        - `WAITING`: The execution is warming up
        - `RUNNING`: The execution is running
        - `ASYNC_WAIT`: The execution is waiting for an async task to complete.
      title: WebhookEndedPayloadStatusReason0
    WebhookEndedPayloadStatusReason1:
      type: string
      enum:
        - MFA_REQUIRED
        - INPUT_REQUIRED
      description: >-
        Reasons for an action required status

        Allowed values are:
         - `MFA_REQUIRED`: The execution is waiting for user to solve a MFA challenge
        - `INPUT_REQUIRED`: The execution is waiting for dynamic input from the
        user
      title: WebhookEndedPayloadStatusReason1
    WebhookEndedPayloadStatusReason2:
      type: string
      enum:
        - USER_CANCELLED
        - CLIENT_CANCELLED
        - SYSTEM_CANCELLED
        - TIMEOUT
        - ACTION_TIMEOUT
        - CUSTOMER_INTERVENTION_REQUIRED
      description: >-
        Reasons for an aborted status

        Allowed values are:
         - `USER_CANCELLED`: Cancelled by the user
        - `CLIENT_CANCELLED`: Cancelled by the client

        - `SYSTEM_CANCELLED`: Cancelled by System

        - `TIMEOUT`: The execution was cancelled due to a timeout

        - `ACTION_TIMEOUT`: Cancelled due to an action timeout

        - `CUSTOMER_INTERVENTION_REQUIRED`: Can not be processed without
        customer intervention
      title: WebhookEndedPayloadStatusReason2
    WebhookEndedPayloadStatusReason3:
      type: string
      enum:
        - BAD_CONFIGURATIONS
        - BAD_PROXY_CONFIGURATION
        - ENCRYPTION_ERROR
        - INCORRECT_PARAMETERS_FORMAT
        - INCORRECT_RESUME_FORMAT
        - T10N_FORBIDDEN
        - T10N_REJECTED
        - T10N_NOT_AVAILABLE
        - T10N_EXCEPTION
      description: >-
        Reasons for a configuration error status

        Allowed values are:
         - `BAD_CONFIGURATIONS`: Rejected due to an invalid configuration
        - `BAD_PROXY_CONFIGURATION`: Rejected due to an invalid proxy
        configuration

        - `ENCRYPTION_ERROR`: Rejected due to an encryption error

        - `INCORRECT_PARAMETERS_FORMAT`: Rejected due to invalid resume payload

        - `INCORRECT_RESUME_FORMAT`: Rejected due to invalid resume payload

        - `T10N_FORBIDDEN`: Current application is not allowed to use
        tokenization

        - `T10N_REJECTED`: Token is locked or has been revoked

        - `T10N_NOT_AVAILABLE`: Tokenization is not available for this engine

        - `T10N_EXCEPTION`: Unexpected exception during tokenization
      title: WebhookEndedPayloadStatusReason3
    WebhookEndedPayloadStatusReason4:
      type: string
      enum:
        - COMPLETED
      description: |-
        Reasons for a completed status
        Allowed values are:
         - `COMPLETED`: Completed successfully
      title: WebhookEndedPayloadStatusReason4
    WebhookEndedPayloadStatusReason5:
      type: string
      enum:
        - PARTIAL
      description: |-
        Reasons for a partial status
        Allowed values are:
         - `PARTIAL`: Completed with errors
      title: WebhookEndedPayloadStatusReason5
    WebhookEndedPayloadStatusReason6:
      type: string
      enum:
        - FAILED
      description: |-
        Reasons for a failed status
        Allowed values are:
         - `FAILED`: Failed to complete the execution
      title: WebhookEndedPayloadStatusReason6
    WebhookEndedPayloadStatusReason7:
      type: string
      enum:
        - AUTH_OK
        - INCORRECT_CREDENTIALS
        - INCORRECT_MFA
        - DUPLICATED_SESSION
        - FRIEZED_CREDENTIALS
        - BLOCKED_USER
        - MANUAL_INTERVENTION
        - CHANGE_PASSWORD
        - INCOMPATIBLE_ACCESS
      description: >-
        Reasons for an authentication error status

        Allowed values are:
         - `AUTH_OK`: Authenticated successfully
        - `INCORRECT_CREDENTIALS`: Authentication failed due to incorrect
        credentials

        - `INCORRECT_MFA`: Authentication failed due to incorrect MFA

        - `DUPLICATED_SESSION`: Authentication failed due to duplicated session

        - `FRIEZED_CREDENTIALS`: Authentication failed due to temporally locked
        credentials

        - `BLOCKED_USER`: Authentication failed due to blocked user

        - `MANUAL_INTERVENTION`: The source requests manual intervention to
        continue

        - `CHANGE_PASSWORD`: Authentication failed due to password change
        required

        - `INCOMPATIBLE_ACCESS`: Authentication failed due to incompatible
        access
      title: WebhookEndedPayloadStatusReason7
    WebhookEndedPayloadStatusReason8:
      type: string
      enum:
        - UNHANDLED_AUTH_ERROR
      description: |-
        Reasons for an unhandled authentication error status
        Allowed values are:
         - `UNHANDLED_AUTH_ERROR`: Unhandled authentication error
      title: WebhookEndedPayloadStatusReason8
    WebhookEndedPayloadStatusReason9:
      type: string
      enum:
        - ENGINE_UNAVAILABLE
        - ENGINE_BANNED
        - OUT_OF_SERVICE
        - ALREADY_EXECUTING
        - AUTO_CAPTCHA_ERROR
        - PROXY_ERROR
        - NETWORK_ERROR
        - INTERNAL_ERROR
      description: >-
        Reasons for a temporary error status

        Allowed values are:
         - `ENGINE_UNAVAILABLE`: Engine is temporarily unavailable
        - `ENGINE_BANNED`: Source banned the engine

        - `OUT_OF_SERVICE`: The execution cannot be processed due to source
        unavailability

        - `ALREADY_EXECUTING`: Engine is already executing

        - `AUTO_CAPTCHA_ERROR`: Engine warmup failed due to captcha error

        - `PROXY_ERROR`: Engine warmup failed due to proxy error

        - `NETWORK_ERROR`: Engine warmup failed due to network error

        - `INTERNAL_ERROR`: Engine warmup failed due to internal error
      title: WebhookEndedPayloadStatusReason9
    WebhookEndedPayloadStatusReason10:
      type: string
      enum:
        - FEATURE_ACCEPTED
        - FEATURE_WAITING
        - FEATURE_RUNNING
        - FEATURE_ASYNC_WAIT
        - FEATURE_ACTION_REQUIRED
        - FEATURE_ACTION_REJECTED
        - FEATURE_INCORRECT_PARAMETERS_FORMAT
        - FEATURE_ABORTED
        - FEATURE_TIMEOUT
        - FEATURE_ACTION_TIMEOUT
        - FEATURE_CUSTOMER_INTERVENTION_REQUIRED
        - FEATURE_CONFIGURATION_ERROR
        - FEATURE_COMPLETED
        - FEATURE_PARTIAL
        - FEATURE_FAILED
        - FEATURE_DISABLED
        - FEATURE_NOT_IMPLEMENTED
        - FEATURE_TEMPORARY_ERROR
        - FEATURE_INTERNAL_ERROR
      description: >-
        Status for features

        Allowed values are:
         - `FEATURE_ACCEPTED`: The feature is accepted and is waiting to be processed
        - `FEATURE_WAITING`: The feature is scheduled to be processed

        - `FEATURE_RUNNING`: The feature is running

        - `FEATURE_ASYNC_WAIT`: The feature is waiting for an async task to be
        completed

        - `FEATURE_ACTION_REQUIRED`: Feature is waiting for an input

        - `FEATURE_ACTION_REJECTED`: Feature action was rejected

        - `FEATURE_INCORRECT_PARAMETERS_FORMAT`: Invalid parameters format for
        the feature

        - `FEATURE_ABORTED`: The feature has been aborted

        - `FEATURE_TIMEOUT`: The feature has been aborted due to a timeout

        - `FEATURE_ACTION_TIMEOUT`: The feature has been aborted due to an
        action timeout

        - `FEATURE_CUSTOMER_INTERVENTION_REQUIRED`: The feature needs customer
        intervention

        - `FEATURE_CONFIGURATION_ERROR`: The execution has been rejected due to
        a configuration error

        - `FEATURE_COMPLETED`: The execution has been completed

        - `FEATURE_PARTIAL`: The execution has been completed with errors

        - `FEATURE_FAILED`: The execution has failed

        - `FEATURE_DISABLED`: The feature is disabled

        - `FEATURE_NOT_IMPLEMENTED`: The feature is not implemented yet

        - `FEATURE_TEMPORARY_ERROR`: The execution cannot be processed due to a
        temporary error

        - `FEATURE_INTERNAL_ERROR`: The execution cannot be processed due to an
        internal error
      title: WebhookEndedPayloadStatusReason10
    WebhookEndedPayloadStatusReason:
      oneOf:
        - $ref: '#/components/schemas/WebhookEndedPayloadStatusReason0'
        - $ref: '#/components/schemas/WebhookEndedPayloadStatusReason1'
        - $ref: '#/components/schemas/WebhookEndedPayloadStatusReason2'
        - $ref: '#/components/schemas/WebhookEndedPayloadStatusReason3'
        - $ref: '#/components/schemas/WebhookEndedPayloadStatusReason4'
        - $ref: '#/components/schemas/WebhookEndedPayloadStatusReason5'
        - $ref: '#/components/schemas/WebhookEndedPayloadStatusReason6'
        - $ref: '#/components/schemas/WebhookEndedPayloadStatusReason7'
        - $ref: '#/components/schemas/WebhookEndedPayloadStatusReason8'
        - $ref: '#/components/schemas/WebhookEndedPayloadStatusReason9'
        - $ref: '#/components/schemas/WebhookEndedPayloadStatusReason10'
      description: >-
        The precise reason for the state at the moment of the event — one of the
        execution reasons, or a feature's own when `producer` names a feature.
        Branch on this and not on the message: it is a stable code. [Every
        reason by family](/direct-executions/lifecycle#every-reason-by-family).
      title: WebhookEndedPayloadStatusReason
    WebhookEndedPayload:
      type: object
      properties:
        app_id:
          type: string
          format: object-id
          description: >-
            The application this execution was launched with — the one your
            secret belongs to. Worth keeping when your product uses more than
            one, a sandbox and a production app being the usual case: every
            record and every event we send carries it.
        customer_id:
          type: string
          description: >-
            The `customer_id` you supplied when the execution was initialised,
            returned as you sent it — so an answer can be routed to the right
            case with no lookup on your side.
        session_id:
          type:
            - string
            - 'null'
          format: object-id
        execution_id:
          type: string
          format: object-id
          description: >-
            The execution this belongs to — the value you poll for the state and
            address every results endpoint with. Always present here: an event
            exists because an execution does.
        external_execution_id:
          type:
            - string
            - 'null'
        locking_hash:
          type:
            - string
            - 'null'
        event_class:
          type: string
          enum:
            - ExecutionEvent
          default: ExecutionEvent
          description: >-
            Payload class discriminator — always this value for an execution
            event. If the same endpoint

            also receives flow events, branch on it first.
        producer:
          $ref: '#/components/schemas/WebhookEndedPayloadProducer'
          default: engine
          description: >-
            Who raised it: `engine` for the execution itself — the login,
            typically — or the feature's own code when a single feature is what
            needs something. An `action_required` from the accounts reader says
            `accounts_read`, so you know what is being asked for before reading
            the form.
        event:
          type: string
          enum:
            - ended
          description: 'The event type: `ended`.'
        date:
          type: string
          format: datetime
          description: >-
            When the event was raised, in UTC. Deliveries can arrive out of
            order — a retry is delivered after whatever came next — so order
            your own records by this, not by arrival.
        customer_available:
          type: boolean
          description: >-
            Whether the execution was started as one a person can answer
            (`customer_interaction_available`). When `false`, a challenge from
            the source cannot be solved and the execution ends instead of
            waiting.
        status_reason:
          $ref: '#/components/schemas/WebhookEndedPayloadStatusReason'
          description: >-
            The precise reason for the state at the moment of the event — one of
            the execution reasons, or a feature's own when `producer` names a
            feature. Branch on this and not on the message: it is a stable code.
            [Every reason by
            family](/direct-executions/lifecycle#every-reason-by-family).
        status_message:
          type:
            - string
            - 'null'
        data:
          type:
            - object
            - 'null'
          additionalProperties:
            description: Any type
      required:
        - app_id
        - customer_id
        - execution_id
        - event
        - date
        - customer_available
        - status_reason
      title: WebhookEndedPayload
```

**`customer_id` and `external_execution_id` are yours.** They are the strings *you* supplied when you started the execution, so a handler can route the event straight to the right case without a lookup table.

**`event_class` is the discriminator to branch on first** if the same endpoint also receives flow events: `ExecutionEvent` is a direct execution, anything else is not.

## 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](/guides/webhooks#the-destination-url). 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.

| Variable                  | Becomes                                                           |
| :------------------------ | :---------------------------------------------------------------- |
| `{execution_id}`          | the execution this event belongs to                               |
| `{external_execution_id}` | **your own reference**, if you attached one when you started it   |
| `{session_id}`            | the session the execution belongs to                              |
| `{customer_id}`           | your identifier for the credentials' owner                        |
| `{app_id}`                | your application                                                  |
| `{locking_hash}`          | the fingerprint of the fixed part of the credentials              |
| `{producer}`              | `engine`, or the feature's code when one feature raised the event |
| `{event}`                 | the event name, e.g. `ended`                                      |
| `{status_reason}`         | the precise reason at the time of the event                       |
| `{customer_available}`    | whether the execution can pause for a person                      |

So `https://api.yourcompany.com/webhooks/infonite/{external_execution_id}` arrives as `…/webhooks/infonite/case-A-1029`, and your router can pick your own case out of the path with no lookup at all.

### Sending your own context along

`hooks_extra_data` is a flat dictionary you set **per execution**, when you start it. Every key and value in it is appended to the delivery as query parameters, and the same template variables work inside them:

**`At execution start`**

```json title="At execution start"
{
  "engine_reference": "DEMOBANKXXXXFIN100ES9999-mobile",
  "customer_id": "my-customer-1",
  "hooks_extra_data": {
    "case": "A-1029",
    "stage": "underwriting",
    "event": "{event}"
  }
}
```

Your endpoint then receives `?case=A-1029&stage=underwriting&event=ended`. It is the natural place for the correlation id of whatever process launched the execution.

**`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.

---

## Configuring your webhooks

Endpoints and rules are per APPLICATION, and they are the same two pieces for every product on the platform: [Receiving Webhooks](/guides/webhooks) is where they are explained, with the Console screens and the test delivery.

**Engine rules fire on direct executions only.** When an engine runs inside a [flow](/flows/overview), that flow's rules deliver the events instead — otherwise one journey would notify you twice, from two vocabularies.