Overview
Brilo sends an HTTPSPOST to your endpoint after every call your agent handles. The body is a single JSON object containing everything you need to record the call in your CRM: who called whom, what was said, how long it lasted, the agent’s summary, and any structured data your agent extracted during the conversation.
One call produces one delivery per webhook target. If delivery fails, Brilo retries automatically (see Delivery and retries).
This page describes payload version 1.1. The
payload_version field at the top of the body tells you which version you’re parsing — when we add fields in a future version, older receivers keep working.Configuring a webhook
Each agent can have its own webhook URL. Add it from the agent builder:- Open your agent in the Brilo dashboard.
- Go to the Actions section of the agent configuration.
- Add a Webhook action and paste your endpoint URL.
- Optionally add custom request headers (e.g. an
Authorizationheader with a shared secret) — Brilo passes these through on every delivery. - Save the agent.
POST per call.
When the webhook fires
Brilo sends the webhook after the call ends and after the agent’s post-call analysis has completed (summary generation, success-check evaluation, and structured-data extraction). This means transcripts and analysis fields are populated before delivery starts. The webhook fires for calls in any terminal status — includingcompleted, failed, and blocked. For calls that ended before any conversation took place, the transcript and summary will be sparse but the envelope is otherwise identical.
Request basics
Body schema
The top level of the body is a flat JSON object with up to 16 fields. The next section gives a complete realistic example; the table below documents each field individually.Example payload
A complete payload from a successful outbound sales call. Your endpoint will receive a body of exactly this shape.Possible values and sub-shapes
direction
call_status
The status of the call at the moment the webhook fires. The webhook is only sent for terminal states.
Non-terminal statuses (
scheduled, not-scheduled, paused, queued, dialing) exist in the call lifecycle but never appear in webhook bodies — Brilo waits for the call to reach a terminal state before delivering.
TranscriptEntry
Each item intranscript_object is an object with at least role and content. Additional keys may be present depending on the LLM provider — these are forwarded as-is and are safe to ignore.
The flattened
transcript string at the top of the body excludes function_call and function_result entries (so it reads like a clean human conversation). The transcript_object array includes them. If you want a complete audit trail, use transcript_object.Contact
A row from your Brilo workspace’scontacts table, matched on the phone number on the other side of the call (the contact’s number, not your Brilo number).
If multiple contacts share the same phone in the workspace, all matches are included. If no contact matches,
contacts is [].
Custom fields
contacts[].custom_fields echoes back, verbatim, whatever key/values were stored on the contact — the columns from a CSV import, or the fields set when the contact was added to a campaign. This is where a CRM record ID lives when it belongs to the contact rather than to a single call.
Variables
Round-trips the per-callvariables object you supplied when placing the call via POST /v1/call. Brilo persists the values on the call record and echoes them back here so you can correlate the webhook event with the originating record in your CRM — typically a Salesforce lead_id, a HubSpot record ID, or your own customer ID.
PersonalizedCallSummary
Output of a Summary action (metadata.type = "call_summary") configured on the agent. Use this when you want a domain-specific summary (e.g. “Sales Recap”, “Support Triage Notes”) in addition to the generic summary field.
The whole object is
null if no Summary action is configured on the agent or if it failed to run.
CallTag
Output of a Success Check action (metadata.type = "success_check") — a yes/no criterion the agent evaluates against the conversation. Only matched success checks appear in call_tags.
ExtractedField
Output of an Extract Data action (metadata.type = "extract_data") — a value the agent pulls out of the conversation (appointment date, budget, lead score, etc.). One entry per configured extractor on the agent, regardless of whether extraction found a value.
Receiving the webhook
A minimal receiver that parses the body, acknowledges quickly, and queues heavy work for after the response is sent.Delivery and retries
Brilo delivers webhooks through a Temporal workflow with strict retry semantics.
If all 5 attempts fail, the delivery is abandoned. Brilo does not currently expose a UI for listing failed deliveries — if you need to recover missed calls, you can fetch them by ID using the Get Call endpoint.
Edge cases & FAQ
Why is `contacts` empty?
Why is `contacts` empty?
Brilo looks up contacts by the phone number on the other side of the call (the caller for inbound, the dialled number for outbound) within the same workspace as the call. If no contact in your workspace matches that number,
contacts is []. Add the contact in Brilo and future calls will populate the array.Why does `summary` say 'No summary available'?
Why does `summary` say 'No summary available'?
This fallback is used when the agent’s post-call summary generation produced no usable output — usually because the call ended before any meaningful conversation took place, or because the LLM returned an empty response. The webhook is still delivered with the rest of the data.
Why does `transcript` say 'No transcript available'?
Why does `transcript` say 'No transcript available'?
The call ended before any utterance was captured (e.g. the recipient hung up before speaking, or the call failed to connect). The
transcript_object array will also be empty in this case.Can I receive multiple webhooks for the same call?
Can I receive multiple webhooks for the same call?
Yes, if you’ve configured multiple webhook actions on the same agent. Each webhook action receives its own delivery for every call. Brilo’s internal idempotency prevents duplicate deliveries to the same target — but two different targets will both fire.
What happens if my endpoint is slow?
What happens if my endpoint is slow?
Brilo waits up to 10 seconds for a response. Slower responses are treated as a timeout and trigger a retry. Make your endpoint respond fast (acknowledge first, process async).
What if my endpoint returns a 4xx?
What if my endpoint returns a 4xx?
Brilo treats any non-
2xx as a retryable failure and will attempt delivery up to 5 times total (the first attempt plus up to 4 retries). If your endpoint sees an “invalid” payload it can’t process, log it and return 200 to stop the retries.Will the schema change?
Will the schema change?
We add fields additively — your receiver should ignore fields it doesn’t know about. When the schema gains new fields,
payload_version will increment (e.g. "1.2"). We won’t remove fields or change types within a major version. See the Changelog.How do I correlate the webhook back to my CRM record (Salesforce, HubSpot, etc.)?
How do I correlate the webhook back to my CRM record (Salesforce, HubSpot, etc.)?
Pass your CRM record ID as a The same
variables entry when placing the call:variables object is echoed back at the top level of the post-call webhook body, so your receiver can look up the originating record without first having to join through call_id. The field is omitted entirely when no variables were sent — guard with body.variables?.lead_id (JS) or (body.get("variables") or {}).get("lead_id") (Python). See Variables for the full contract.How do I test my webhook receiver without making real calls?
How do I test my webhook receiver without making real calls?
Place a real test call to your agent. Brilo doesn’t currently send synthetic test deliveries — every webhook corresponds to a real call. We recommend keeping a dedicated test agent with a separate webhook URL pointing at a staging endpoint.
Changelog
v1.1 (additive update — variables echo)
Adds an additive top-level field for CRM tie-back:
variables— echo of the per-callvariablesobject supplied onPOST /v1/call. Omitted from the payload when no variables were sent on the originating call, so receivers pinned to the earlier v1.1 shape keep working unchanged.
v1.1
Adds three top-level analysis sections derived from the agent’s post-call actions:personalized_call_summary— output of any Summary action configured on the agentcall_tags— matched Success Check outcomesextracted_fields— Extract Data values
payload_version field at the top of the body. Delivery now uses a Temporal-backed retry workflow: 5 attempts with exponential backoff, per-target idempotency.
v1.0
Original payload:call_id, direction, call_status, transcript, transcript_object, duration, call_from, call_to, agent_id, summary, contacts. Single-attempt, fire-and-forget delivery.