Skip to main content
When your agents finish calls (or fail them), Ryvo sends an event to a URL you configure. Useful for:
  • Updating the deal in your CRM with the outcome of the call.
  • Triggering an automated follow-up if the call failed.
  • Logging the summary of every conversation in your data warehouse.

Set up your webhook

  1. Go to app.ryvo.so/developers.
  2. In the Webhook section, add your public URL (HTTPS required).
  3. Copy the signing secret we generate. It is shown only once: store it in a secret manager. You need it to verify the signature of every event.
The webhook URL has to be public and accept POST requests. If it sits behind a firewall or requires auth, the events will not get through.

Available events

For each occurrence you get exactly one event: the canonical one (conversation.*) if you subscribed to it, or its alias (call.*) if you did not. Never both. You pick the events of each endpoint in Developers → Webhooks, and you can change them at any time. A new endpoint starts with every event checked; the call.* names are there for endpoints already written against them.
Older subscriptions created with the * wildcard receive the call.* family only. That is on purpose: the wildcard means “everything that existed when I subscribed”, not “everything Ryvo invents later”. To receive conversation.* or analysis.ready, check them in the portal.

What changes between lanes

A Builder agent (Ryvo Engine) sends the same events with three differences, all visible in the payload:
  • call_id is null. When you place the call with POST /v1/calls, the id that endpoint returns is the session_id, so match on that.
  • session_id is included. It is the key you use to match a started with its ended, and both with the conversation.
  • transcript_summary is not sent: in this lane the summary is produced later by the analysis, and it travels in analysis.ready.
Engine chat does not emit conversation.* yet: a chat conversation has no end the system can observe (nobody hangs up). It does emit analysis.ready.

Shape of an event

Every event shares this structure:
Inside data, two different identifiers:
  • call_id: the call_… handle that POST /v1/calls returned to you. Use it to match the event with the call you triggered.
  • id: the identifier of the conversation in Ryvo. It is null on conversation.started because at that point the conversation does not exist yet.

conversation.started / call.initiated

conversation.ended / call.completed

status can be completed, failure or unknown depending on the result of the call analysis. The summary never includes the full transcript.

conversation.ended / call.failed (could not be started)

analysis.ready

The keys inside evaluation_results and data_collection are the identifiers you defined in the Builder (Analysis after each conversation).

Headers on every delivery

Automatic retries

If your endpoint answers with something other than a 2xx, or hangs for more than 10 seconds, we retry automatically with exponential backoff: After the 4th failed attempt we mark the delivery as exhausted and stop trying. You can see the history in app.ryvo.so/developers.

Idempotency on the receiving side

Even though we only retry on errors, the same event can reach you twice (say your endpoint processed it fine but the 200 was lost on the network). That is why we send X-Ryvo-Event-Id: store the IDs you have processed and drop the duplicates.

Answer fast

Your endpoint has to answer 200 in under 10 seconds. If you need to do slow work (an LLM call, a sync to an external API), answer 200 right away and process it asynchronously (a queue, a background job).

Next steps

Verify signatures

How to validate that the event comes from Ryvo and not from an attacker.

Test your webhook

From the portal you can send yourself a test event.