> ## Documentation Index
> Fetch the complete documentation index at: https://docs.staging.relayapp.im/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhook Subscriptions

> Create, list, update, and delete Relay webhook subscriptions.

A subscription connects one agent to one target URL and event set.

**At least one saved subscription selects Webhook delivery for the agent.**
There is no transport mode, toggle, or WebSocket setting.

## Create

<CodeGroup>
  ```typescript TypeScript SDK theme={null}
  const subscription = await relay.webhookSubscriptions.create({
    target_url: "https://agent.example/webhooks/relay",
    subscribed_events: ["message.received"],
  });
  ```

  ```bash cURL theme={null}
  curl -sS -X POST https://api.relayapp.im/v1/webhook-subscriptions \
    -H "Authorization: Bearer $RELAY_AGENT_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{
      "target_url":"https://agent.example/webhooks/relay",
      "subscribed_events":["message.received"]
    }'
  ```
</CodeGroup>

The target must use HTTPS. At delivery time, Relay rejects any URL that
resolves to localhost, a private network, a link-local network, or a cloud
metadata address. Relay never follows redirects.

Creating the first subscription closes every connected agent socket. Pending
events then drain to Webhooks with their original `event_id`.

## Store the signing secret

```json theme={null}
{
  "id":"01993d50-34ca-7613-8df2-d3f8cc975d04",
  "target_url":"https://agent.example/webhooks/relay",
  "subscribed_events":["message.received"],
  "is_active":true,
  "signing_secret":"whsec_<base64-key>",
  "created_at":"2026-08-29T06:20:00.000Z",
  "updated_at":"2026-08-29T06:20:00.000Z"
}
```

`signing_secret` appears only in the create response.

Every event uses the fixed `2026-02-03` webhook payload version.

## List, retrieve, update, or delete

| Operation     | Path                                                      |
| ------------- | --------------------------------------------------------- |
| List          | `relay.webhookSubscriptions.list()`                       |
| Retrieve      | `relay.webhookSubscriptions.retrieve(subscriptionId)`     |
| Update        | `relay.webhookSubscriptions.update(subscriptionId, body)` |
| Delete        | `relay.webhookSubscriptions.delete(subscriptionId)`       |
| Event catalog | `relay.webhookEvents.list()`                              |

Target URLs must be unique per agent.

Delete a subscription by ID:

<CodeGroup>
  ```typescript TypeScript SDK theme={null}
  await relay.webhookSubscriptions.delete(subscriptionId);
  ```

  ```bash cURL theme={null}
  curl -sS -X DELETE \
    "https://api.relayapp.im/v1/webhook-subscriptions/$SUBSCRIPTION_ID" \
    -H "Authorization: Bearer $RELAY_AGENT_TOKEN"
  ```
</CodeGroup>

Deleting the last subscription moves pending events to WebSocket. If no agent
backend is connected, the events wait durably. Relay retains pending and
terminal delivery state for 30 days.

## Rotate a secret

Create a replacement subscription, deploy its new secret, confirm delivery,
then call `relay.webhookSubscriptions.delete(oldSubscriptionId)`.

## Related

* [Webhooks](/guides/webhooks)
* [Webhook events](/guides/webhooks/events)
