Skip to content

Webhooks

Register webhook endpoints to receive lifecycle and post-call intelligence events from Rymi.

Register Webhook

http
POST /v1/webhooks

Request Body

FieldTypeRequiredDefaultDescription
urlstringYesHTTPS endpoint to receive events
eventsstring[]YesEvents to subscribe to (see Available Events)
secretstringYesHMAC signing secret used to verify deliveries. Min 16 characters recommended

Available Events

EventDescription
call.startedA call has been initiated and the room is active
call.intelligence.readyPost-call intelligence completed — transcript, summary, extraction, and evaluation data is ready
call.completedLegacy alias for post-call completion; still emitted for existing integrations
call.failedA call failed before post-call processing could begin
batch.completedAll 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

StatusMeaning
400Missing or invalid url, events, or secret
401Missing 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

FieldTypeDescription
typestringEvent name (same value as event)
eventstringEvent name
timestampstringISO 8601 timestamp of when the event was emitted
dataobjectEvent-specific payload

Delivery Behavior

  • Timeout: Rymi waits up to 30 seconds for your endpoint to respond with a 2xx status 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 timestamp and call_id for deduplication.
  • Idempotency: Your webhook handler should be idempotent — the same event may be delivered more than once.

Update Webhook

http
PATCH /v1/webhooks/:id

Update an existing webhook's URL, subscribed events, or signing secret. Only provided fields are changed.

Path Parameters

ParameterTypeDescription
idstringWebhook ID

Request Body

All fields are optional. Provide only the fields you want to change.

FieldTypeRequiredDescription
urlstringNoNew HTTPS endpoint URL
eventsstring[]NoUpdated event subscriptions
secretstringNoNew 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

StatusMeaning
400Invalid url (not HTTPS, private network, etc.) or empty body
401Missing or invalid API key
404Webhook not found

List Webhooks

http
GET /v1/webhooks

Query Parameters

ParameterTypeDefaultDescription
limitinteger50Max records to return
offsetinteger0Records 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

StatusMeaning
401Missing or invalid API key

Delete Webhook

http
DELETE /v1/webhooks/:id

Path Parameters

ParameterTypeDescription
idstringWebhook 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

StatusMeaning
401Missing or invalid API key
404Webhook not found

Signature Verification

Every webhook delivery includes three headers for verification:

HeaderDescription
X-Rymi-EventEvent name (e.g., call.intelligence.ready)
X-Rymi-TimestampUnix timestamp (seconds) used in the signature payload
X-Rymi-SignatureHMAC-SHA256 hex digest of {timestamp}.{raw_body}

Verification Steps

  1. Read the raw request body exactly as received (do not parse JSON first).
  2. Concatenate X-Rymi-Timestamp, a literal ., and the raw body string.
  3. Compute an HMAC-SHA256 digest using your webhook secret as the key.
  4. Compare the result to X-Rymi-Signature using 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.