Webhook manager

Explore webhook settings for customized notifications on your applicants.

On this page, you can learn how to create, configure, and manage a webhook based on your requirements in the Webhook manager section.

📘

Mind the following:

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

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 typesDelays between attempts
applicantActionPending, applicantActionReviewed, applicantActionOnHold30 seconds → 30 seconds → 1 minute → 5 minutes → 5 minutes
All other webhooks1 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:

ReceiverPermanent (not retried)
HTTP401, 403, 404
Slack400, 403, 404, 410
Telegram400, 401, 403, 404
EmailEmail has no response code. A delivery is considered failed if the send itself fails, and such failures are retried.
📘

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.

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.

📘

Note

If a webhook is automatically disabled after continuous request failures, team members with Manage company settings enabled in their role configuration will receive an email notification.

Create webhooks

To create a webhook:

  1. In the Dashboard, open Dev space, go to the WebhooksWebhook manager section, and click Create webhook.
  2. In the Name field, enter a preferable webhook name.
  3. Select a Webhook receiver type, which can be any HTTP address, Slack, Telegram, or email. This field determines where you will receive verification results.

    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.
  5. From the Webhook types 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, Transaction Monitoring, Case management, and Fraud Network Detection 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 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.
  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 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 this article.
  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.

    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.

    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.

🚧

Important

To use this feature, set a Secret Key value when creating a webhook.

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)
📘

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.

You can choose among these algorithms when setting up your 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.
📘

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.

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

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'
NameTypeRequiredDescriptionDefault
# {body}ObjectYesAny payload.N/A
secretKeyStringYesA secret key that can be used for signing.N/A
digestAlgStringNoA signature algorithm that can be used for signing. Possible values: HMAC_SHA1_HEX, HMAC_SHA256_HEX, HMAC_SHA512_HEX.HMAC_SHA1_HEX

Response

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

Example request to the client endpoint

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

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']
}
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

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

Note

The bot token and chatId should be pasted with @ between them in the following format: telegram_bot_token@chatId

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.


Did this page help you?