Webhook settings
- Webhook Configuration
- Retry and Backoff Strategy
- Signature Verification
- Webhook notifications examples
- System settings
Webhook Configuration
Webhooks can be configured with the following environment variables:
|
Variable |
Description |
Default value |
|---|---|---|
|
|
Enable or disable the webhook dispatcher timer |
|
|
|
Execution interval for the webhook dispatcher timer as cron expression |
|
|
|
Maximum time that webhook events are retained in the system (24 hours) |
|
|
|
Time to wait before attempting to resend a failed webhook event |
|
|
|
The maximum number of times the system will attempt to deliver a webhook event |
|
|
|
Enable exponential backoff for webhook retry attempts |
|
|
|
The base URL where webhook callbacks will be sent |
|
|
|
The shared secret used to sign webhook requests |
|
|
|
Time to wait for establishing a connection to the webhook endpoint in milliseconds |
|
|
|
Maximum time to wait for the webhook endpoint to respond in milliseconds |
|
|
|
An array of event types that will trigger webhook notifications |
|
Retry and Backoff Strategy
Exponential Backoff Calculation
When WEBHOOK_TIMER_EVENT_DELIVERY_EXPONENTIAL_BACKOFF=true, the system calculates progressively longer delays between retry attempts using an exponential backoff algorithm.
Formula
delay = initialDelay × 2^attempt
Where:
-
initialDelayis the value ofWEBHOOK_TIMER_EVENT_DELIVERY_RESEND_DELAY(default: 30 seconds) -
attemptis the current attempt number -
The maximum exponent is capped at 30 to prevent overflow
Calculation Examples
With default settings (WEBHOOK_TIMER_EVENT_DELIVERY_RESEND_DELAY=PT30S):
| Attempt | Multiplier | Delay Calculation | Actual Delay |
|---------|------------|-------------------|--------------|
| 1 | - | - | no delay |
| 2 | 2^1 = 2 | 30s × 2 | 1 minute |
| 3 | 2^2 = 4 | 30s × 4 | 2 minutes |
| 4 | 2^3 = 8 | 30s × 8 | 4 minutes |
| 5 | 2^4 = 16 | 30s × 16 | 8 minutes |
| 6 | 2^5 = 32 | 30s × 32 | 16 minutes |
Real-world case delay could be longer because system will perform delivery on the next timer execution, so you should take to account timer settings. For example if the timer configured to be executed each minute and delay is 8 min for an attempt, real sent time will be in a range 8-9 min (delay + wait for timer to be executed).
Fixed Delay
When WEBHOOK_TIMER_EVENT_DELIVERY_EXPONENTIAL_BACKOFF=false, the system uses a fixed delay between all retry attempts:
delay = WEBHOOK_TIMER_EVENT_DELIVERY_RESEND_DELAY (constant for all attempts)
For example, with WEBHOOK_TIMER_EVENT_DELIVERY_RESEND_DELAY=PT30S:
-
Attempt 1: no delay
-
Attempt 2: 30 seconds
-
Attempt 3: 30 seconds
Use fixed delay when: You need predictable, consistent retry intervals (e.g., testing, strict SLA requirements).
Use exponential backoff when: You want to reduce load on failing systems and improve overall system resilience (recommended for production).
Webhook statuses
The implementation has specific delivery logic for HTTP response handling behaviour:
-
2xx response code → status =
DELIVERED
-
4xx response code → status =
FAILED(permanent, no retry)
-
5xx / network error → status =
PENDING(will retry) orFAILEDif max attempts exceeded
Signature Verification
All webhook requests are signed using HMAC-SHA256 to ensure authenticity and integrity. Webhook consumers must verify the signature before processing the event.
How the Signature is Created
The signature is computed using the following steps:
-
Concatenate: timestamp + webhook_url + json_body
-
Compute HMAC-SHA256 hash using the shared secret
-
Encode the result in Base64
Example:
timestamp = "1735689600"
url = "https://your-domain.com/api/v1/webhook/callback/url"
body = "{\"eventType\":\"KYC_CHECK_REQUIRED\",\"resourceId\":\"123\",...}"
message = timestamp + url + body
signature = Base64(HMAC-SHA256(shared_secret, message))
HTTP Headers
The webhook request includes the following headers:
-
Authorization: `HMAC-SHA256 {base64_signature}` – The computed signature
-
Timestamp: `{unix_timestamp}` – Unix epoch timestamp in seconds
-
Content-Type: `application/json`
Verification Steps
To verify the webhook signature on the client side:
Step 1: Extract Headers
String authHeader = request.getHeader("Authorization");
String timestampHeader = request.getHeader("Timestamp");
String signature = authHeader.replace("HMAC-SHA256 ", "");
Step 2: Read Request Body
String requestBody = // Read raw request body as string
Step 3: Reconstruct the Message
String webhookUrl = "https://your-domain.com/api/v1/webhook/callback/url";
String message = timestampHeader + webhookUrl + requestBody;
Step 4: Compute Expected Signature
Mac hmacSha256 = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKey = new SecretKeySpec(
sharedSecret.getBytes(StandardCharsets.UTF_8),
"HmacSHA256"
);
hmacSha256.init(secretKey);
byte[] hash = hmacSha256.doFinal(message.getBytes(StandardCharsets.UTF_8));
String expectedSignature = Base64.getEncoder().encodeToString(hash);
Step 5: Compare Signatures
if (!MessageDigest.isEqual(
expectedSignature.getBytes(StandardCharsets.UTF_8),
signature.getBytes(StandardCharsets.UTF_8)
)) {
throw new SecurityException("Invalid webhook signature");
}
Use constant-time comparison: Always use
MessageDigest.isEqual()or equivalent to prevent timing attacksPreserve raw body: Verify signature against the raw request body exactly as received (no parsing/reformatting)
Timestamp validation: Consider rejecting requests with timestamps too far in the past (e.g., > 5 minutes) to prevent replay attacks
Shared secret security: Store the shared secret securely (environment variables, secret management systems)
HTTPS only: Always use HTTPS to protect the shared secret and webhook data in transit
Webhook notifications examples
Transaction notifications
Webhook notification body example
{
"id": "019c0a3d-91f0-7246-8a69-cc09293ae9d6",
"eventType": "GATE_TOPUP_INITIATED", //GATE_WITHDRAWAL_INITIATED
"resourceId": "019c0a3d-91ef-70b2-b101-957feb1eab49",
"createdAt": "2026-01-29T14:52:13.168Z",
"data": {
"providerId": "019bff88-19ee-7466-88b3-29cd68ba08b3"
}
}
KYC notifications
Webhook notification body example
{
"id": "019c0a44-3775-7b72-8d2a-b49daefd82d1",
"eventType": "KYC_CHECK_REQUIRED",
"resourceId": "019c0a40-f6b9-739a-862e-eee0a4a85367",
"createdAt": "2026-01-29T14:59:28.757Z",
"data": {}
}
System settings
KYC and Gate providers should be configured in an appropriate way to send webhook notifications.
KYC provider configuration
Webhook will be sent if the provider is specified in active-provider-name and externalIntegration: true for this provider.
Example:
compliance:
document-verification:
auto-enabled: false
active-provider-name: SUMSUB
providers:
- name: MANUAL
organization-types:
- individual
- business
- shareholder
verification-item-types:
- USER_ID
- FIRST_NAME_PLAIN
- LAST_NAME_PLAIN
- DATE_OF_BIRTH
- ORGANIZATION_TYPE
- CREDENTIAL
- COUNTRY
- ZIPCODE
- REGION
external-integration: false
- name: SUMSUB
organization-types:
- individual
- business
- shareholder
verification-item-types:
- USER_ID
- FIRST_NAME_PLAIN
- LAST_NAME_PLAIN
- DATE_OF_BIRTH
- ORGANIZATION_TYPE
- CREDENTIAL
- COUNTRY
- ZIPCODE
- REGION
external-integration: true
Webhook will not be sent if the provider is specified in active-provider-name and externalIntegration: true for another provider.
Example:
compliance:
document-verification:
auto-enabled: false
active-provider-name: MANUAL
providers:
- name: MANUAL
organization-types:
- individual
- business
- shareholder
verification-item-types:
- USER_ID
- FIRST_NAME_PLAIN
- LAST_NAME_PLAIN
- DATE_OF_BIRTH
- ORGANIZATION_TYPE
- CREDENTIAL
- COUNTRY
- ZIPCODE
- REGION
external-integration: false
- name: SUMSUB
organization-types:
- individual
- business
- shareholder
verification-item-types:
- USER_ID
- FIRST_NAME_PLAIN
- LAST_NAME_PLAIN
- DATE_OF_BIRTH
- ORGANIZATION_TYPE
- CREDENTIAL
- COUNTRY
- ZIPCODE
- REGION
external-integration: true
Gate provider configuration
Webhook for transaction notification will be sent if the provider is linked to a custom gate (gate with "custom": true) and externalIntegration: true for this provider.
Create a gate provider: POST /gate-providers. During creation externalIntegration: true can be specified.
Set externalIntegration: true PATCH /gate-providers/{gateProviderId}
Link gate provider to the custom gate: PATCH /gate-providers/{gateProviderId}/link