curl -X POST 'https://verification.didit.me/v3/webhook/destinations/' \
-H 'x-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"label": "Production session webhooks",
"url": "https://yourapp.com/webhooks/didit",
"webhook_version": "v3",
"subscribed_events": ["status.updated", "data.updated"]
}'from didit import Didit
# Reads DIDIT_API_KEY.
client = Didit()
result = client.webhooks.destinations.create(
label="Production session webhooks",
url="https://yourapp.com/webhooks/didit",
webhook_version="v3",
subscribed_events=["status.updated", "data.updated"],
)
print(result)import Didit from "@didit-protocol/sdk-node";
// Reads DIDIT_API_KEY.
const didit = new Didit();
const result = await didit.webhooks.destinations.create({
label: "Production session webhooks",
url: "https://yourapp.com/webhooks/didit",
webhook_version: "v3",
subscribed_events: ["status.updated", "data.updated"],
});
console.log(result);ctx := context.Background()
// Reads DIDIT_API_KEY.
client := didit.NewClient()
result, err := client.Webhooks.Destinations.Create(ctx, didit.WebhooksDestinationsCreateParams{
Label: "Production session webhooks",
URL: "https://yourapp.com/webhooks/didit",
WebhookVersion: didit.Some(didit.WebhooksDestinationsCreateParamsWebhookVersion("v3")),
SubscribedEvents: didit.Some([]didit.WebhookEvent{didit.WebhookEvent("status.updated"), didit.WebhookEvent("data.updated")}),
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("%+v\n", result)import java.util.List;
import me.didit.Didit;
import me.didit.types.WebhookEvent;
import me.didit.types.WebhooksDestinationsCreateParams;
// Reads DIDIT_API_KEY.
Didit client = new Didit();
var result = client.webhooks().destinations().create(WebhooksDestinationsCreateParams.builder()
.label("Production session webhooks")
.url("https://yourapp.com/webhooks/didit")
.webhookVersion(WebhooksDestinationsCreateParams.WebhookVersion.of("v3"))
.subscribedEvents(List.of(WebhookEvent.of("status.updated"), WebhookEvent.of("data.updated")))
.build());
System.out.println(result);<?php
require 'vendor/autoload.php';
use Didit\DiditClient;
// Reads DIDIT_API_KEY.
$didit = new DiditClient();
$result = $didit->webhooks->destinations->create([
'label' => 'Production session webhooks',
'url' => 'https://yourapp.com/webhooks/didit',
'webhook_version' => 'v3',
'subscribed_events' => ['status.updated', 'data.updated'],
]);
print_r($result->toArray());require "didit"
# Reads DIDIT_API_KEY.
didit = Didit::Client.new
result = didit.webhooks.destinations.create(
label: "Production session webhooks",
url: "https://yourapp.com/webhooks/didit",
webhook_version: "v3",
subscribed_events: ["status.updated", "data.updated"]
)
puts result.to_husing System;
using System.Text.Json;
using Didit;
// Reads DIDIT_API_KEY.
using var client = new DiditClient();
var result = await client.Webhooks.Destinations.CreateAsync(new WebhooksDestinationsCreateParams
{
Label = "Production session webhooks",
Url = "https://yourapp.com/webhooks/didit",
WebhookVersion = new WebhooksDestinationsCreateParamsWebhookVersion("v3"),
SubscribedEvents = new WebhookEvent[] { "status.updated", "data.updated" },
});
Console.WriteLine(JsonSerializer.Serialize(result));{
"uuid": "11111111-2222-3333-4444-555555555555",
"label": "Production session webhooks",
"url": "https://yourapp.com/webhooks/didit",
"enabled": true,
"webhook_version": "v3",
"secret_shared_key": "tno2NTeRC1ZK3sBOYlBJDyEzBVENl3pQ1AcZyAW0xnQ",
"secret_last4": "0xnQ",
"previous_secret_expires_at": null,
"subscribed_events": [
"status.updated",
"data.updated"
],
"created_by_email": null,
"created_by_name": null,
"created_at": "2026-05-17T10:00:00Z",
"updated_at": "2026-05-17T10:00:00Z",
"health_status": "healthy",
"failing_streak": 0,
"failing_since": null,
"last_success_at": null,
"environment": "live",
"summary": {
"total_deliveries": 0,
"failed_deliveries": 0,
"error_rate_percentage": 0,
"min_response_time_ms": null,
"avg_response_time_ms": null,
"max_response_time_ms": null,
"last_delivery_at": null,
"metrics_window_days": 30
}
}Create Webhook Destination API
Register a new Didit webhook destination with version, subscribed events, and a destination-specific signing secret. Programmatic webhook ops, pay-per-call.
curl -X POST 'https://verification.didit.me/v3/webhook/destinations/' \
-H 'x-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"label": "Production session webhooks",
"url": "https://yourapp.com/webhooks/didit",
"webhook_version": "v3",
"subscribed_events": ["status.updated", "data.updated"]
}'from didit import Didit
# Reads DIDIT_API_KEY.
client = Didit()
result = client.webhooks.destinations.create(
label="Production session webhooks",
url="https://yourapp.com/webhooks/didit",
webhook_version="v3",
subscribed_events=["status.updated", "data.updated"],
)
print(result)import Didit from "@didit-protocol/sdk-node";
// Reads DIDIT_API_KEY.
const didit = new Didit();
const result = await didit.webhooks.destinations.create({
label: "Production session webhooks",
url: "https://yourapp.com/webhooks/didit",
webhook_version: "v3",
subscribed_events: ["status.updated", "data.updated"],
});
console.log(result);ctx := context.Background()
// Reads DIDIT_API_KEY.
client := didit.NewClient()
result, err := client.Webhooks.Destinations.Create(ctx, didit.WebhooksDestinationsCreateParams{
Label: "Production session webhooks",
URL: "https://yourapp.com/webhooks/didit",
WebhookVersion: didit.Some(didit.WebhooksDestinationsCreateParamsWebhookVersion("v3")),
SubscribedEvents: didit.Some([]didit.WebhookEvent{didit.WebhookEvent("status.updated"), didit.WebhookEvent("data.updated")}),
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("%+v\n", result)import java.util.List;
import me.didit.Didit;
import me.didit.types.WebhookEvent;
import me.didit.types.WebhooksDestinationsCreateParams;
// Reads DIDIT_API_KEY.
Didit client = new Didit();
var result = client.webhooks().destinations().create(WebhooksDestinationsCreateParams.builder()
.label("Production session webhooks")
.url("https://yourapp.com/webhooks/didit")
.webhookVersion(WebhooksDestinationsCreateParams.WebhookVersion.of("v3"))
.subscribedEvents(List.of(WebhookEvent.of("status.updated"), WebhookEvent.of("data.updated")))
.build());
System.out.println(result);<?php
require 'vendor/autoload.php';
use Didit\DiditClient;
// Reads DIDIT_API_KEY.
$didit = new DiditClient();
$result = $didit->webhooks->destinations->create([
'label' => 'Production session webhooks',
'url' => 'https://yourapp.com/webhooks/didit',
'webhook_version' => 'v3',
'subscribed_events' => ['status.updated', 'data.updated'],
]);
print_r($result->toArray());require "didit"
# Reads DIDIT_API_KEY.
didit = Didit::Client.new
result = didit.webhooks.destinations.create(
label: "Production session webhooks",
url: "https://yourapp.com/webhooks/didit",
webhook_version: "v3",
subscribed_events: ["status.updated", "data.updated"]
)
puts result.to_husing System;
using System.Text.Json;
using Didit;
// Reads DIDIT_API_KEY.
using var client = new DiditClient();
var result = await client.Webhooks.Destinations.CreateAsync(new WebhooksDestinationsCreateParams
{
Label = "Production session webhooks",
Url = "https://yourapp.com/webhooks/didit",
WebhookVersion = new WebhooksDestinationsCreateParamsWebhookVersion("v3"),
SubscribedEvents = new WebhookEvent[] { "status.updated", "data.updated" },
});
Console.WriteLine(JsonSerializer.Serialize(result));{
"uuid": "11111111-2222-3333-4444-555555555555",
"label": "Production session webhooks",
"url": "https://yourapp.com/webhooks/didit",
"enabled": true,
"webhook_version": "v3",
"secret_shared_key": "tno2NTeRC1ZK3sBOYlBJDyEzBVENl3pQ1AcZyAW0xnQ",
"secret_last4": "0xnQ",
"previous_secret_expires_at": null,
"subscribed_events": [
"status.updated",
"data.updated"
],
"created_by_email": null,
"created_by_name": null,
"created_at": "2026-05-17T10:00:00Z",
"updated_at": "2026-05-17T10:00:00Z",
"health_status": "healthy",
"failing_streak": 0,
"failing_since": null,
"last_success_at": null,
"environment": "live",
"summary": {
"total_deliveries": 0,
"failed_deliveries": 0,
"error_rate_percentage": 0,
"min_response_time_ms": null,
"avg_response_time_ms": null,
"max_response_time_ms": null,
"last_delivery_at": null,
"metrics_window_days": 30
}
}secret_shared_key, webhook_version, and subscribed_events list.
How subscribed_events works
subscribed_events is a required array of exact event names. Didit delivers a webhook to this destination only when the payload’s webhook_type matches one of the values in the array.
- Include at least one event.
- Use only the supported event names below.
- Add every event you want delivered; there is no wildcard value like
*orall. - To route different product areas to different systems, create multiple destinations with different
subscribed_eventslists.
Event catalog
| Event | Subscribe when you need |
|---|---|
status.updated | User Verification (KYC) or Business Verification (KYB) session status changes. KYB payloads include session_kind: "business" and business_session_id. |
data.updated | Session verification data is corrected or updated after creation, such as reviewer edits to KYC, Proof of Address, or KYB data. |
user.status.updated | A User entity changes status, for example ACTIVE, FLAGGED, or BLOCKED. |
user.data.updated | A User entity profile, counters, metadata, documents, or aggregate verification fields change. |
business.status.updated | A Business entity changes status, for example after manual review, KYB results, or a blocklist action. |
business.data.updated | A Business entity profile, counters, metadata, registration fields, or aggregate verification fields change. |
activity.created | Accepted in subscribed_events and covered by Try Webhook, but Didit does not currently deliver it for live traffic. |
transaction.created | A Transaction Monitoring transaction is created and its initial rule evaluation is complete. |
transaction.status.updated | A transaction changes status after rules, analyst review, remediation, provider updates, or API/console actions; also sent once when a crypto screening retried in the background completes. |
travel_rule.status.updated | A Travel Rule exchange changes status. |
workflow.compliance_changed | A published workflow’s compliance status changes after a re-evaluation. |
transaction_rule_suggestions.created | A rule suggestion run proposes new rule changes. |
investigator.*) and Authentication (auth.*) event families are also accepted. Every event and its payload is listed in Webhooks.
Common subscription sets:
{
"subscribed_events": ["status.updated", "data.updated"]
}
{
"subscribed_events": ["transaction.created", "transaction.status.updated"]
}
{
"subscribed_events": [
"user.status.updated",
"user.data.updated",
"business.status.updated",
"business.data.updated",
"activity.created"
]
}
Authorizations
Your application's API key, from Developers -> API keys in the Business Console. The primary key has full access. A named key can be scoped: none, read or write per resource, limited to some workflows or to approved sessions, to a list of IP addresses, and to an expiry date. 401 means the key is wrong, revoked or expired; 403 means no key was sent, the key has no access to this resource or action, or the request came from an address outside its IP list; 404 on a session route means the session is outside the key's workflows or statuses. A key without media access receives image, video and PDF URLs as null, and a key without sessions write receives session links and tokens as null. See https://docs.didit.me/console/api-keys.
Headers
Optional retry-safety key you choose for one operation, up to 255 characters (a UUID works). A request repeated with the same key, endpoint, query and body is answered with the first response, same status and same body, and runs and bills nothing a second time. The same key with another endpoint, query or body returns 409 idempotency_key_reused, and while the first request is still running, 409 idempotency_key_in_progress with Retry-After. A failed request (any 4xx or 5xx) is not stored, so you can send it again with the same key. Keys belong to your application, whether you call with an API key or an access token. See Retries and idempotency.
1 - 255Body
Events to deliver. Effectively required: an explicit [] is rejected with 400, and omitting the field creates a destination with no subscriptions that receives nothing.
1Supported webhook event name. Use these exact strings in subscribed_events; unsupported values are rejected.
status.updated, data.updated, user.status.updated, user.data.updated, business.status.updated, business.data.updated, activity.created, transaction.created, transaction.status.updated, travel_rule.status.updated, workflow.compliance_changed, transaction_rule_suggestions.created, auth.signup, auth.login, auth.login_failed, auth.password_reset_requested, auth.password_reset_completed, auth.mfa_changed, auth.new_device, auth.account_locked, investigator.report.ready, investigator.decision.automated, investigator.decision.overridden, investigator.decision.contested, investigator.question.raised, investigator.automation.paused, investigator.rfi.answered, investigator.rfi.overdue, investigator.drift.detected, investigator.sla.breached, investigator.qa.failed, investigator.external.report.ready ["status.updated", "data.updated"]
Response
Destination created. secret_shared_key is the HMAC signing secret — store it now.
Auto-generated HMAC signing secret bound to this destination (43-character URL-safe token). Use it to verify the signature headers on incoming webhooks. Always returned in full to API-key callers; Console users need the read:webhooks permission, otherwise they see an empty string.
Set for 24 hours after the destination's signing secret is rotated in the Business Console. Until this time, every delivery to the destination also carries X-Signature-Previous, X-Signature-V2-Previous and X-Signature-Simple-Previous, made with the replaced secret (see Signature verification). null when no rotation happened in the last 24 hours.
Event filter for this webhook destination. Didit delivers only webhooks whose event type exactly matches one of these values — there is no wildcard subscription. When sent, the list must contain at least one valid event (an explicit [] is rejected with 400); a destination whose list is empty (field omitted at create) receives nothing. On update the list is replaced wholesale, never merged.
1Supported webhook event name. Use these exact strings in subscribed_events; unsupported values are rejected.
status.updated, data.updated, user.status.updated, user.data.updated, business.status.updated, business.data.updated, activity.created, transaction.created, transaction.status.updated, travel_rule.status.updated, workflow.compliance_changed, transaction_rule_suggestions.created, auth.signup, auth.login, auth.login_failed, auth.password_reset_requested, auth.password_reset_completed, auth.mfa_changed, auth.new_device, auth.account_locked, investigator.report.ready, investigator.decision.automated, investigator.decision.overridden, investigator.decision.contested, investigator.question.raised, investigator.automation.paused, investigator.rfi.answered, investigator.rfi.overdue, investigator.drift.detected, investigator.sla.breached, investigator.qa.failed, investigator.external.report.ready ["status.updated", "data.updated"]
Delivery health. failing: 3 or more consecutive events failed their first delivery attempt since the destination last answered successfully (see failing_streak). degraded: not failing, but a delivery attempt failed in the last 24 hours and no later attempt has delivered that event; a retry or resend that delivers it clears this. healthy: neither. Independent of enabled. A failed attempt is one answered with HTTP 400 or above, or not answered at all.
healthy, degraded, failing How many consecutive events failed their first delivery attempt since the destination last answered successfully, within the last 30 days. Retries are not counted, and any successful attempt, including a retry or a resend, ends the streak. 0 when no event has failed since the last success.