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

# Webhooks

Webhooks are an essential piece of integration offered to inform your system of any updates that have been performed on resources. These webhooks are submitted machine to machine and have an at-least-once delivery guarantee.

Webhooks are crucial to getting informed about asynchronous updates or scenarios where the network is faulty and you have not yet been able to get a response. The latter scenario can also be caught through [Idempotency](/platform-guides/for-developers/idempotency). It is recommended that both are implemented as asynchronous processes, such as generating invoices, which will not provide any other means of identifying this action happened besides webhooks.

A webhook is marked as received on our part when an HTTP 2XX response has been provided (for example; a 200, 201, or 202). When this has not been done within 10 seconds or any other HTTP status has been replied we will retry this webhook and queue any subsequent webhooks. This is to ensure at-least-once delivery as well as the order of events. More details on the retries that follow this scenario are detailed in the Retries chapter.

# Structure

Our webhooks have a general structure with a variable data property. The following fields are present in a webhook.

| Field                 | Description                                                                                                                            |
| --------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| `created_at`          | When the webhook event was created.                                                                                                    |
| `type`                | The event type, for example `INVOICE.STATUS_TO_FINAL`. See [Webhook event types](/platform-guides/for-developers/webhook-event-types). |
| `original_request_id` | Optional. The ID of the API request that caused this event.                                                                            |
| `environment`         | The environment the event comes from, `TEST` or `LIVE`.                                                                                |
| `previous_data`       | Optional. The resource before the change. Present on updates when the webhook's `include_previous_data` is enabled.                    |
| `data`                | The resource after the change, in the same shape the API returns it.                                                                   |
| `metadata`            | Optional. Extra context for some events, for example the workflow IDs on an `INVOICE.EXPORT_TO_ERP` event sent by a workflow.          |

## Event types

An event type is the object type and the action, in uppercase, separated by a period, for example `CUSTOMER.CREATED`. Besides `CREATED`, `UPDATED` and `DELETED`, some object types send sub-action events for specific transitions, such as `INVOICE.STATUS_TO_FINAL`, `INVOICE.PAID` and `QUOTE_VERSION.SIGNED`. You must list each sub-action event in `included_actions`.

[Webhook event types](/platform-guides/for-developers/webhook-event-types) lists every object type and event, and when each one fires. [GET /v1/webhooks/actions](https://docs.solvimon.com/api-docs/configuration-api/webhooks/get-webhooks-actions) returns the values your platform accepts.

# Configure webhooks

Configuration of webhooks can be managed through the API or in the Developers area in Desk. For a webhook to work a URL needs to be exposed in your application. We recommend this URL to be an HTTPS endpoint with TLSv1.2 or TLSv1.3. Alongside the URL the following additional options can be configured:

1. `included_actions`: for which resources actions this webhook should be applied (like `CUSTOMER.CREATED`)
2. `excluded_actions`: which resources actions should be excluded from this webhook
3. `active`: this webhook can be enabled/disabled

Event names must match exactly. There are no wildcards. When you set `included_actions`, Solvimon ignores `excluded_actions`. Leave both out to receive every event.

For configuration of webhooks by API please refer to the API Reference: [Create a web hook](/api-docs/configuration-api/webhooks/create-a-web-hook)

# Webhooks security

## Authentication

In order to protect the exposed endpoint on your part from any malicious activity there are two forms of authentication that can be included when sending a webhook. These two forms are:

1. API key
2. Username/password combination

### API Key

An API key can be stored in combination with an optional header field that will be sent along with the payload. The custom header field will be used to put the API key in, if omitted the field will be put in the `X-API-KEY` header.

### Username/password

A username-password combination can be stored and will be passed along as a basic auth configuration.

## Payload integrity

> **Note**
>
> Obtaining a secret requires an additional step after the webhook is created, this currently only available through API, this is done through the [Get a new webhook secret, this rolls the current token](/api-docs/configuration-api/webhooks/patch-an-existing-webhook)

In order to verify the integrity of the webhook payload as well as prevent man-in-the-middle attacks a webhook secret can be configured.

This will result in the webhook containing two additional headers:

1. `X-PAYLOAD-SIGNATURE-TIMESTAMP`: that indicates the time when the signature was created. This can be used to not accept any webhook coming in that is older than a defined window, our recommendation would be to block anything older than 5 minutes.
2. `X-PAYLOAD-SIGNATURE`: the actual signature of the payload. This may contain more than one signature.

After the signature timestamp has been checked and deemed valid the signature itself can be split into the comma (`,`) character.

This will result in **at least one** signature that is a key value pair of the version and the signature, for example:

```
v1=6DEA8E1EE12473F99776783FD36A430C0744C0C959085EB2D14061BE2A54729E
```

The signature is calculated with the HMAC SHA256 protocol. Verification can be done by concatenating the provided timestamp from the `X-PAYLOAD-SIGNATURE-TIMESTAMP` header as a string (according to ISO8601 standard) with the payload separated by a period (`.`).

### Rolling webhook secret

A webhook secret can be rolled and will result in the signature being calculated for the new secret as well as the previous secret for up to 24 hours. This will ensure you have a sufficient amount of time to configure the new secret.

# Retries

Whenever a webhook is submitted and not accepted within 10 seconds with HTTP 2XX status the webhook will be resent in an increasing interval:

1. 2 minutes
2. 4 minutes
3. 8 minutes
4. 16 minutes
5. 32 minutes
6. 64 minutes
7. 128 minutes
8. 256 minutes
9. 512 minutes

After that, Solvimon keeps retrying every 512 minutes (about 8.5 hours). A notification that is more than 7 days old is no longer sent.

While a webhook fails, its `penalty_box_data` shows three values: the number of failed attempts (`retry_count`), the time of the next attempt (`retry_at`) and the `cause` of the last failure. A successful delivery clears it.

If 20 retries in a row fail, Solvimon disables the webhook: `active` becomes `false` and `disabled_reason` says why. On the schedule above, that is about 4.6 days after the first failure. Solvimon skips notifications that are still in the queue for a disabled webhook. To receive events again, fix your endpoint and set `active` back to `true` with [PATCH /v1/webhooks/\{id}](https://docs.solvimon.com/api-docs/configuration-api/webhooks/patch-webhooks-by-resource-id). Re-enabling the webhook clears `disabled_reason`.

> **Warning**
>
> It is recommended that you accept the webhook before processing, this might otherwise result in a timeout on our part and the webhook being sent again.

---