> For the complete documentation index, see [llms.txt](https://wallee.gitbook.io/documentation/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://wallee.gitbook.io/documentation/developer-tools/webhooks.md).

# Webhooks

Webhooks allow your application to receive real-time notifications when specific events occur in our system. Instead of polling the API for changes, we push updates directly to your endpoint via HTTP POST requests the moment they happen. This is the recommended way to react to state changes — for example, to trigger order fulfillment when a transaction reaches the `Fulfill` state (see [Transaction States](/documentation/payments/overview/transaction-states.md)).

### Typical use cases

* **Instant fulfillment** — trigger warehouse picking or digital product delivery the moment a transaction is authorized or fulfilled.
* **Automated bookkeeping** — feed payment data directly into your ERP or accounting software without manual data entry.
* **Inventory synchronization** — update stock levels across all sales channels as soon as a sale is finalized.
* **Proactive subscription recovery** — react immediately when a subscription payment fails, for example by sending a dunning email.

### Setup and configuration

Configuring webhooks takes two steps: register a **webhook URL** (the endpoint on your server that receives the payloads), then create a **webhook listener** (defines which entity types and states trigger a notification to that URL).

#### Webhook URLs

Webhook URLs are managed centrally under **Space > Settings > General > Webhook URLs**. Because the URL is stored in one place, you can change your endpoint without touching the individual listener configurations. You can also create webhook URLs programmatically using our API or SDKs.

#### Webhook listeners

Webhook listeners are configured under **Space > Settings > General > Webhook Listeners**. For each listener you define:

* which **entity type** to observe (e.g. Transaction, Subscription — only certain entities are subscribable; the list of available entities is shown when creating a listener),
* which **states** should trigger a notification, or all state changes,
* whether **payload signing** (and with it the `state` field) is enabled,
* other metadata such as name, identity, deletion schedule and versioning.

### Receiving webhooks

When an event is triggered, we send an HTTP POST request to your registered URL. Your endpoint must return an HTTP `200` response to confirm receipt.

If your endpoint does not confirm the delivery, {inject-platform-name} retries with increasing intervals over a period of up to 80 hours. After 14 failed attempts, the listener is automatically deactivated to avoid endless attempts against an unresponsive endpoint.

All webhook invocations — successful or failed — are logged under **Space > Settings > General > Webhook Invocations**, including error details and the response returned by your server. This is the first place to look when notifications do not arrive as expected.

### Webhook payload structure

The basic payload contains metadata about the event — no business data. Your application must make a follow-up API call to fetch the entity details if needed.

| Field                         | Description                                                 |
| ----------------------------- | ----------------------------------------------------------- |
| `eventId`                     | Unique ID for the event                                     |
| `entityId`                    | ID of the entity that changed                               |
| `listenerEntityId`            | ID of the entity type                                       |
| `listenerEntityTechnicalName` | Technical name of the entity type                           |
| `spaceId`                     | Your space ID                                               |
| `webhookListenerId`           | ID of the listener                                          |
| `timestamp`                   | Time of the event                                           |
| `state` (optional)            | State of the entity, included if payload signing is enabled |

#### Basic payload example

```json
{
  "eventId": 138833842,
  "entityId": 63762876,
  "listenerEntityId": 1472041829003,
  "listenerEntityTechnicalName": "Transaction",
  "spaceId": 30140,
  "webhookListenerId": 285874,
  "timestamp": "2022-08-23T14:20:53+0000"
}
```

#### Payload with state (signed)

```json
{
  "eventId": 138833842,
  "entityId": 63762876,
  "listenerEntityId": 1472041829003,
  "listenerEntityTechnicalName": "Transaction",
  "spaceId": 30140,
  "webhookListenerId": 285874,
  "timestamp": "2022-08-23T14:20:53+0000",
  "state": "PROCESSING"
}
```

If the `state` field is present, your application can act on the state change directly — saving the additional API call to check the entity's state.

### Security: payload signing

A robust signing mechanism based on **ECDSA with SHA-256** guarantees the authenticity and integrity of every payload: it proves the notification really came from us and has not been tampered with. Payloads are hashed and signed with a private key; the matching public key is exposed via the **Webhook Encryption Service** using a `keyId`. Payload signing is enabled per listener.

Signed requests include an `x-signature` header:

```
x-signature: algorithm=SHA256withECDSA, keyId=2dcd5b38-78a1-47ea-a1c7-ed760403d88c, signature=jXt3fB8v7v/ea2MGUXj+c+...
```

| Field       | Description                                               |
| ----------- | --------------------------------------------------------- |
| `algorithm` | Always `SHA256withECDSA`                                  |
| `keyId`     | Identifier of the public key used to verify the signature |
| `signature` | The encoded signature itself                              |

To verify a payload, retrieve the public key for the `keyId` from {inject-platform-name} and validate the signature against the received payload. We recommend using one of our official [SDKs](/documentation/developer-tools/sdks.md) — they provide built-in methods that handle the key retrieval and signature verification for you.

{% hint style="warning" %}
Payload signing is mandatory. Integrate the signature verification from the start of your development.
{% endhint %}

{% hint style="warning" %}
Always serve your webhook endpoint over HTTPS to protect the payload and headers in transit.
{% endhint %}

### Firewall requirements

To ensure webhook delivery, allow incoming HTTPS requests from the following IP addresses in your firewall and at your hosting provider:

```
52.211.247.160
52.211.171.77
52.211.239.229
52.211.209.173
52.208.210.84
52.212.109.85
52.210.89.1
52.212.185.152
52.212.192.130
```

### Quick checklist

* [ ] Configure the **destination URL** and enable the **payload signature** (mandatory — it also gives you the `state` field).
* [ ] Define a **webhook listener** (via portal or API) for the entities and states you are interested in.
* [ ] Return HTTP `200` from your endpoint to confirm each delivery.
* [ ] Implement server-side logic to verify the `x-signature`, parse the `state` directly, or retrieve the entity via the API.
* [ ] Monitor the webhook **invocations** in your space and adjust as necessary.

### Troubleshooting

* [What does 'Webhook call failed' mean and how can it be fixed?](/documentation/faq/e-commerce-troubleshooting-and-handling/error-messages/what-does-webhook-call-failed-mean-and-how-can-it-be-fixed.md)
* Notifications blocked by rules on your web server, for example in your `.htaccess` file? See [How to debug and fix a timeout issue when redirecting to the shop after transaction is processed?](/documentation/faq/e-commerce-troubleshooting-and-handling/error-messages/how-to-debug-and-fix-a-timeout-issue-when-redirecting-to-the-shop-after-transaction-is-processed.md)
* Retries exhausted? Reactivate the listener after fixing your endpoint, and check the invocation log for the exact error returned by your server.
