Webhook endpoints
Configure DALP webhook endpoints, delivery privacy, idempotent mutations, signing secrets, retries, and chain-of-custody proofs.
Overview
Webhook endpoints deliver selected DALP events to an external HTTPS URL. Use them when an integration needs pushed event delivery instead of polling token or account collections.
DALP also exposes inbound provider callback endpoints when an external provider must report decisions back to the platform. Provider callbacks are not webhook endpoint subscriptions. DALP owns these fixed intake routes and processes them before any customer-facing event delivery happens.
Use the webhook events reference and the AsyncAPI manifest to check event names, lifecycle states, and payload schemas before choosing endpoint subscriptions.
Endpoint model
Create endpoints with POST /api/v2/webhooks. The request includes:
| Field | Behaviour |
|---|---|
url | Required HTTPS target URL for delivery. |
displayName | Optional label, up to 200 characters. |
subscriptions | Event patterns to deliver. Defaults to *.final, *.retracted, and *.recalled. |
defaultPayloadShape | Must be thin when creating an endpoint. Switch to fat later with a PATCH request and the required field acknowledgement. |
counterSignedReceipts | Optional flag for endpoints that return signed delivery receipts. |
The create and rotate-secret responses reveal the signing secret once. Later reads return endpoint metadata and secret status, not the cleartext signing secret. If a client retries the same create or rotate request with the same idempotency key, DALP returns the same response envelope with signingSecret: null so the cleartext secret is not exposed again. Store the first dalp_whsk_... value securely, and do not overwrite it with null from an idempotent retry.
Mutation idempotency
Webhook endpoint mutations accept the Idempotency-Key header. Send a stable key when creating an endpoint, updating endpoint settings, rotating secrets, retrying deliveries, replaying events, recalling events, or changing secret state from an automated workflow. If the network drops after DALP accepts the request, retry the same request with the same key instead of submitting a second mutation.
Reuse an idempotency key only for the same request body. Reusing the key for a different body is rejected so one key cannot accidentally describe two different operations. Keep request idempotency separate from consumer-side event deduplication: the Idempotency-Key protects the API mutation, while webhook receivers should still dedupe delivered events by the webhook-id header and any stable business fields carried by the event.
Payload privacy
DALP delivers thin payloads by default. Thin payloads omit configured personal-data fields for event types such as identity registration, access-control role changes, asset issuance, compliance freeze recalls, and token transfers.
Create the endpoint as thin first. Switching an endpoint to fat requires a later PATCH /api/v2/webhooks/{id} request with a fatEventsAcknowledgment.fieldsAcknowledged list that covers every additional field implied by the endpoint's subscriptions. DALP rejects the update when the acknowledgement does not match the subscription set.
Delivery operations
| Operation | API route |
|---|---|
| List endpoints | GET /api/v2/webhooks |
| Read endpoint metadata | GET /api/v2/webhooks/{id} |
| Update URL, subscriptions, payload shape, receipt mode, or disabled state | PATCH /api/v2/webhooks/{id} |
| Disable an endpoint | DELETE /api/v2/webhooks/{id} |
| Enqueue a test event | POST /api/v2/webhooks/{id}/test-events |
| List delivery attempts | GET /api/v2/webhooks/{id}/deliveries |
| Read one delivery attempt | GET /api/v2/webhooks/{id}/deliveries/{deliveryId} |
| Retry one delivery event | POST /api/v2/webhooks/{id}/deliveries/{deliveryId}/retries |
| Replay historical events | POST /api/v2/webhooks/{id}/replays |
| Recall an event | POST /api/v2/webhooks/events/{evtId}/recall |
| Get chain-of-custody proof | GET /api/v2/webhooks/events/{evtId}/chain-of-custody |
| Rotate the signing secret | POST /api/v2/webhooks/{id}/rotate-secret |
| Revoke the previous signing secret | POST /api/v2/webhooks/{id}/revoke-previous-secret |
| Read delivery statistics | GET /api/v2/webhooks/stats |
When updating an endpoint URL while deliveries are pending, pass acknowledgePending=true only when you intend DALP to retarget those queued attempts to the new URL.
Secret rotation keeps the previous signing secret valid for a 24-hour overlap. Revoke the previous secret after DALP has observed delivery under the new secret.
Retry, replay, and consumer idempotency
Use the retry and replay routes for different recovery jobs:
| Recovery job | Use it when | Request shape | Consumer expectation |
|---|---|---|---|
| Retry one delivery | One recorded delivery attempt needs to be scheduled again for the same event. | POST /api/v2/webhooks/{id}/deliveries/{deliveryId}/retries | Dedupe by the webhook-id header before applying side effects. |
| Replay an event | A known event must be enqueued for the endpoint again. | POST /api/v2/webhooks/{id}/replays with evtId and optional chainId | Treat the replay as another delivery of the same event, not as a new business event. |
| Replay a block range | A receiver was offline or a new endpoint must catch up from historical chain events. | POST /api/v2/webhooks/{id}/replays with fromBlock, optional toBlock, chainId, and confirmLargeRange for large ranges | Dedupe delivery attempts by webhook-id. Dedupe business side effects by stable payload fields such as transaction hash, log index, and lifecycle state when the event type includes them. |
Replay responses return a replayId, the endpointId, and eventsEnqueued. The response also includes snapshotToBlock when DALP bounded the replay to a chain snapshot. Replays enqueue delivery work; they do not change the original event's lifecycle state.
For request-to-chain reconciliation, keep DAPI idempotency and webhook outcome handling separate. A cached mutation response proves that DALP accepted the synchronous request for that idempotency key. The webhook event proves the later chain outcome. See Idempotency and on-chain outcome for the consumer-side reconciliation pattern.
Ripple Custody inbound callbacks
POST /api/webhooks/ripple is the inbound callback boundary for Ripple Custody intent events. Ripple calls this route when a custody intent changes state. DALP parses the provider envelope and acknowledges intermediate events. Terminal approval or rejection events are dispatched to the custody approval monitor for the matching intent.
This route is separate from outbound webhook endpoints created with POST /api/v2/webhooks:
| Direction | Purpose | Who configures the URL | Public API surface |
|---|---|---|---|
| Outbound webhook endpoint | DALP sends selected platform events to your HTTPS receiver | You create and manage the receiver URL | /api/v2/webhooks and child routes |
| Ripple Custody callback | Ripple Custody sends intent state changes back to DALP | The platform exposes the DALP callback URL to the provider integration | POST /api/webhooks/ripple |
Accepted provider envelope
DALP reads the Ripple Custody event type from payload.type, not from a top-level type field. The envelope can include provider event metadata such as domainId, event id, savedAt, and sequenceNumber.
{
"domainId": "25aaec0d-e8dc-44b6-8070-9231f1ddadf0",
"id": "03424a3f-bdfc-4521-b8b2-933a8ff13cce",
"payload": {
"id": "35e4d8d9-f943-484b-865c-c736679ba0cc",
"type": "IntentApproved"
},
"savedAt": "2026-05-19T07:32:00.000Z",
"sequenceNumber": 369
}Terminal approval events resolve the custody approval as approved. Terminal rejection, failure, expiry, closed, or denied events resolve it as rejected. Intermediate events are acknowledged without changing the approval monitor. Malformed or unrecognised events are also acknowledged without a monitor update.
Authentication and request limits
The Ripple callback body is capped at 64 KB before DALP verifies a signature or parses JSON. Requests over the cap return 413.
If RIPPLE_WEBHOOK_SECRET is set, DALP verifies an HMAC-SHA256 signature from x-ripple-webhook-signature, with x-webhook-signature as a fallback header. A signature mismatch returns 410 so the provider stops retrying a permanently unauthenticated delivery.
In production, DALP refuses to process Ripple callbacks when RIPPLE_WEBHOOK_SECRET is not set unless the deployment explicitly opts out with RIPPLE_INSECURE_NO_HMAC=1. Non-production deployments can proceed without the HMAC secret, but the integration then relies on HTTPS and network allowlisting at the deployment boundary.
Replay and retry behaviour
When the provider envelope includes both domainId and sequenceNumber, DALP records the highest sequence number seen for each (domainId, intent) pair. A duplicate or stale sequence is acknowledged with 200 and is not dispatched again. If the deduplication write fails, DALP still dispatches the terminal event because the approval monitor is idempotent for repeated resolution signals.
DALP returns provider-facing status codes with these meanings:
| Condition | Response | Effect |
|---|---|---|
| Valid terminal event dispatched to the approval monitor | 200 | Delivery accepted |
| Intermediate, malformed, unrecognised, duplicate, or stale event | 200 | Delivery acknowledged without a monitor update |
| Body exceeds 64 KB | 413 | Delivery rejected as too large |
| Signature mismatch when the HMAC secret is configured | 410 | Delivery rejected permanently |
| Approval monitor is not reachable yet, or a retryable dispatch error occurs | 503 | Provider can retry delivery |
| Approval monitor rejects the signal as a permanent client error | 410 | Provider can stop retrying delivery |
Related pages:
Counter-signed receipts
Enable counterSignedReceipts when the receiving system must prove that it accepted a delivered event. DALP accepts receipts only for deliveries queued with counter-signed receipts enabled. Each receipt record stores the submitted signature, inner event hash, receipt time, and verification status.
| Operation | API route |
|---|---|
| List counter-signed webhook receipts | GET /api/v2/webhook-receipts |
| Read one counter-signed webhook receipt | GET /api/v2/webhook-receipts/{id} |
| Submit a consumer counter-signed receipt | POST /api/v2/webhook-receipts |
A receipt submission includes deliveryId, evtId, endpointId, consumerSignature, and innerEventHash. The consumer signature is an HMAC-SHA256 signature over the inner event hash. Generate it with the endpoint signing secret after removing the dalp_whsk_ prefix, or use the DALP SDK helper that normalizes webhook secrets for Standard Webhooks. DALP also compares the submitted hash with the delivered event body, so a receipt only verifies when the signature and delivered payload hash both match.
Late receipts are rejected after the receipt window closes. A consumer that misses the window should acknowledge the next retry attempt instead of trying to repair the timed-out delivery.
Audit proof
DALP records delivery rows with the fields that were redacted during delivery preparation. It also records hop hashes for the prepared payload.
Use GET /api/v2/webhooks/events/{evtId}/chain-of-custody when an audit workflow needs proof for one event. The response uses the standard single-resource envelope:
{
"data": {
"evtId": "evt_01h...",
"hops": [
{
"stage": "outbox-write",
"contentHash": "sha256:4f7c...",
"signedBy": "0x...",
"recordedAt": "2026-05-13T07:32:00.000Z"
}
],
"merkleRoot": "7a1f...",
"platformSignature": "dalp-platform:7a1f..."
},
"links": {
"self": "/v2/webhooks/events/evt_01h.../chain-of-custody"
}
}The hops array lists the recorded delivery stages and content hashes. signedBy is present when a hop has a recorded signer. Downstream systems can compare the hop hashes with the merkleRoot and platform signature when they need evidence of what DALP prepared for delivery.
Related pages: