# Webhooks

> Get an HTTPS callback when a session finishes processing or an export completes.

Source: https://rayform.world/docs/api/webhooks
Last updated: 2026-10-18
Status: Rayform is in private preview. APIs, formats and the SDK described here may change before general availability.

Webhooks send a `POST` to your endpoint when something finishes, so you do not need to poll.

## Events

| Event | Sent when |
| --- | --- |
| `session.processed` | A session finished processing and its segments are searchable |
| `session.failed` | Processing stopped; the payload includes the session's `issues` |
| `export.completed` | An export finished writing |
| `export.failed` | An export stopped |

## Create an endpoint

```endpoint
POST /v1/webhook_endpoints
```

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `url` | string | yes | HTTPS URL to call |
| `events` | array | yes | Event names to send |

```bash
curl https://api.rayform.world/v1/webhook_endpoints \
  -H "Authorization: Bearer $RAYFORM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://ml.example.com/hooks/rayform", "events": ["session.processed", "export.completed"]}'
```

The response includes a `secret` beginning with `whsec_`. It is shown once; store it to verify signatures.

## Payload

```json
{
  "id": "evt_01JA9F2KQ8",
  "type": "session.processed",
  "created_at": "2026-10-14T13:58:48Z",
  "data": {
    "object": { "id": "ses_01JA8K2M9Q", "object": "session", "status": "processed", "segment_count": 14 }
  }
}
```

## Verifying signatures

Every request carries a `Rayform-Signature` header: `t=<unix seconds>,v1=<hex HMAC-SHA256>`. The signed message is the timestamp, a dot, and the raw request body.

```python title="verify.py"
import hashlib
import hmac
import time


def verify(body: bytes, header: str, secret: str, tolerance_s: int = 300) -> bool:
    parts = dict(item.split("=", 1) for item in header.split(","))
    timestamp, signature = int(parts["t"]), parts["v1"]
    if abs(time.time() - timestamp) > tolerance_s:
        return False
    expected = hmac.new(secret.encode(), f"{timestamp}.".encode() + body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, signature)
```

Respond with any `2xx` within 10 seconds. Failed deliveries are retried with exponential backoff for up to 24 hours. Events can arrive more than once or out of order; use `id` to deduplicate.
