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

# Create Webhook

> Create a new webhook. A signing secret is auto-generated and returned once.

## Request Body

<ParamField body="url" type="string" required>
  The HTTPS endpoint URL that will receive webhook events.
</ParamField>

<ParamField body="events" type="array" required>
  List of event types to subscribe to. See [Event Types](/webhooks/events) for all available events.
</ParamField>

<ParamField body="isActive" type="boolean" default="true">
  Whether the webhook should start receiving events immediately.
</ParamField>

## Response

Returns the created webhook including the `secret` field. The secret is **only returned on creation** — save it securely for [signature verification](/webhooks/signatures).

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST "https://api.foxreach.io/api/v1/webhooks" \
    -H "X-API-Key: otr_your_key" \
    -H "Content-Type: application/json" \
    -d '{
      "url": "https://your-app.com/webhooks/outreach",
      "events": ["email.sent", "reply.received", "campaign.completed"]
    }'
  ```

  ```python Python theme={null}
  import requests

  response = requests.post(
      "https://api.foxreach.io/api/v1/webhooks",
      headers={"X-API-Key": "otr_your_key"},
      json={
          "url": "https://your-app.com/webhooks/outreach",
          "events": ["email.sent", "reply.received", "campaign.completed"]
      }
  )
  data = response.json()
  ```

  ```typescript TypeScript theme={null}
  const response = await fetch(
      "https://api.foxreach.io/api/v1/webhooks",
      {
          method: "POST",
          headers: {
              "X-API-Key": "otr_your_key",
              "Content-Type": "application/json"
          },
          body: JSON.stringify({
              url: "https://your-app.com/webhooks/outreach",
              events: ["email.sent", "reply.received", "campaign.completed"]
          })
      }
  );
  const data = await response.json();
  ```
</RequestExample>

<ResponseExample>
  ```json theme={null}
  {
    "data": {
      "id": "cwh_new456",
      "url": "https://your-app.com/webhooks/outreach",
      "isActive": true,
      "events": ["email.sent", "reply.received", "campaign.completed"],
      "secret": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6a7b8c9d0e1f2",
      "lastDeliveredAt": null,
      "consecutiveFailures": 0,
      "createdAt": "2025-01-15T10:00:00",
      "updatedAt": "2025-01-15T10:00:00"
    }
  }
  ```
</ResponseExample>

<Warning>
  Save the `secret` value immediately. It is only returned in this response and cannot be retrieved later. You'll need it to verify webhook signatures.
</Warning>

## Errors

| Status | Description                  |
| ------ | ---------------------------- |
| `422`  | Invalid event types provided |

## Available Event Types

```
email.sent, email.failed, email.bounced, email.opened,
reply.received, reply.categorized,
campaign.started, campaign.paused, campaign.completed,
lead.created, lead.updated
```

## Rate Limit

* **100 requests per minute, per API key.** Fixed 60-second window.
* Every response includes `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Reset` (unix epoch seconds).
* A `429` response includes a `Retry-After` header (seconds until the bucket resets).

For client-side patterns (`Retry-After`, exponential backoff, monitoring `X-RateLimit-Remaining`), see [Rate Limiting](/rate-limiting).
