---
updatedAt: 2026-10-08T10:43:07.000Z
agentTools:
  projectIndex: https://docs.sumsub.com/llms.txt
---

# Webhook manager

Create and configure webhooks to receive notifications — via HTTP, Slack, Telegram, or email — when verification events occur.

On this page you will learn how to create, configure, and manage webhooks in the [Webhook manager](https://cockpit.sumsub.com/checkus/#/devSpace/webhooks/webhookManager).

<Callout icon="📘" theme="info">
  ### Note the following requirements and limits

  - You can configure up to 50 active webhooks at a time. If you exceed this limit, the error message "The maximum number of webhooks has been reached" is displayed.

  - We do not send any information to endpoints using HTTP, only HTTPS.

  - For security reasons, we do not send any personal information by the means of webhooks.

  - Supported versions of the TLS protocol are 1.2 or higher.

  - Your endpoint must accept the connection within 1 second and return a response within 10 seconds. Exceeding either limit is treated as a timeout, and our service retry the webhook on the schedule below.
</Callout>

> Webhooks triggered manually in the **Webhook manager** can be distinguished from real events by the `testMode` field in the payload.

# Timing

Retry and resend logic applies identically to all receiver types — HTTP, Slack, Telegram, and Email. The number of attempts and the delay schedule do not change based on the receiver type.

When a webhook delivery fails, we attempt to resend it five times (six total delivery attempts, including the initial one) before giving up. Resend delays depend on the webhook type:

| Webhook types                                                                | Delays between attempts                                    |
| ---------------------------------------------------------------------------- | ---------------------------------------------------------- |
| `applicantActionPending`, `applicantActionReviewed`, `applicantActionOnHold` | 30 seconds → 30 seconds → 1 minute → 5 minutes → 5 minutes |
| All other webhooks                                                           | 1 minute → 5 minutes → 1 hour → 5 hours → 18 hours         |

Certain response codes are treated as permanent — the webhook is marked as failed immediately and is not retried, regardless of the retry budget. These indicate a misconfiguration on your endpoint (auth, permissions, or wrong target) that cannot be resolved by waiting. Permanent status codes differ by receiver type:

| Receiver | Permanent (not retried)                                                                                              |
| -------- | -------------------------------------------------------------------------------------------------------------------- |
| HTTP     | `401`, `403`, `404`                                                                                                  |
| Slack    | `400`, `403`, `404`, `410`                                                                                           |
| Telegram | `400`, `401`, `403`, `404`                                                                                           |
| Email    | Email has no response code. A delivery is considered failed if the send itself fails, and such failures are retried. |

<Callout icon="📘" theme="info">
  ### Note

  Email delivery does not return an HTTP-style response code. In the webhook logs, a successfully sent email is shown with a synthetic `200` status.
</Callout>

All other non-2xx responses, and any failure with no response code at all — network errors, connection resets, or timeouts — are treated as transient and retried on the schedule above, for every receiver type.

<Callout icon="📘" theme="info">
  ### Note

  If a webhook is automatically disabled after continuous request failures, [team members](https://docs.sumsub.com/sumsub/docs/start-a-team) with **Manage company settings** enabled in their [role configuration](https://docs.sumsub.com/sumsub/docs/user-roles#permissions) will receive an email notification.
</Callout>

# Create webhooks

To create a webhook:

1. In the Dashboard, open **Dev space**, go to the **Webhooks** → **Webhook manager** section, and click **Create webhook**.
2. In the **Name** field, enter a preferable webhook name.
3. Select a **Receiver** type, which can be any HTTP address, Slack, Telegram, or email. This field determines where you will receive verification results. <br /><br />For Slack, the target URL is validated the same way as an HTTP endpoint: URLs pointing to internal or non-public hosts are rejected on save.
4. In the **Target** field, specify the target endpoint where webhooks are expected to be received. Based on the receiver type you selected in the previous step, you can provide an HTTP address, a Slack webhook URL, a Telegram API token with a Telegram chat ID after `@`, or an email address. To learn how to set up a webhook on Telegram, refer to [this section](https://docs.sumsub.com/sumsub/docs/webhook-manager#get-webhooks-with-telegram). For the HTTP address and **Slack** receiver types, the **Target** must be an HTTPS URL.
5. From the **Type** drop-down list, select the webhook type. At this step, you define which type of events or triggers (such as applicant status change) you want to receive. For more details on webhook types, see [User Verification](https://docs.sumsub.com/sumsub/docs/user-verification-webhooks), [Transaction Monitoring](https://docs.sumsub.com/sumsub/docs/transaction-monitoring-webhooks), [Case management](https://docs.sumsub.com/sumsub/docs/case-management-webhooks), and [Fraud Network Detection](https://docs.sumsub.com/sumsub/docs/fraud-network-webhooks) webhooks.
6. In the **Applicant types** section, select whether you want to receive these webhooks for Individual applicants, Companies, or both.
7. In the **Secret key** field, our system automatically generates a secret key value for [digest calculation](https://docs.sumsub.com/sumsub/docs/webhooks#verify-webhook-sender) using the HMAC algorithm. You can set your key value or use a generated one.
8. Select a **Signature algorithm** to use for verifying the webhook sender. SHA256 is the default algorithm, but you can also switch to SHA512 if needed. This ensures that the webhook is sent by Sumsub and that the data has not been altered. Verifying the webhook sender is optional, but if you choose to do so, see [this section](https://docs.sumsub.com/sumsub/docs/webhooks#verify-webhook-sender).
9. \[Optionally] In **HTTP Headers**, click **Add key value pair** to create a custom header. This option is only available if **HTTP address** was selected as the webhook receiver type in Step 3.
10. \[Optionally] From the **Source keys** drop-down list, choose a source key if you have created it previously. In case there are no available source keys, refer to [this article](https://docs.sumsub.com/sumsub/docs/source-keys) to learn how to set one up.
11. Set the **Resend failed webhooks several times** checkbox to enable/disable sending failed webhooks repeatedly. This setting applies to all receiver types — HTTP, Slack, Telegram, and Email.
12. \[Optionally] Configure the **Rollout percentage** to define what share of matching webhook events Sumsub delivers. If you do not specify the field, the system uses **100** as the default value. For more information on the **Rollout percentage setting**, refer to <Anchor target="_blank" href="doc:webhook-rollout-percentage">this article</Anchor>.&#x20;
13. Click **Test webhook** to see how it performs. The test reflects the real delivery outcome for all receiver types — HTTP, Slack, Telegram, and Email.<br /><br />The test webhook payload includes the `"testMode": true` field so you can distinguish it from real events. Note that this field is never sent with production webhooks. <br /><br />For Slack and Telegram receivers, the test message text is prefixed with `[TEST]`. For Email receivers, the subject line is prefixed with `[TEST]` .
14. Save your changes.

## Verify webhook sender

To make sure the webhook is sent to you by Sumsub, we sign it using the HMAC (Hash-based Message Authentication Code) algorithm.

<Callout icon="🚧" theme="warn">
  ### Important

  To use this feature, set a **Secret Key** value when [creating a webhook](https://docs.sumsub.com/sumsub/docs/webhooks#create-webhooks).
</Callout>

We also send the additional `X-Payload-Digest-Alg` header that specifies one of the following algorithms to be used:

* `HMAC_SHA256_HEX` (default upon creating a new webhook)
* `HMAC_SHA512_HEX`
* `HMAC_SHA1_HEX` (legacy, deprecated)

<Callout icon="📘" theme="info">
  ### Note

  If a webhook was created before `SHA256` became the dashboard default and no algorithm was explicitly chosen, the platform falls back to `HMAC_SHA1_HEX` on the wire. This is why existing webhooks may show `SHA1`. To upgrade, edit the webhook and select `SHA256` or `SHA512`.
</Callout>

You can choose among these algorithms when [setting up your webhooks](https://docs.sumsub.com/sumsub/docs/webhooks#create-webhooks).

To verify the webhook sender:

1. Get a webhook `x-payload-digest` header value and the payload as it is, without any alteration or conversion to JSON.
2. Receive the HTTP webhook body in bytes.
3. Calculate the digest with the raw webhook payload in bytes and the HMAC algorithm specified in the `x-payload-digest-alg` header.
4. Compare the `x-payload-digest` header value with the calculated digest.

<Callout icon="📘" theme="info">
  ### Note

  - Do not rely on our IP addresses for whitelisting them as the webhook sender as they may change.
  - Make sure to test your webhooks before sending the URL to us. At a minimum, it should not return a 500 HTTP response or require any sort of authorization.
</Callout>

To check that you compute the digest the same way we do, use the following `POST` method:

```curl
curl -X POST \
  'https://api.sumsub.com/resources/inspectionCallbacks/testDigest?secretKey=SoMe_SeCrEt_KeY&digestAlg=HMAC_SHA256_HEX' \
  -H 'Content-Type: text/plain' \
  -d 'someText'
```

| Name        | Type   | Required | Description                                                                                                                 | Default         |
| :---------- | :----- | :------- | :-------------------------------------------------------------------------------------------------------------------------- | :-------------- |
| `# {body}`  | Object | Yes      | Any payload.                                                                                                                | N/A             |
| `secretKey` | String | Yes      | A secret key that can be used for signing.                                                                                  | N/A             |
| `digestAlg` | String | No       | A signature algorithm that can be used for signing. Possible values: `HMAC_SHA1_HEX`, `HMAC_SHA256_HEX`, `HMAC_SHA512_HEX`. | `HMAC_SHA1_HEX` |

**Response**

```json
{
  "digest": "f6e92ffe371718694d46e28436f76589312df8db",
  "digestAlg": "HMAC_SHA256_HEX"
}
```

**Example request to the client endpoint**

```curl
curl -X POST \
  'https://callbackurl.com/kyc' \
  -H 'Content-Type: application/json' \
  -d '{
    "applicantId": "5cb56e8e0a975a35f333cb83",
    "inspectionId": "5cb56e8e0a975a35f333cb84",
    "correlationId": "req-ec508a2a-fa33-4dd2-b93d-fcade2967e03",
    "externalUserId": "12672",
    "type": "applicantReviewed",
    "reviewResult": {
        "reviewAnswer": "GREEN"
    },
    "reviewStatus": "completed",
    "createdAtMs": "2020-02-21 13:23:19.111",
    "clientId": "SumsubClient"
}'
```

**Example of computing the digest**

```javascript
export function checkDigest(req): boolean {
 const algo = {
  'HMAC_SHA1_HEX': 'sha1',
  'HMAC_SHA256_HEX': 'sha256',
  'HMAC_SHA512_HEX': 'sha512',
 }[req.headers['X-Payload-Digest-Alg']]

 if (!algo) {
  throw new Error('Unsupported algorithm')
 }

 const calculatedDigest = crypto
  .createHmac(algo, SUMSUB_PRIVATE_KEY)
  .update(req.rawBody)
  .digest('hex')

 return calculatedDigest === req.headers['x-payload-digest']
}
```
```javascript C#
private async Task<bool> CheckDigest(HttpRequest request)
{
    using (var reader = new StreamReader(request.Body))
    {
        var body = await reader.ReadToEndAsync();
        byte[] byteArray = Encoding.UTF8.GetBytes(body);
        using (MemoryStream stream = new MemoryStream(byteArray))
        {
            string algo = request.Headers["x-payload-digest-alg"];
            string calculateDigest = string.Empty;

            switch (algo)
            {
                case "HMAC_SHA1_HEX":
                    using (var hmacsha1 = new HMACSHA1(Encoding.UTF8.GetBytes(_verificationAccessor.SumSubPrivateKey)))
                    {
                        calculateDigest = hmacsha1.ComputeHash(stream).Aggregate("", (s, e) => s + String.Format("{0:x2}", e));
                    }
                    break;
                case "HMAC_SHA256_HEX":
                    using (var hmacsha256 = new HMACSHA256(Encoding.UTF8.GetBytes(_verificationAccessor.SumSubPrivateKey)))
                    {
                        calculateDigest = hmacsha256.ComputeHash(stream).Aggregate("", (s, e) => s + String.Format("{0:x2}", e));
                    }
                    break;
                case "HMAC_SHA512_HEX":
                    using (var hmacsha512 = new HMACSHA512(Encoding.UTF8.GetBytes(_verificationAccessor.SumSubPrivateKey)))
                    {
                        calculateDigest = hmacsha512.ComputeHash(stream).Aggregate("", (s, e) => s + String.Format("{0:x2}", e));
                    }
                    break;
                default:
                    using (var hmacsha256 = new HMACSHA256(Encoding.UTF8.GetBytes(_verificationAccessor.SumSubPrivateKey)))
                    {
                        calculateDigest = hmacsha256.ComputeHash(stream).Aggregate("", (s, e) => s + String.Format("{0:x2}", e));
                    }
                    break;
            }

            return calculateDigest == request.Headers["x-payload-digest"];
        }
    }
}
```
```php
<?php

$return = file_get_contents('php://input');

$algoStr = $_SERVER['HTTP_X_PAYLOAD_DIGEST_ALG'];
$digest = $_SERVER['HTTP_X_PAYLOAD_DIGEST'];


    $algo = match($algoStr) {
      'HMAC_SHA1_HEX' => 'sha1',
      'HMAC_SHA256_HEX' => 'sha256',
      'HMAC_SHA512_HEX' => 'sha512',
      default => throw new \RuntimeException('Unsupported algorithm'),
    };

    $res = $digest === hash_hmac(
        $algo,
        $return,
        'secret'
    );

    echo $res ? "true" : "false";
?>
```

## Get webhooks with Telegram

To receive webhooks in Telegram:

1. Create a Telegram bot with [BotFather](https://core.telegram.org/bots#6-botfather).
2. Save the bot token.
3. Create a new group and invite the bot to this group.
4. Get the list of updates for your bot using: `https://api.telegram.org/bot<YourBOTToken>/getUpdates`.
5. Paste the bot token and `chatId` into the **Target** field of the webhook configuration and save it.

<Callout icon="📘" theme="info">
  ### Note

  The bot token and `chatId` should be pasted with `@` between them in the following format: `telegram_bot_token@chatId`
</Callout>

# Manage webhook settings

You can manage your webhook settings in any of the following ways:

* **Edit webhook parameters**. Modify webhook parameters at any time if you need to change any of the existing settings.
* **Remove webhooks**. Remove a webhook configuration whenever you no longer need it.
* **Disable webhooks**. Disable the existing webhook temporarily. You can re-enable it at any time.

You can also search for webhooks by specifying filters such as webhook type, status, and receiver.

<HTMLBlock>{`
<div id="video-player-data" data-videoid="q5iKr7psOrg"></div>
`}</HTMLBlock>