> ## 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.

# Webhooks

> Receive signed Relay agent events at an HTTPS endpoint.

Save a webhook subscription to deliver agent events to an HTTPS endpoint you
control.

<Note>
  At least one saved subscription selects Webhook delivery. Relay closes any
  connected agent sockets when the first subscription is created, then drains
  pending events to Webhooks.
</Note>

## Flow

```text theme={null}
Relay event → signed HTTP POST → verify → durable insert → 2xx → process
```

## Create a subscription

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

  ```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",
        "message.delivered",
        "message.read",
        "reaction.added",
        "reaction.removed"
      ]
    }'
  ```
</CodeGroup>

Save `signing_secret` from the `201` response. Relay returns it once.

Relay never sends the same event through Webhooks and WebSocket. Delete the
last subscription to move pending events to
[WebSocket](/guides/websocket).

## Verify the signature

Relay uses Standard Webhooks headers:

| Header              | Use                       |
| ------------------- | ------------------------- |
| `webhook-id`        | Stable event ID           |
| `webhook-timestamp` | Unix timestamp in seconds |
| `webhook-signature` | `v1,<base64-signature>`   |

Verify the exact raw body before parsing JSON.

<Tabs>
  <Tab title="TypeScript SDK">
    ```typescript theme={null}
    const event = relay.webhooks.unwrap(rawBody, {
      headers: request.headers,
    });
    ```
  </Tab>

  <Tab title="Signing input">
    ```text theme={null}
    webhook-id.webhook-timestamp.raw-body
    ```
  </Tab>
</Tabs>

## Acknowledge safely

<Steps>
  <Step title="Verify">Reject invalid signatures before parsing.</Step>
  <Step title="Deduplicate">Insert `event_id` under a unique constraint.</Step>
  <Step title="Commit">Save the exact event or a durable job.</Step>
  <Step title="Respond">Return `200` or `204` within 10 seconds.</Step>
  <Step title="Process">Run tools, model work, and replies after acknowledgement.</Step>
</Steps>

A successful `message.received` response marks that agent recipient Delivered.

`message.received` is an event name, not a Message delivery state. Message
state remains `sent`, `delivered`, or `read`.

## Review with an agent

**Audit a webhook receiver against Relay's durable acceptance boundary.** Copy
this prompt into your coding agent.

```text theme={null}
You are auditing a codebase that receives Relay webhooks.

This is read-only. Do not change code unless I ask.

1. Read https://docs.relayapp.im/llms.txt, the Webhooks guides, and the current Relay OpenAPI.
2. Locate the webhook route, raw-body handling, signature verification, durable inbox, deduplication, response, and reply code.
3. Prove the signature is checked before JSON parsing.
4. Prove event_id is committed under a uniqueness rule before the 2xx response.
5. Prove duplicates return success without repeating model work, tools, or replies.
6. Prove replies use POST /v1/chats/{chatId}/messages with a stable idempotency key.
7. Prove redirects are not followed and unsafe destination addresses are rejected.
8. Report Check | Status | file:line evidence | Fix.
9. Mark anything you cannot prove as unknown.
```

## Related

* [Subscriptions](/guides/webhooks/subscriptions)
* [Webhook events](/guides/webhooks/events)
* [Delivery and retries](/guides/webhooks/delivery)
