The webhooks feature must be enabled using a feature flag on your Ripple Custody instance. Contact your Ripple liaison if you're unable to access the feature.
Use this page to manage webhook channels with the Event Distribution Service (EDS) API. Webhooks push event notifications to an HTTPS endpoint that you control. To pull event history for reconciliation or audit workflows, see Manage events with the API.
| Action | API procedure | API reference |
|---|---|---|
| Create a webhook channel | Create a webhook channel | Create a channel |
| List webhook channels | List webhook channels | Get all channels |
| Get webhook channel details | Get webhook channel details | Get channel details |
| Update a webhook channel | Update a webhook channel | Update a channel |
| Test a webhook channel | Test a webhook channel | Test a channel |
| Delete a webhook channel | Delete a webhook channel | Delete a channel |
| List delivery events across channels | Track webhook delivery events | Get all events |
| List delivery events for a channel | Track webhook delivery events | Get all events for a channel |
| Get delivery event details | Track webhook delivery events | Get channel event details |
Prepare the following values:
| Value | Description |
|---|---|
domainId | Domain that owns the webhook channel. |
JWT_TOKEN | JWT for an authenticated API user. |
createdBy or lastUpdatedBy | User ID for the user creating or updating the channel. |
| Webhook URL | HTTPS endpoint where Ripple Custody sends event notifications. |
| Event types | Event payload.type values to subscribe to, such as IntentCreated, IntentUpdated, or IntentClosed. |
You must be a user with the eds-manager role to create and manage webhook channels for your domain.
You can subscribe to many event types when configuring a webhook. For detailed information about each event type and complete payload structures, see Event payload reference.
For intent state changes, subscribe to IntentUpdated to receive updates when an intent is approved or rejected. Subscribe to IntentClosed to receive updates when an intent reaches its final state.
When a subscribed event occurs, Ripple Custody sends a minimal event payload to your webhook URL. The callback body uses the event shape returned by the Events API and is designed for easy parsing.
{
"domainId": "25aaec0d-e8dc-44b6-8070-9231f1ddadf0",
"payload": {
"id": "35e4d8d9-f943-484b-865c-c736679ba0cc",
"type": "IntentCreated"
},
"id": "03424a3f-bdfc-4521-b8b2-933a8ff13cce",
"sequenceNumber": 369,
"savedAt": "2025-03-07T14:22:37.389Z"
}| Field | Description |
|---|---|
domainId | Domain where the event was generated. |
payload | Event-specific payload. The payload includes a type discriminator. |
id | Unique event ID. |
sequenceNumber | Event sequence number. Sequence numbers are strictly increasing, but may not be contiguous. |
savedAt | Timestamp when the event was saved. |
For event-specific payload fields and event type names, see Event payload reference.
Call POST /v1/domains/{domainId}/channels to create a webhook channel.
curl -X POST "${CUSTODY_API_URL}/v1/domains/${DOMAIN_ID}/channels" \
-H "Authorization: Bearer ${JWT_TOKEN}" \
-H "Content-Type: application/json" \
-d @create-webhook-channel.jsonThe request body uses the EDS_WebhookChannelCreate schema:
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"name": "My Webhook Channel",
"supportedEventTypes": [
"IntentCreated",
"IntentUpdated",
"IntentClosed"
],
"createdBy": "b7c8d9e0-f1a2-3456-bcde-f23456789012",
"type": "WEBHOOK",
"url": "https://your-domain.com/webhook-endpoint"
}| Field | Type | Required | Description |
|---|---|---|---|
id | string, UUID | Yes | Unique identifier for the webhook channel. |
name | string | Yes | Descriptive channel name. |
supportedEventTypes | array of strings | Yes | Event payload.type values to subscribe to. |
createdBy | string, UUID | Yes | User ID for the user creating the channel. |
type | string | Yes | Channel type. For webhooks, use WEBHOOK. |
url | string | Yes | Webhook endpoint URL where events are sent. |
status | string | No | Initial channel status. Supported values are ACTIVE and DISABLED. |
maxRetries | integer | No | Maximum number of retry attempts for failed webhook deliveries. |
The API returns the created channel:
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"domainId": "25aaec0d-e8dc-44b6-8070-9231f1ddadf0",
"name": "My Webhook Channel",
"supportedEventTypes": [
"IntentCreated",
"IntentUpdated",
"IntentClosed"
],
"type": "WEBHOOK",
"status": "ACTIVE",
"maxRetries": 3,
"url": "https://your-domain.com/webhook-endpoint",
"createdBy": "b7c8d9e0-f1a2-3456-bcde-f23456789012",
"lastUpdatedBy": "b7c8d9e0-f1a2-3456-bcde-f23456789012",
"createdAt": "2025-03-07T14:22:37.389Z",
"lastUpdatedAt": "2025-03-07T14:22:37.389Z",
"errorRate": 0
}| Field | Description |
|---|---|
id | Unique channel ID. |
domainId | Domain that owns the channel. |
name | Channel name. |
supportedEventTypes | Event types subscribed by the channel. |
type | Channel type. Webhook channel responses use WEBHOOK. |
status | Channel status. Supported response values are ACTIVE and DISABLED. |
maxRetries | Maximum number of retry attempts for failed webhook deliveries. |
url | Webhook endpoint URL. |
createdBy | User ID for the user who created the channel. |
lastUpdatedBy | User ID for the user who last updated the channel. |
createdAt | Timestamp when the channel was created. |
lastUpdatedAt | Timestamp when the channel was last updated. |
errorRate | Webhook delivery error rate. |
Call GET /v1/domains/{domainId}/channels to retrieve all webhook channels for a domain.
curl -X GET "${CUSTODY_API_URL}/v1/domains/${DOMAIN_ID}/channels" \
-H "Authorization: Bearer ${JWT_TOKEN}"The response is an array of channel objects.
Call GET /v1/domains/{domainId}/channels/{channelId} to retrieve one webhook channel.
curl -X GET "${CUSTODY_API_URL}/v1/domains/${DOMAIN_ID}/channels/${CHANNEL_ID}" \
-H "Authorization: Bearer ${JWT_TOKEN}"The response is a channel object.
Call PATCH /v1/domains/{domainId}/channels/{channelId} to update a webhook channel. Include only the fields you need to change.
curl -X PATCH "${CUSTODY_API_URL}/v1/domains/${DOMAIN_ID}/channels/${CHANNEL_ID}" \
-H "Authorization: Bearer ${JWT_TOKEN}" \
-H "Content-Type: application/json" \
-d @update-webhook-channel.jsonThe request body uses the EDS_ChannelUpdate schema:
{
"name": "Operations Webhook Channel",
"supportedEventTypes": [
"IntentCreated",
"IntentUpdated",
"IntentClosed"
],
"status": "active",
"maxRetries": 3,
"lastUpdatedBy": "b7c8d9e0-f1a2-3456-bcde-f23456789012"
}| Field | Type | Description |
|---|---|---|
name | string | New channel name. |
supportedEventTypes | array of strings | New list of event types to subscribe to. |
status | string | New channel status. The update schema supports active and disabled. |
maxRetries | integer | New maximum number of retry attempts for failed webhook deliveries. |
lastUpdatedBy | string, UUID | User ID for the user updating the channel. |
The update request schema uses lowercase active and disabled values. Channel responses use uppercase ACTIVE and DISABLED values.
The API returns the updated channel object.
Call POST /v1/domains/{domainId}/channels/{channelId}/test to test a webhook channel. The operation does not define a request body.
curl -X POST "${CUSTODY_API_URL}/v1/domains/${DOMAIN_ID}/channels/${CHANNEL_ID}/test" \
-H "Authorization: Bearer ${JWT_TOKEN}"The API returns 200 OK when the test request is accepted.
Call DELETE /v1/domains/{domainId}/channels/{channelId} to delete a webhook channel. The operation does not define a request body.
curl -X DELETE "${CUSTODY_API_URL}/v1/domains/${DOMAIN_ID}/channels/${CHANNEL_ID}" \
-H "Authorization: Bearer ${JWT_TOKEN}"The API returns 200 OK when the channel is deleted.
EDS delivery events are operational records for webhook delivery attempts. They are different from the event payload sent to your webhook endpoint.
| Task | Endpoint |
|---|---|
| List delivery events across all channels in a domain | GET /v1/domains/{domainId}/channels/events |
| List delivery events for one channel | GET /v1/domains/{domainId}/channels/{channelId}/events |
| Get one delivery event | GET /v1/domains/{domainId}/channels/{channelId}/events/{eventId} |
List all delivery events across channels:
curl -X GET "${CUSTODY_API_URL}/v1/domains/${DOMAIN_ID}/channels/events" \
-H "Authorization: Bearer ${JWT_TOKEN}"List delivery events for one channel:
curl -X GET "${CUSTODY_API_URL}/v1/domains/${DOMAIN_ID}/channels/${CHANNEL_ID}/events" \
-H "Authorization: Bearer ${JWT_TOKEN}"Get one delivery event:
curl -X GET "${CUSTODY_API_URL}/v1/domains/${DOMAIN_ID}/channels/${CHANNEL_ID}/events/${EVENT_ID}" \
-H "Authorization: Bearer ${JWT_TOKEN}"Delivery event list operations return an array of EDS_Event objects. The delivery event detail operation returns one EDS_Event object.
{
"id": "c0c54215-77e8-4e8f-8b0f-b3d60269f2f7",
"channelId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"channelName": "My Webhook Channel",
"type": "IntentCreated",
"status": "FAILED",
"createdAt": "2025-03-07T14:22:37.389Z",
"lastUpdatedAt": "2025-03-07T14:22:40.889Z",
"retriedCount": 3,
"payload": "{\"id\":\"35e4d8d9-f943-484b-865c-c736679ba0cc\",\"type\":\"IntentCreated\"}"
}| Field | Description |
|---|---|
id | Unique delivery event ID. |
channelId | Webhook channel ID. |
channelName | Webhook channel name. |
type | Event type associated with the delivery event. |
status | Delivery status. Supported values are PENDING, SUCCESS, and FAILED. |
createdAt | Timestamp when the delivery event was created. |
lastUpdatedAt | Timestamp when the delivery event was last updated. |
retriedCount | Number of retry attempts recorded for the delivery event. |
payload | Event payload stored as a string. |
If a webhook delivery fails, for example if your endpoint is unavailable or returns an error, Ripple Custody retries delivery using exponential backoff.
- Ripple Custody sends the event to your webhook endpoint.
- If delivery fails, the system waits and retries using exponential backoff.
- After the maximum number of retry attempts is exhausted, the delivery event is marked as
FAILEDand automatic retries stop.
The default retry behavior is:
| Parameter | Default | Description |
|---|---|---|
| Max retries | 3 | Number of retry attempts before marking the delivery event as FAILED. |
| Initial interval | 500 ms | Wait time before the first retry. |
| Multiplier | 2 | Factor applied to increase wait time between retries. |
| Max interval | 5000 ms | Upper limit for wait time between retries. |
With the default values, if delivery fails, the system retries after 500 ms, then 1000 ms, then 2000 ms.
The EDS channel schema exposes maxRetries for channel configuration. Other retry timing settings are instance-level operational configuration and require coordination with Ripple teams to modify.
| Parameter | Default | Description |
|---|---|---|
| Request timeout | 30 seconds | Maximum time to wait for your endpoint to respond. |
| Connection timeout | 10 seconds | Maximum time to establish a connection to your endpoint. |
If your endpoint does not respond within the timeout period, the delivery is considered failed and is retried.
| Status code range | Behavior |
|---|---|
2xx | Delivery successful, no retry. |
3xx | Redirects are not followed. Delivery is treated as failed and retried. |
4xx | Delivery failed and retried, except for 410 Gone. |
410 | Delivery failed, no retry. The endpoint is treated as permanently unavailable. |
5xx | Delivery failed and retried. |
| Timeout | Delivery failed and retried. |
| Connection error | Delivery failed and retried. |
To prevent retries for a specific event, return 410 Gone. For all other cases, return 2xx to acknowledge successful receipt or return another status code to allow retries.
Webhook deliveries are not rate-limited by default. Design your endpoint to handle:
- Multiple concurrent webhook deliveries.
- Bursts of events during high activity periods.
- Retry attempts for failed deliveries.
If your endpoint cannot keep up with event volume, consider:
- Implementing a queue to buffer incoming webhooks.
- Scaling your webhook receiver horizontally.
- Using AMQP queues instead for guaranteed delivery and backpressure handling.
Use EDS delivery events, the Events API, and any configured AMQP integration when recovering from webhook delivery failures:
- Check delivery event records for
FAILEDstatus. - Use
retriedCountandlastUpdatedAtto understand retry activity. - Use List events with
sortBy=sequenceNumberto reconcile the event history your system processed. - Page through Events API results with
startingAfter; the Events API schema does not define time-range filters. - If your deployment includes an AMQP queue integration, inspect the queue to recover missed events. Contact Ripple support for help with AMQP-based recovery.
- Process any missing event IDs or sequence numbers in your integration.
Secure the endpoint that receives webhook callbacks:
- Use HTTPS endpoints.
- Validate incoming requests.
- Implement authentication or authorization for your receiver.
- Use IP allowlisting if required by your environment.
Want to manage webhooks with the UI instead? See Manage webhooks in the UI.