> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://poly-ai.ferndocs.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://poly-ai.ferndocs.com/_mcp/server.

# Webhooks API

> Register webhook endpoints to receive real-time signed notifications for PolyAI events.

Use webhooks when your systems need to react to PolyAI events in real time–for example, triggering incident response when an alert fires or updating a dashboard. Webhooks include HMAC-SHA256 signatures and automatic retries, with support for secret rotation.

The Webhooks API lets you register HTTP endpoints that receive real-time notifications when events occur in your PolyAI account. Webhooks are currently used by the [Alerts API](/api-reference/monitoring/alerts-api/overview) and will expand to other services in the future.

## Key features

* **Signed delivery** - Every webhook includes an HMAC-SHA256 signature you can verify
* **Automatic retries** - Failed deliveries retry with exponential backoff
* **Secret rotation** - Rotate signing secrets without recreating the endpoint

## Limits

| Resource          | Maximum per account |
| ----------------- | ------------------- |
| Webhook endpoints | 10                  |

Requests to create a webhook endpoint beyond the limit return a `409 Conflict` error.

## Event types

| Event              | Description                                    |
| ------------------ | ---------------------------------------------- |
| `alerts.triggered` | An alert rule transitioned into a firing state |
| `alerts.resolved`  | A firing alert transitioned back to `ok`       |

## Webhook headers

Each webhook request includes these headers:

| Header               | Description                                        |
| -------------------- | -------------------------------------------------- |
| `X-PolyAI-Timestamp` | Unix timestamp (seconds) when the webhook was sent |
| `X-PolyAI-Signature` | HMAC-SHA256 signature for verification             |
| `X-PolyAI-Event-ID`  | Unique event identifier for deduplication          |

## Retry policy

Failed webhook deliveries are retried with exponential backoff:

| Attempt | Delay      | Cumulative time |
| ------- | ---------- | --------------- |
| 1       | Immediate  | 0               |
| 2       | 1 minute   | 1 minute        |
| 3       | 5 minutes  | 6 minutes       |
| 4       | 15 minutes | 21 minutes      |
| 5       | 1 hour     | \~1.5 hours     |
| 6       | 4 hours    | \~5.5 hours     |

Retried failures:

* Timeout
* Network error
* HTTP 408, 429, 5xx

Not retried:

* Other 4xx errors

## Signature verification

Verify webhook signatures to ensure requests are from PolyAI.

**Algorithm:** HMAC-SHA256

**Signed message format:** `{timestamp}.{raw_request_body}`

#### Python

```python
import hmac
import hashlib
import time

def verify_webhook(payload: bytes, timestamp: str, signature: str, secret: str) -> bool:
    # Reject requests older than 5 minutes
    if abs(time.time() - int(timestamp)) > 300:
        return False
    
    # Compute expected signature
    message = f"{timestamp}.{payload.decode('utf-8')}"
    expected = hmac.new(
        secret.encode('utf-8'),
        message.encode('utf-8'),
        hashlib.sha256
    ).hexdigest()
    
    # Constant-time comparison
    return hmac.compare_digest(expected, signature)
```

#### JavaScript

```javascript
const crypto = require('crypto');

function verifyWebhook(payload, timestamp, signature, secret) {
  // Reject requests older than 5 minutes
  const now = Math.floor(Date.now() / 1000);
  if (Math.abs(now - parseInt(timestamp)) > 300) {
    return false;
  }
  
  // Compute expected signature
  const message = `${timestamp}.${payload}`;
  const expected = crypto
    .createHmac('sha256', secret)
    .update(message)
    .digest('hex');
  
  // Constant-time comparison
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signature)
  );
}
```

#### Go

```go
package main

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/hex"
    "math"
    "strconv"
    "time"
)

func verifyWebhook(payload []byte, timestamp, signature, secret string) bool {
    // Reject requests older than 5 minutes
    ts, err := strconv.ParseInt(timestamp, 10, 64)
    if err != nil {
        return false
    }
    if math.Abs(float64(time.Now().Unix()-ts)) > 300 {
        return false
    }
    
    // Compute expected signature
    message := timestamp + "." + string(payload)
    mac := hmac.New(sha256.New, []byte(secret))
    mac.Write([]byte(message))
    expected := hex.EncodeToString(mac.Sum(nil))
    
    // Constant-time comparison
    return hmac.Equal([]byte(expected), []byte(signature))
}
```

> **Tip**
>
> Use `X-PolyAI-Event-ID` for deduplication since retries can deliver the same event more than once.

## Authentication

All Webhooks API endpoints use API key authentication with the `x-api-key` header. Resources are automatically scoped to your account.

> **Note**
>
> API keys are not yet available through self-service. To request access, contact your PolyAI representative.