# Webhooks

**Note**

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.

Webhooks are lightweight HTTP callbacks that allow real-time delivery of event notifications to your systems. You can use the Event Distribution Service (EDS) API to programmatically manage webhook channels.

You can select from a wide range of events that you want to subscribe to and configure a webhook URL. Once you complete the setup, Ripple Custody will automatically send minimal event payloads to your registered endpoint when the selected events occur.

## Available event types

You can subscribe to many event types when configuring a webhook. For detailed information about each event type and complete payload structures, see [Event tracking](/products/custody/v1.34/system-management/events) for a complete list.

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.

## Event payload

Event payloads are minimal and designed for easy parsing.

The following sample shows a typical payload for an intent creation event:


```json
{
    "domainId": "25aaec0d-e8dc-44b6-8070-9231f1ddadf0",
    "id": "03424a3f-bdfc-4521-b8b2-933a8ff13cce",
    "payload": {
        "id": "35e4d8d9-f943-484b-865c-c736679ba0cc",
        "type": "IntentCreated"
    },
    "savedAt": "2025-03-07T14:22:37.389Z",
    "sequenceNumber": 369
}
```

**Payload fields:**

| Field | Description |
|  --- | --- |
| `domainId` | The unique identifier of the domain where the event originated |
| `id` | The ID of the webhook notification |
| `payload.id` | The ID of the event |
| `payload.type` | The type of event (e.g., `IntentCreated`, `IntentUpdated`) |
| `savedAt` | The timestamp when the webhook notification was sent |
| `sequenceNumber` | The sequence in which this event occurred |


## Securing your webhook endpoint

Make sure your webhook endpoint is properly secured to prevent unauthorized access. Best practices include:

- Use HTTPS endpoints only
- Validate incoming requests
- Implement authentication/authorization
- Use IP allowlisting if needed


## Delivery retries

If a webhook delivery fails (for example, if your endpoint is down or returns an error), Ripple Custody automatically retries delivery using an **exponential backoff** strategy.

### How it works

1. **Initial delivery attempt** — Ripple Custody sends the event to your webhook endpoint.
2. **Retry on failure** — If delivery fails, the system waits and retries using exponential backoff.
3. **Maximum retries** — After the maximum number of retry attempts is exhausted, the event delivery status is marked as `FAILED` and automatic retries stop.


The retry mechanism uses exponential backoff with the following default parameters:

| Parameter | Default | Description |
|  --- | --- | --- |
| Max retries | 3 | Number of retry attempts before marking 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 |


**Example with defaults**: If delivery fails, the system retries after 500 ms, then 1000 ms, then 2000 ms (capped at 5000 ms if exceeded).

Retry parameters are configured at the instance level and require coordination with Ripple teams to modify. Contact your Ripple partner engineer if you need to adjust retry behavior for your deployment.

### Handling failed deliveries

If webhook delivery fails after all retries are exhausted, you have two options to recover missed events:

| Recovery method | Description |
|  --- | --- |
| **Events API** | Use the [List events](/products/custody/v1.34/api/reference/openapi/events/getevents) API to retrieve events that occurred during the downtime. Filter by time range or sequence number to identify missed events. |
| **AMQP queue** | If your deployment includes an AMQP queue integration, you can inspect the queue to recover missed events. Contact [Ripple support](/products/custody/v1.34/support/get-support) for assistance with AMQP-based recovery. |


## Create a webhook channel

Prerequisite
You must be a user with the role `eds-manager` to create and manage webhooks for your domain.

To create a webhook channel using the API:

1. Obtain a JWT token. See [Authenticate API requests](/products/custody/v1.34/concepts/authenticate-api-requests).
2. Call the [Create channel](/products/custody/v1.34/api/reference/openapi/event-distribution-service/createchannel) operation with the following request body:



```json
{
    "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"
}
```

**Request body fields:**

| Field | Type | Required | Description |
|  --- | --- | --- | --- |
| `id` | string | Yes | The unique identifier for the webhook channel |
| `name` | string | Yes | A descriptive name for the webhook channel |
| `supportedEventTypes` | array | Yes | List of event types to subscribe to |
| `createdBy` | string | Yes | The unique identifier of the user creating the webhook channel |
| `type` | string | Yes | Must be `WEBHOOK` |
| `url` | string | Yes | The webhook endpoint URL where events will be sent |
| `status` | string | No | The channel status |
| `maxRetries` | integer | No | The maximum number of retry attempts for failed webhook deliveries |


1. The API returns a response with the created channel details:


**Example response:**


```json
{
    "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
}
```

**Response fields:**

| Field | Description |
|  --- | --- |
| `id` | The unique identifier for the webhook channel |
| `domainId` | The unique identifier of the domain that owns the channel |
| `name` | The name of the webhook channel |
| `type` | The channel type (always `WEBHOOK`) |
| `url` | The configured webhook endpoint URL |
| `supportedEventTypes` | Array of subscribed event types |
| `status` | The channel status |
| `maxRetries` | The maximum number of retry attempts for failed webhook deliveries |
| `createdBy` | The unique identifier of the user who created the channel |
| `lastUpdatedBy` | The unique identifier of the user who last updated the channel |
| `createdAt` | Timestamp when the channel was created |
| `lastUpdatedAt` | Timestamp when the channel was last updated |
| `errorRate` | The webhook delivery error rate |


## Update a webhook channel

To update an existing webhook channel, use the [Update channel](/products/custody/v1.34/api/reference/openapi/event-distribution-service/updatechannel) operation.

You can update:

- The webhook name
- The list of subscribed event types
- The channel status
- The maximum retry count


## Delete a webhook channel

To delete a webhook channel, use the [Delete channel](/products/custody/v1.34/api/reference/openapi/event-distribution-service/deletechannel) operation.

## List webhook channels

To retrieve all webhook channels for a domain, use the [List channels](/products/custody/v1.34/api/reference/openapi/event-distribution-service/getallchannels) operation.

## Get webhook channel details

To retrieve details for a specific webhook channel, use the [Get channel](/products/custody/v1.34/api/reference/openapi/event-distribution-service/getchannel) operation.

## Operational details

### Timeout configuration

| 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 will be retried.

### HTTP status code handling

| Status Code Range | Behavior |
|  --- | --- |
| `2xx` (Success) | Delivery successful, no retry |
| `3xx` (Redirect) | Not followed, treated as failure, will retry |
| `4xx` (Client Error) | Delivery failed, will retry (except `410 Gone`) |
| `410` (Gone) | Delivery failed, no retry (endpoint permanently unavailable) |
| `5xx` (Server Error) | Delivery failed, will retry |
| Timeout | Delivery failed, will retry |
| Connection Error | Delivery failed, will retry |


To prevent retries for a specific event (e.g., if your endpoint determines the event is invalid), return `410 Gone`. For all other cases, return `2xx` to acknowledge successful receipt or allow retries by returning other status codes.

### Rate limiting

Webhook deliveries are not rate-limited by default. However, your endpoint should be designed 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 the 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


Want to manage webhooks with the UI instead? See [Webhooks](/products/custody/v1.34/ui/environment/events/webhooks) in the UI documentation.