curl -X PATCH 'https://verification.didit.me/v3/webhook/' \
-H 'x-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{"face_retention_policy": "retain_until_user_deleted", "face_retention_days": 365}'import os
import requests
resp = requests.patch(
'https://verification.didit.me/v3/webhook/',
headers={'x-api-key': os.environ['DIDIT_API_KEY']},
json={'face_retention_policy': 'retain_until_user_deleted', 'face_retention_days': 365},
timeout=10,
)
resp.raise_for_status()
config = resp.json()
print(config['face_retention_policy'], config['face_retention_days'])const resp = await fetch('https://verification.didit.me/v3/webhook/', {
method: 'PATCH',
headers: {
'x-api-key': process.env.DIDIT_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({ face_retention_policy: 'retain_until_user_deleted', face_retention_days: 365 }),
});
if (!resp.ok) throw new Error(await resp.text());
const config = await resp.json();
console.log(config.face_retention_policy, config.face_retention_days);<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://verification.didit.me/v3/webhook/",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PATCH",
CURLOPT_POSTFIELDS => json_encode([
'face_retention_policy' => 'retain_until_user_deleted',
'face_retention_days' => 365
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"x-api-key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://verification.didit.me/v3/webhook/"
payload := strings.NewReader("{\n \"face_retention_policy\": \"retain_until_user_deleted\",\n \"face_retention_days\": 365\n}")
req, _ := http.NewRequest("PATCH", url, payload)
req.Header.Add("x-api-key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.patch("https://verification.didit.me/v3/webhook/")
.header("x-api-key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"face_retention_policy\": \"retain_until_user_deleted\",\n \"face_retention_days\": 365\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://verification.didit.me/v3/webhook/")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Patch.new(url)
request["x-api-key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"face_retention_policy\": \"retain_until_user_deleted\",\n \"face_retention_days\": 365\n}"
response = http.request(request)
puts response.read_body{
"webhook_url": "https://yourapp.com/webhooks/didit",
"webhook_version": "v3",
"secret_shared_key": "dGhpcy1pcy10aGUtYXBwLWxldmVsLXNlY3JldC0wMDA",
"capture_method": "MOBILE",
"data_retention_months": 24,
"face_retention_policy": "retain_until_user_deleted",
"face_retention_days": 365,
"available_events": [
{
"value": "status.updated",
"label": "Session status updated"
},
{
"value": "data.updated",
"label": "Session data updated"
},
{
"value": "user.status.updated",
"label": "User status updated"
},
{
"value": "user.data.updated",
"label": "User data updated"
},
{
"value": "business.status.updated",
"label": "Business status updated"
},
{
"value": "business.data.updated",
"label": "Business data updated"
},
{
"value": "activity.created",
"label": "Activity created"
},
{
"value": "transaction.created",
"label": "Transaction created"
},
{
"value": "transaction.status.updated",
"label": "Transaction status updated"
},
{
"value": "travel_rule.status.updated",
"label": "Travel Rule Status Updated"
},
{
"value": "workflow.compliance_changed",
"label": "Workflow Compliance Changed"
}
],
"allowed_return_hosts": [
"shop.example.com"
],
"endpoints": [
{
"uuid": "11111111-2222-3333-4444-555555555555",
"label": "Production session webhooks",
"url": "https://yourapp.com/webhooks/didit",
"enabled": true,
"webhook_version": "v3",
"secret_shared_key": "dGhpcy1pcy1hbi1leGFtcGxlLXNlY3JldC0wMDAwMDA",
"subscribed_events": [
"status.updated",
"data.updated"
],
"created_at": "2026-01-04T08:00:00.000000Z",
"updated_at": "2026-04-22T14:09:11.523671Z"
}
]
}Update Webhook Configuration
Change the application-level webhook, rotate its secret, set data and biometric-template retention, and register Digital ID wallet return hosts.
curl -X PATCH 'https://verification.didit.me/v3/webhook/' \
-H 'x-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{"face_retention_policy": "retain_until_user_deleted", "face_retention_days": 365}'import os
import requests
resp = requests.patch(
'https://verification.didit.me/v3/webhook/',
headers={'x-api-key': os.environ['DIDIT_API_KEY']},
json={'face_retention_policy': 'retain_until_user_deleted', 'face_retention_days': 365},
timeout=10,
)
resp.raise_for_status()
config = resp.json()
print(config['face_retention_policy'], config['face_retention_days'])const resp = await fetch('https://verification.didit.me/v3/webhook/', {
method: 'PATCH',
headers: {
'x-api-key': process.env.DIDIT_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({ face_retention_policy: 'retain_until_user_deleted', face_retention_days: 365 }),
});
if (!resp.ok) throw new Error(await resp.text());
const config = await resp.json();
console.log(config.face_retention_policy, config.face_retention_days);<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://verification.didit.me/v3/webhook/",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PATCH",
CURLOPT_POSTFIELDS => json_encode([
'face_retention_policy' => 'retain_until_user_deleted',
'face_retention_days' => 365
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"x-api-key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://verification.didit.me/v3/webhook/"
payload := strings.NewReader("{\n \"face_retention_policy\": \"retain_until_user_deleted\",\n \"face_retention_days\": 365\n}")
req, _ := http.NewRequest("PATCH", url, payload)
req.Header.Add("x-api-key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.patch("https://verification.didit.me/v3/webhook/")
.header("x-api-key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"face_retention_policy\": \"retain_until_user_deleted\",\n \"face_retention_days\": 365\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://verification.didit.me/v3/webhook/")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Patch.new(url)
request["x-api-key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"face_retention_policy\": \"retain_until_user_deleted\",\n \"face_retention_days\": 365\n}"
response = http.request(request)
puts response.read_body{
"webhook_url": "https://yourapp.com/webhooks/didit",
"webhook_version": "v3",
"secret_shared_key": "dGhpcy1pcy10aGUtYXBwLWxldmVsLXNlY3JldC0wMDA",
"capture_method": "MOBILE",
"data_retention_months": 24,
"face_retention_policy": "retain_until_user_deleted",
"face_retention_days": 365,
"available_events": [
{
"value": "status.updated",
"label": "Session status updated"
},
{
"value": "data.updated",
"label": "Session data updated"
},
{
"value": "user.status.updated",
"label": "User status updated"
},
{
"value": "user.data.updated",
"label": "User data updated"
},
{
"value": "business.status.updated",
"label": "Business status updated"
},
{
"value": "business.data.updated",
"label": "Business data updated"
},
{
"value": "activity.created",
"label": "Activity created"
},
{
"value": "transaction.created",
"label": "Transaction created"
},
{
"value": "transaction.status.updated",
"label": "Transaction status updated"
},
{
"value": "travel_rule.status.updated",
"label": "Travel Rule Status Updated"
},
{
"value": "workflow.compliance_changed",
"label": "Workflow Compliance Changed"
}
],
"allowed_return_hosts": [
"shop.example.com"
],
"endpoints": [
{
"uuid": "11111111-2222-3333-4444-555555555555",
"label": "Production session webhooks",
"url": "https://yourapp.com/webhooks/didit",
"enabled": true,
"webhook_version": "v3",
"secret_shared_key": "dGhpcy1pcy1hbi1leGFtcGxlLXNlY3JldC0wMDAwMDA",
"subscribed_events": [
"status.updated",
"data.updated"
],
"created_at": "2026-01-04T08:00:00.000000Z",
"updated_at": "2026-04-22T14:09:11.523671Z"
}
]
}rotate_secret_key rotates only the application-level secret, which signs deliveries to the legacy webhook_url. Every webhook destination signs with its own secret_shared_key, which this call does not change.allowed_return_hosts is described in Register your return destination.
To read the current values first, call Get Webhook Configuration.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 missing, wrong, revoked or expired; 403 means 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.
Body
Send only the fields to change. Destinations are not managed here.
Legacy application-level webhook URL. Didit uses it only while the application has no enabled destination, and then only for status.updated and data.updated. It does not create a destination, and the next create, update or delete of a destination replaces it. To receive webhooks, create a destination with POST /v3/webhook/destinations/ instead.
2000Payload version of the legacy webhook_url.
v1, v2, v3 true replaces the application-level secret_shared_key with a new random value at once; the new value is in the response. Destination secrets are not changed. Every call with true generates a new secret, so it is not idempotent.
Legacy application setting. The verification flow does not read it.
MOBILE, MOBILE_AND_DESKTOP Automatic deletion window in months (a month counts as 30 days), or null for no automatic deletion. A change applies to existing sessions too: every user verification session older than the new window is deleted automatically, and that deletion is irreversible.
1 <= x <= 120What happens to a session's face embedding when the session is deleted. retain_until_user_deleted needs face_retention_days in the same request or already set. A change applies to session deletions from then on; it creates or purges no existing biometric template.
delete_with_session, retain_until_user_deleted Retention period, in days, of biometric templates under retain_until_user_deleted.
1 <= x <= 3650Replaces the whole list of Digital ID wallet return hosts. Bare hostnames only, such as shop.example.com: no scheme, port, path or wildcard. Values are lowercased and duplicates are dropped. Send [] to clear the list.
20253Response
The full configuration after the update, the same shape as GET /v3/webhook/. When you rotated the secret, store the new secret_shared_key before you discard the response.
Application-level webhook configuration, retention policy and Digital ID wallet return hosts, with every webhook destination.
Application-level (legacy) webhook URL. Didit sends to it only while the application has no enabled destination, and then only status.updated and data.updated. Every create, update or delete of a destination sets it to the URL of the oldest enabled destination, or null when none is enabled.
Payload version of the legacy webhook_url. Each destination has its own webhook_version.
v1, v2, v3 Application-level signing secret (43-character URL-safe token). It signs only deliveries to the legacy webhook_url; each destination signs with its own secret_shared_key. API-key callers always receive it in full; Console users without read:webhooks receive an empty string.
Legacy application setting. The verification flow does not read it: capture options are configured per workflow.
MOBILE, MOBILE_AND_DESKTOP Automatic deletion window. User verification sessions created more than this many months ago (a month counts as 30 days) are deleted automatically, and that deletion is irreversible. null means sessions are never deleted automatically. See Data retention.
1 <= x <= 120What happens to a session's face embedding when the session is deleted. delete_with_session (default) deletes it with the session. retain_until_user_deleted is the opt-in biometric template retention: an image-free biometric template linked to the User is managed separately until its retention period ends, the User is deleted, or you purge it.
delete_with_session, retain_until_user_deleted Retention period, in days, of biometric templates under retain_until_user_deleted. Required when that policy is set.
1 <= x <= 3650Every event type a destination can subscribe to, with a display label.
Show child attributes
Show child attributes
Hostnames (exact match, HTTPS only) a standalone Digital ID wallet verification may send the user back to with its return_url. Your white-label domain is always allowed as well. See Register your return destination.
20253Every webhook destination of the application, oldest first, each with its signing secret. Read only here: manage destinations with the /v3/webhook/destinations/ endpoints.
Show child attributes
Show child attributes