Webhooks deliver real-time, signed notifications when resource states change. Instead of polling, configure a webhook endpoint to receive push notifications.
Webhook delivery is not guaranteed. Clients must implement polling to retrieve final results if webhook delivery fails. We recommend a polling interval of 10 minutes or greater.
Endpoint Requirements
| Item | Requirement |
|---|---|
| Protocol | HTTPS only (TLS 1.2+) |
| Method | POST |
| Content-Type | application/json |
| Timeout | 60 seconds |
Configuration
Webhook endpoints are configured per account in the AdvanGuard platform:
- Webhook HTTPS URL — your endpoint that receives event notifications
- Shared secret key (
secretKey) — used for request signing (Base64-encoded; decode before use)
Event Format
{
"eventId": "550e8400-e29b-41d4-a716-446655440000",
"eventType": "APPLICATION_COMPLETED",
"webhookId": "wh_xxx",
"data": {
"applicationId": "app-7392581047263518721",
"externalUserId": "user-001",
"profileId": "pf-1234567890",
"levelId": "level-kyc-standard",
"status": "completed",
"reviewAnswer": "GREEN"
}
}Event Fields
| Field | Type | Description |
|---|---|---|
eventId | String | Unique event identifier (use for deduplication) |
eventType | String | Event type (see table below) |
webhookId | String | Webhook configuration ID in the AdvanGuard platform |
data | Object | Event-specific payload |
Event Types
Application Events
Triggered when an Application's status changes.
| eventType | Trigger | Description |
|---|---|---|
APPLICATION_PENDING | init → pending | Data submission completed, verification in progress |
APPLICATION_ON_HOLD | pending → onHold | Waiting for manual review or external processing |
APPLICATION_COMPLETED | → completed | Verification finished (check reviewAnswer for result) |
APPLICATION_TERMINATED | → terminated | Application cancelled (manual or timeout) |
Application event data fields:
| Field | Type | Description |
|---|---|---|
applicationId | String | Application ID |
externalUserId | String | Customer system user identifier |
profileId | String | Associated Profile ID |
levelId | String | Associated Level ID |
status | String | New application status |
reviewAnswer | String | GREEN (passed) or RED (rejected). Only present for APPLICATION_COMPLETED |
AML Events
Triggered by AML ongoing screening updates.
| eventType | Trigger | Description |
|---|---|---|
AML_OGS_UPDATE | Ongoing screening detects changes | New or updated screening results found |
AML_ALERT_CREATE | A new AML alert is created | Triggered when an alert is created by screening, re-screening, or ongoing monitoring |
AML_OGS_UPDATE event data fields:
| Field | Type | Description |
|---|---|---|
profileId | String | Profile ID |
caseId | String | AML Case ID |
alertId | String | Alert ID |
numberOfNewResults | Integer | Number of new matches |
numberOfUpdatedResults | Integer | Number of updated matches |
Example:
{
"eventId": "660e8400-e29b-41d4-a716-446655440001",
"eventType": "AML_OGS_UPDATE",
"webhookId": "wh_xxx",
"data": {
"profileId": "pf-1234567890",
"caseId": "amlc_4d372e682f464d8f93856892b8b2cee1",
"alertId": "amla_5e4fc571870841ecbff2f0e506101d7d",
"numberOfNewResults": 0,
"numberOfUpdatedResults": 1
}
}AML_ALERT_CREATE event data fields:
| Field | Type | Description |
|---|---|---|
profileId | String | Associated Profile ID. null if the alert is not linked to a case |
caseId | String | AML Case ID. null if the alert is not linked to a case |
alertId | String | Alert ID |
createdAt | Long | Alert creation time in Unix epoch milliseconds |
Example:
{
"eventId": "bb0e8400-e29b-41d4-a716-446655440006",
"eventType": "AML_ALERT_CREATE",
"webhookId": "wh_xxx",
"data": {
"profileId": "pf-1234567890",
"caseId": "amlc_4d372e682f464d8f93856892b8b2cee1",
"alertId": "amla_5e4fc571870841ecbff2f0e506101d7d",
"createdAt": 1752489000000
}
}Business Verification Events
Triggered when a standalone business verification check changes status.
| eventType | Trigger | Description |
|---|---|---|
BUSINESS_VERIFICATION_STATUS | Business verification status changes | The verification result for a submitted business check has been updated |
BUSINESS_VERIFICATION_STATUS event data fields:
| Field | Type | Description |
|---|---|---|
id | String | Business verification result ID |
status | String | Verification check status (IN_PROGRESS, COMPLETED, ERROR) |
checkItem | String | The business check item that was performed (e.g. Basic Business Verification) |
updatedAt | Integer | Update time in Unix epoch seconds |
Example:
{
"eventId": "770e8400-e29b-41d4-a716-446655440002",
"eventType": "BUSINESS_VERIFICATION_STATUS",
"webhookId": "wh_xxx",
"data": {
"id": "KYBC-201464826803499999",
"status": "COMPLETED",
"checkItem": "Basic Business Verification",
"updatedAt": 1769405823
}
}KYB Application Events
Triggered when a KYB application changes status.
| eventType | Trigger | Description |
|---|---|---|
KYB_APPLICATION_STATUS_CHANGE | Application status changes | KYB application status change event (triggered when the application status changes) |
KYB_APPLICATION_STATUS_CHANGE event data fields:
| Field | Type | Description |
|---|---|---|
applicationId | String | KYB application ID (e.g. KYB-XXXXXXXX) |
status | String | New application status (DRAFT, IN_PROGRESS, PENDING_REVIEW, PENDING_RESUBMISSION, APPROVED, REJECTED) |
updatedAt | Integer | Update time in Unix epoch seconds |
Example:
{
"eventId": "880e8400-e29b-41d4-a716-446655440003",
"eventType": "KYB_APPLICATION_STATUS_CHANGE",
"webhookId": "wh_xxx",
"data": {
"applicationId": "KYB-XXXXXXXX",
"status": "IN_PROGRESS",
"updatedAt": 1720425600
}
}KYB Email Notification Events
When webhook email notifications are enabled, the system pushes email content to your endpoint instead of sending emails directly, so you can handle delivery yourself. Empty fields are omitted from the payload (rather than returned as null).
| eventType | Trigger | Description |
|---|---|---|
KYB_IDV_EMAIL | IDV email needs to be sent | Email notifications during the Identity Verification (IDV) process |
KYB_QNRE_EMAIL | Questionnaire email needs to be sent | Email notifications during the Questionnaire process (OTP, form link, confirmation) |
KYB_IDV_EMAIL event data fields:
| Field | Type | Presence | Description |
|---|---|---|---|
email | String | Always | Email address of the individual being verified |
directUrl | String | Only for REQUEST_IDV | Identity verification link for the individual |
expireHour | Integer | Only for REQUEST_IDV | Link validity period in hours |
emailType | String | Always | REQUEST_IDV, NOTIFY_IDV_SUCCESS, or NOTIFY_IDV_FAILED |
KYB_QNRE_EMAIL event data fields:
| Field | Type | Presence | Description |
|---|---|---|---|
email | String | Always | Email address of the respondent |
otpCode | String | Only for OTP_VERIFICATION | 6-digit numeric verification code |
url | String | Only for FORM_NOTIFICATION | Form link for the recipient to continue filling |
emailType | String | Always | FORM_NOTIFICATION, OTP_VERIFICATION, or SUBMISSION_CONFIRMATION |
Examples:
{
"eventId": "990e8400-e29b-41d4-a716-446655440004",
"eventType": "KYB_IDV_EMAIL",
"webhookId": "wh_xxx",
"data": {
"email": "[email protected]",
"directUrl": "https://kyb.example.com/idv?id=xxx&token=yyy",
"expireHour": 72,
"emailType": "REQUEST_IDV"
}
}{
"eventId": "aa0e8400-e29b-41d4-a716-446655440005",
"eventType": "KYB_QNRE_EMAIL",
"webhookId": "wh_xxx",
"data": {
"email": "[email protected]",
"otpCode": "385721",
"emailType": "OTP_VERIFICATION"
}
}Content Signature
All webhook requests are signed using your shared secret key. Verify signatures to ensure events originate from AdvanGuard.
Signature Headers
| Header | Description |
|---|---|
aai-timestamp | Request timestamp (seconds, Unix epoch) |
aai-nonce | One-time random string for replay protection |
aai-signature | Base64-encoded HMAC-SHA256 signature |
What to Sign
The exact raw HTTP POST body (UTF-8), as received. Do not re-serialize or reformat the JSON — any change breaks the signature.
How to Compute
keyBytes = Base64.decode(sharedSecretFromConsole)
signature = Base64( HMAC_SHA256(keyBytes, rawRequestBody) )
aai-timestamp and aai-nonce are for replay checks only — they are not part of the HMAC input.
Signature Verification
Step 1: Read Headers
Extract aai-timestamp, aai-nonce, and aai-signature from the request.
Step 2: Validate Timestamp
Reject requests where aai-timestamp is more than 5 minutes from current time.
Step 3: Validate Nonce
Each aai-nonce must be unique. Cache nonce values for at least 5 minutes to detect replays.
Step 4: Verify Signature
Recompute the HMAC-SHA256 signature and compare with aai-signature.
Java
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
public class WebhookVerifier {
public static boolean verify(String rawBody, String secretKey, String signatureHeader)
throws Exception {
byte[] keyBytes = Base64.getDecoder().decode(secretKey);
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(keyBytes, "HmacSHA256"));
String expected = Base64.getEncoder().encodeToString(
mac.doFinal(rawBody.getBytes(StandardCharsets.UTF_8)));
return expected.equals(signatureHeader);
}
}Step 5: Respond
| Response | Result |
|---|---|
| Any 2xx | Delivery successful |
| Non-2xx | Delivery failed, retry will be triggered |
Important: Respond with
200quickly and process the event asynchronously. Long processing times may cause timeouts and unnecessary retries.
Retry Policy
If a webhook request fails, the system retries delivery automatically.
Failure Conditions
- Network error
- Request timeout (> 60 seconds)
- Any non-2xx HTTP response
Retry Schedule
| Attempt | Delay |
|---|---|
| 1st retry | 5 seconds |
| 2nd retry | 10 seconds |
| 3rd retry | 20 seconds |
Webhooks follow an at-least-once delivery model. After 3 failed retries, the event is not retried further — use polling as a fallback.
Best Practices
- Respond quickly — return
200immediately and process the event in background. - Handle duplicates — use
eventIdto deduplicate; the same event may be delivered more than once. - Verify signatures — always verify the
aai-signatureheader before processing. - Implement polling — do not rely solely on webhooks; poll for status as a fallback.
- Use HTTPS — only HTTPS endpoints with TLS 1.2+ are supported.
- Cache the raw body — read and cache the raw HTTP body before your framework parses JSON, to ensure correct signature verification.