View as Markdownllms.txt

Webhooks

Receive a server-to-server notification with the full call result when an in-app agent call ends.

Overview

When an in-app agent call finishes, RevRag can send a call.ended webhook — a POST request to an HTTPS endpoint on your server — containing the complete result of that call: status, duration, summary, transcript, recording URL, and your app_user_id.

The payload below shows exactly what your server receives when an in-app call ends.

Webhooks are server-to-server. They are delivered to your backend, not to the SDK running in your app. The in-app SDK's own agent lifecycle events (e.g. agent_start / agent_end) are a separate, in-process mechanism — they are not the same as this webhook.

Enabling webhooks

Webhooks are opt-in and configured per agent. They are not enabled by default.

To turn them on for your in-app agent, contact contact@revrag.ai with:

  • Webhook URL — the HTTPS endpoint that will receive the POST (HTTP is not supported).
  • Signing secret — a shared secret used to verify each request (see Security & headers).

Security & headers

In-app webhooks use the same signing scheme as every RevRag webhook — HMAC-SHA256 computed over "{timestamp}.{raw_body}" with your shared secret. Each request carries:

HeaderPurpose
X-Webhook-EventEvent type (call.ended)
X-Webhook-TimestampUnix timestamp (seconds) the webhook was sent
X-Webhook-Signaturet=<timestamp>,v1=<hmac_sha256_hex>
X-Webhook-IDUnique id for deduplication

Always verify the signature before trusting a payload. See Webhook Security for the full verification steps and ready-to-use Python / Node.js examples.

Payload

The webhook body is the same JSON shape as the Get Call Status response. For an in-app call it carries your app_user_id as a top-level correlation field.

{
  "call_status": "ENDED",
  "call_id": "123e4567-e89b-12d3-a456-426614174000",
  "agent_id": "33333333-3333-3333-3333-333333333333",
  "summary": "User asked about their loan eligibility and was guided to the application.",
  "start_time": "2026-05-27T03:05:00+00:00",
  "end_time": "2026-05-27T03:06:12+00:00",
  "duration": 72,
  "disconnection_reason": "user_hangup",
  "recording_url": "https://.../recording.mp3",
  "transcription": {
    "messages": [
      { "role": "assistant", "content": "Hi! How can I help you today?" },
      { "role": "user", "content": "What documents do I need for a loan?" }
    ]
  },
  "custom_variables": [
    { "key": "interested_in_loan", "type": "boolean", "value": true }
  ],
  "variables_fields": { "plan": "gold" },
  "app_user_id": "user_12345"
}

Payload parameters

FieldTypeDescription
call_statusstringFinal call status — ENDED once the call has finished
call_idstring (uuid)The call this notification is for
agent_idstring (uuid)The in-app agent that handled the call
summarystringLLM-generated call summary (when a transcript exists)
start_time / end_timestring | nullISO-8601 timestamps
durationnumberCall duration in seconds
disconnection_reasonstring | nullNormalized hangup reason
recording_urlstring | nullPresigned recording URL (for transcribed calls)
transcriptionobject | nullTurn-by-turn messages (role, content)
custom_variablesarrayPost-call variables extracted by the agent (key, type, value)
variables_fieldsobject | nullVariables associated with the call
app_user_idstring | nullYour user id, echoed back top-level — see Correlation IDs

Retries & failure recovery

If your endpoint is temporarily unreachable, RevRag retries automatically with exponential backoff. If every attempt fails, the delivery is retained as a dead-letter so you can recover it later via the polling API below.

Retry policy

Each webhook goes through up to 3 attempts in total:

AttemptTiming
1Fires immediately after the call ends
2Retries 5 minutes after attempt 1 fails
3Retries 15 minutes after attempt 2 fails
If attempt 3 fails, the delivery is moved to dead-letter

Any non-2xx response — including network errors, timeouts, and 4xx — is treated as a failure and triggers a retry.

Every retry carries the same X-Webhook-ID as the original attempt. Dedupe on this header so a delivered-but-ack-lost webhook doesn't get processed twice.

Discovering failed deliveries

Poll this endpoint at a regular cadence (for example, every 5 minutes) to discover any calls whose webhook could not be delivered. Only dead-letter deliveries are returned — successful and in-progress deliveries are not included.

GET /webhook-failures

Header Parameters

ParameterTypeRequiredDescription
X-API-KeystringYesAPI Key for authentication

Query Parameters

ParameterTypeRequiredDescription
sincestring (ISO 8601)YesStart of the window (exclusive). On subsequent polls, pass your previous request's until value here.
untilstring (ISO 8601)NoEnd of the window (inclusive). Defaults to the current time.
limitintegerNoRows per page. Default 100, maximum 500.
cursorstringNoOpaque pagination cursor. Pass unchanged from the previous response's next_cursor to fetch the next page.

Response

{
  "items": [
    {
      "call_id": "123e4567-e89b-12d3-a456-426614174000",
      "failed_at": "2026-05-27T03:20:00.000Z",
      "last_status_code": 500,
      "attempts": 3
    }
  ],
  "next_cursor": null
}
FieldTypeDescription
items[].call_idstring (uuid)Identifier of the call whose webhook failed. Use this to fetch the full payload (see below).
items[].failed_atstring (ISO 8601)When the delivery entered the failed state, after all retries were exhausted.
items[].last_status_codeinteger | nullHTTP status returned by your endpoint on the final attempt. null if every attempt failed at the network layer (timeout, connection refused, DNS failure).
items[].attemptsintegerTotal delivery attempts made before giving up.
next_cursorstring | nullPass this value as cursor on the next request to fetch more results within the same window. null when the window is fully drained.

Example

curl -X GET "https://api.revrag.ai/webhook-failures?since=2026-05-27T00:00:00Z&limit=100" \
  -H "X-API-Key: YOUR_API_KEY"

Fetching the missed payload

For each call_id returned above, fetch the full call payload — the same JSON shape as the webhook body — from the Get Call Status endpoint:

curl -X GET "https://api.revrag.ai/v1/campaigns/trigger/status/{call_id}" \
  -H "X-API-Key: YOUR_API_KEY"
  1. Persist the timestamp of your last successful poll (last_poll_at).
  2. Every few minutes, call GET /webhook-failures?since=<last_poll_at>&until=<now>.
  3. For each call_id in the response, call GET /v1/campaigns/trigger/status/{call_id} and process the payload the same way your webhook handler would.
  4. If next_cursor is present, paginate through by re-calling with &cursor=<next_cursor> until next_cursor is null.
  5. Set last_poll_at = <now> for the next poll.

This guarantees no call is lost even if your endpoint is briefly unreachable.

Correlation IDs

To tie a webhook back to the right user in your own system, use app_user_id together with call_id.

The app_user_id you pass when requesting an in-app call token is echoed back top-level in every webhook, so you can attribute the call to the exact user who initiated it.

In-app events vs webhooks

Don't confuse these two mechanisms:

In-app SDK eventsWebhooks (this page)
WhereIn your app process (browser / mobile)Server-to-server, to your backend
WhenLive, during the call (agent_start, agent_end, …)After the call ends (call.ended)
Use forDriving UI, real-time statePersisting the call result, transcript, summary, analytics

For the in-process events, see your platform's integration guide.