Webhooks
Register webhook endpoints to receive lifecycle and post-call intelligence events from Rymi.
Register Webhook
http
POST /v1/webhooksRequest Body
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
url | string | Yes | — | HTTPS endpoint to receive events |
events | string[] | Yes | — | Events to subscribe to (see Available Events) |
secret | string | Yes | — | HMAC signing secret used to verify deliveries. Min 16 characters recommended |
Available Events
| Event | Description |
|---|---|
call.started | A call has been initiated and the room is active |
call.intelligence.ready | Post-call intelligence completed — transcript, summary, extraction, and evaluation data is ready |
call.completed | Legacy alias for post-call completion; still emitted for existing integrations |
call.failed | A call failed before post-call processing could begin |
batch.completed | All calls in a batch have concluded (either completed or failed) |
Example Request
bash
curl -X POST https://api.rymi.live/v1/webhooks \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/webhooks/rymi",
"events": ["call.intelligence.ready", "call.failed", "batch.completed"],
"secret": "whsec_your_webhook_secret_here"
}'Response 201
json
{
"id": "wh_123",
"url": "https://example.com/webhooks/rymi",
"events": ["call.intelligence.ready", "call.failed", "batch.completed"],
"created_at": "2026-03-01T10:00:00Z"
}Errors
| Status | Meaning |
|---|---|
400 | Missing or invalid url, events, or secret |
401 | Missing or invalid API key |
Payload Format
When an event fires, Rymi sends a POST request to your registered URL with the following JSON body:
call.intelligence.ready Payload
json
{
"type": "call.intelligence.ready",
"event": "call.intelligence.ready",
"timestamp": "2026-03-01T10:05:00Z",
"data": {
"call_id": "call_abc123",
"status": "completed",
"duration": 145,
"cost": 0.21,
"intelligence_status": "completed",
"transcript": {
"text": "Caller: Hello\nAgent: Hi there",
"segments": [
{
"speaker": "user",
"text": "Hello",
"sequence": 0,
"started_at_ms": 0,
"ended_at_ms": 900
}
]
},
"summary": "Caller asked for support and the agent resolved the issue.",
"sentiment": "neutral",
"structured_data": {
"customer_intent": "support"
},
"evaluation": {
"passed": true,
"score": 1,
"reasoning": "The issue was resolved."
},
"errors": []
}
}call.failed Payload
Dispatched immediately when a call cannot be completed. Intentionally smaller than call.intelligence.ready:
json
{
"type": "call.failed",
"event": "call.failed",
"timestamp": "2026-03-01T10:01:00Z",
"data": {
"call_id": "call_abc456",
"status": "failed",
"error": "PSTN leg could not connect: busy signal"
}
}batch.completed Payload
json
{
"type": "batch.completed",
"event": "batch.completed",
"timestamp": "2026-03-01T10:30:00Z",
"data": {
"batch_id": "batch_7c6f...",
"total": 50,
"completed": 47,
"failed": 3
}
}Payload Fields
| Field | Type | Description |
|---|---|---|
type | string | Event name (same value as event) |
event | string | Event name |
timestamp | string | ISO 8601 timestamp of when the event was emitted |
data | object | Event-specific payload |
Delivery Behavior
- Timeout: Rymi waits up to 30 seconds for your endpoint to respond with a
2xxstatus code. - Retries: Failed deliveries are retried up to 5 times with exponential backoff (roughly 10s, 30s, 90s, 270s, 810s).
- Ordering: Events are delivered in approximate order but are not guaranteed to arrive sequentially. Use
timestampandcall_idfor deduplication. - Idempotency: Your webhook handler should be idempotent — the same event may be delivered more than once.
Update Webhook
http
PATCH /v1/webhooks/:idUpdate an existing webhook's URL, subscribed events, or signing secret. Only provided fields are changed.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
id | string | Webhook ID |
Request Body
All fields are optional. Provide only the fields you want to change.
| Field | Type | Required | Description |
|---|---|---|---|
url | string | No | New HTTPS endpoint URL |
events | string[] | No | Updated event subscriptions |
secret | string | No | New HMAC signing secret (min 16 characters) |
Example — Change URL
bash
curl -X PATCH https://api.rymi.live/v1/webhooks/wh_123 \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://new-endpoint.example.com/webhooks/rymi"
}'Example — Update Subscribed Events
bash
curl -X PATCH https://api.rymi.live/v1/webhooks/wh_123 \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"events": ["call.intelligence.ready", "call.failed", "call.started"]
}'Response 200
json
{
"status": "updated",
"id": "wh_123"
}Errors
| Status | Meaning |
|---|---|
400 | Invalid url (not HTTPS, private network, etc.) or empty body |
401 | Missing or invalid API key |
404 | Webhook not found |
List Webhooks
http
GET /v1/webhooksQuery Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | integer | 50 | Max records to return |
offset | integer | 0 | Records to skip |
Example Request
bash
curl "https://api.rymi.live/v1/webhooks?limit=10" \
-H "Authorization: Bearer YOUR_API_KEY"Response 200
json
{
"webhooks": [
{
"id": "wh_123",
"url": "https://example.com/webhooks/rymi",
"events": ["call.intelligence.ready"],
"created_at": "2026-03-01T10:00:00Z"
}
],
"total": 1,
"offset": 0,
"limit": 10
}Errors
| Status | Meaning |
|---|---|
401 | Missing or invalid API key |
Delete Webhook
http
DELETE /v1/webhooks/:idPath Parameters
| Parameter | Type | Description |
|---|---|---|
id | string | Webhook ID |
Example Request
bash
curl -X DELETE https://api.rymi.live/v1/webhooks/wh_123 \
-H "Authorization: Bearer YOUR_API_KEY"Response 200
json
{
"status": "deleted",
"id": "wh_123"
}Errors
| Status | Meaning |
|---|---|
401 | Missing or invalid API key |
404 | Webhook not found |
Signature Verification
Every webhook delivery includes three headers for verification:
| Header | Description |
|---|---|
X-Rymi-Event | Event name (e.g., call.intelligence.ready) |
X-Rymi-Timestamp | Unix timestamp (seconds) used in the signature payload |
X-Rymi-Signature | HMAC-SHA256 hex digest of {timestamp}.{raw_body} |
Verification Steps
- Read the raw request body exactly as received (do not parse JSON first).
- Concatenate
X-Rymi-Timestamp, a literal., and the raw body string. - Compute an HMAC-SHA256 digest using your webhook
secretas the key. - Compare the result to
X-Rymi-Signatureusing a timing-safe comparison.
Example (Node.js)
javascript
const crypto = require('crypto');
function verifyWebhook(rawBody, signature, timestamp, secret) {
const payload = `${timestamp}.${rawBody}`;
const expected = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
throw new Error('Invalid webhook signature');
}
}Or use the built-in verifier in @rymi/node:
typescript
rymi.webhooks.verifySignature(rawBody, signature, timestamp, secret);TIP
HTTP header names are case-insensitive. Frameworks commonly expose these as lowercase keys: x-rymi-signature, x-rymi-timestamp, x-rymi-event.
URL Safety Rules
- Production webhook URLs must use
https://. - Localhost, link-local, and private network destinations are rejected.
- URLs with embedded username/password credentials are rejected.

