- 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
- Go to app.ryvo.so/developers.
- In the Webhook section, add your public URL (HTTPS required).
- 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.
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_idisnull. When you place the call withPOST /v1/calls, theidthat endpoint returns is thesession_id, so match on that.session_idis included. It is the key you use to match astartedwith itsended, and both with the conversation.transcript_summaryis not sent: in this lane the summary is produced later by the analysis, and it travels inanalysis.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:data, two different identifiers:
call_id: thecall_…handle thatPOST /v1/callsreturned to you. Use it to match the event with the call you triggered.id: the identifier of the conversation in Ryvo. It isnullonconversation.startedbecause 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
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 a2xx, 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 the200 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 answer200 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.