Webhooks
Webhooks are used to asynchronously notify your system about events such as onboarding progress, pending requirements, or entitlement status changes. This allows you to react to updates without the need for constant polling.
Overview
- Protocol: HTTPS
POSTrequests sent to your registered URL. - Security: Payloads are signed using the Ed25519 algorithm. You must verify the signature to ensure authenticity.
- Retry Policy: If your server returns a non-200 status (e.g., 5xx), the system will retry delivery with exponential backoff.
- Idempotency: Each event has a unique
idandtimestamp. Always returnHTTP 200 OKto acknowledge receipt.
Managing Webhooks
Register a Webhook
Endpoint: POST /webhooks
When registering a webhook, you can now specify exactly which events should be delivered to that URL.
Request Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
url | String | Yes | HTTPS endpoint where notifications will be delivered. |
include_all_events | Boolean | No | (Default true) If true, the webhook subscribes to all current and future event types, and event_types list is ignored. |
event_types | List[String] | No | A specific list of event types to subscribe to. Ignored if include_all_events is true. |
View Example Request
curl -X POST https://<kyc_domain>/api/v1/webhooks \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"url": "https://client.example.com/dlt-webhook",
"include_all_events": false,
"event_types": [
"requirementsQuestionnaire",
"entitlementCompleted"
]
}'
View Example Response
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"key_id": "string",
"url": "https://client.example.com/dlt-webhook",
"event_types": [
"requirementsQuestionnaire",
"entitlementCompleted"
],
"include_all_events": false,
"public_key": "aH92jD8kL9n2Z4V3x...",
"created_at": "2025-12-05T14:22:30.497Z"
}
Store the public_key returned in the registration response. You will need it to verify the signatures of incoming webhook requests.
List Registered Webhooks
Endpoint: GET /webhooks
Retrieves a paginated list of all registered webhooks and their configurations.
View Example Response
{
"items": [
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"key_id": "string",
"url": "https://client.example.com/dlt-webhook",
"event_types": [
"requirementsQuestionnaire"
],
"include_all_events": true,
"public_key": "aH92jD8kL9n2Z4V3x...",
"created_at": "2025-12-05T14:22:30.500Z"
}
],
"total": 1,
"page": 1,
"size": 50,
"pages": 1
}
Delete a Webhook
Endpoint: DELETE /webhooks/{webhook_id}
Handling Events
Payload Format
Each webhook is an HTTPS POST request containing a JSON payload and a signature header.
Header:
X-DLT-Signature: dupyAbK5Z2vB7cNHN5pu7voGKPANyjyM/g...==
Content-Type: application/json
Body:
{
"id": "8e9b9a45-6212-4a77-bb43-4b8d123a5e42",
"owner_id": "8e9b9a45-6212-4a77-bb43-4b8d123a5e42",
"owner_type": "person",
"entitlement_trigger_id": "8e9b9a45-6212-4a77-bb43-4b8d123a5e42",
"entitlement_id": "9a9b9a45-6212-4a77-bb43-4b8d123a5e42",
"timestamp": "2024-10-29T12:15:42Z",
"type": "requirementsQuestionnaire",
"details": {
// Event specific data
}
}
Common Event Types
| Type | Description |
|---|---|
requirementsQuestionnaire | Triggered when a user needs to answer additional questions. |
requirementsVid | Triggered when a video identification step is required. |
entitlementCompleted | Triggered when a workflow (like KYC) finishes successfully. |
entitlementFailed | Triggered when a workflow is rejected or fails. |
Verifying Signatures
To ensure message integrity and authenticity, the system signs each webhook payload using its private Ed25519 key. You must verify this using the public_key you received during registration.
Verification Steps:
- Read the raw request body (exact bytes, do not parse as JSON yet).
- Retrieve the signature from the
X-DLT-Signatureheader. - Use an Ed25519 library to verify the signature against the body bytes.
Python Example
import nacl.signing # requires 'pynacl' library
import nacl.encoding
# 1. Get these from your config/headers
public_key_hex = "aH92jD8kL9n2Z4V3x..." # From registration response
signature_hex = request.headers.get("X-DLT-Signature")
payload_bytes = request.data # Raw body bytes
# 2. Setup verification key
verify_key = nacl.signing.VerifyKey(public_key_hex, encoder=nacl.encoding.Base64Encoder)
# 3. Verify
try:
verify_key.verify(payload_bytes, bytes.fromhex(signature_hex))
print("✅ Signature valid")
except:
print("❌ Invalid signature")
# Return 400 or 401