Webhooks
Events & payloads
The exact requests Daffy Voice sends to your server: the call.result event from a workflow's Send results step, and the end-of-call and prefetch webhooks set on an AI employee.
call.result
Sent by a workflow's Send results step, once per call attempt that the step is set to report. See Send results to your tools for setup.
Headers
POST /ubiq/results HTTP/1.1
Content-Type: application/json
User-Agent: UBIQ-Workflows/1
X-Ubiq-Signature: t=1791625380,v1=5f1c3e0a9b7d2c4e6f8a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1eX-Ubiq-Signature is present only when the step has a signing secret. See Verifying signatures.
Example: answered call
{
"event": "call.result",
"workflow": { "id": "8d0f6a52-3c1e-4b7a-9e21-5f4c3b2a1d0e", "name": "October EMI reminders" },
"run_id": "c2b9e7a1-0f3d-4c6b-8a5e-7d1f2e3c4b5a",
"call_id": "4a7e1c9b-2d3f-4e5a-8b6c-0d1e2f3a4b5c",
"called_at": "2026-10-10T09:42:17.512340+00:00",
"attempt": 1,
"contact": {
"name": "Rahul Sharma",
"phone": "+919876543210",
"loan_id": "LN-20417",
"amount_due": "4500"
},
"result": {
"answered": true,
"outcome": "Promised to pay",
"outcome_key": "promise_to_pay",
"call_status": "completed",
"note": "Salary is delayed this month; will pay on the 15th.",
"objection": "salary delayed",
"promise_date": "2026-10-15",
"promise_amount": 4500,
"callback_at": null,
"details": { "Preferred payment method": "UPI" },
"summary": "Rahul confirmed the EMI and promised to pay ₹4,500 by 15 October via UPI.",
"duration_sec": 74
},
"row": {
"Called at": "2026-10-10T09:42:17.512340+00:00",
"Name": "Rahul Sharma",
"Phone": "+919876543210",
"Answered": "Yes",
"Outcome": "Promised to pay",
"Note": "Salary is delayed this month; will pay on the 15th.",
"Promise date": "2026-10-15",
"Promise amount": 4500,
"Callback at": "",
"Preferred payment method": "UPI",
"Duration (sec)": 74,
"Workflow": "October EMI reminders"
}
}Example: no answer
{
"event": "call.result",
"workflow": { "id": "8d0f6a52-3c1e-4b7a-9e21-5f4c3b2a1d0e", "name": "October EMI reminders" },
"run_id": "c2b9e7a1-0f3d-4c6b-8a5e-7d1f2e3c4b5a",
"call_id": "9b3c5d7e-1f2a-4b6c-8d0e-2f4a6b8c0d1e",
"called_at": "2026-10-10T09:44:03.118201+00:00",
"attempt": 2,
"contact": { "name": "Priya Nair", "phone": "+919812345678" },
"result": {
"answered": false,
"outcome": null,
"outcome_key": null,
"call_status": "no_answer",
"note": null,
"objection": null,
"promise_date": null,
"promise_amount": null,
"callback_at": null,
"details": {},
"summary": null,
"duration_sec": null
},
"row": {
"Called at": "2026-10-10T09:44:03.118201+00:00",
"Name": "Priya Nair",
"Phone": "+919812345678",
"Answered": "No",
"Outcome": "no_answer",
"Note": "",
"Promise date": "",
"Promise amount": "",
"Callback at": "",
"Duration (sec)": "",
"Workflow": "October EMI reminders"
}
}Top-level fields
eventstringcall.result.workflowobjectid and name.run_idstringtest for a test delivery.call_idstring | nullnull when no call was placed, and for a test delivery.called_atstring | nullattemptinteger1 for the first call, 2 for the first retry, and so on. It is not a delivery retry counter.contactobject_ are removed), plus name and phone.resultobjectrowobjecttestbooleantrue only on deliveries from Send a test.result
answeredbooleanoutcomestring | nullPromised to pay. null when the call wasn't classified (usually unanswered calls).outcome_keystring | nullpromise_to_pay. Use this in code; labels can be renamed.call_statusstring | nullcompleted, no_answer, busy, voicemail, failed. If a workflow action already re-queued the contact, it can read requeued.notestring | nullobjectionstring | nulltoo expensive.promise_datestring | nullYYYY-MM-DD.promise_amountnumber | nullcallback_atstring | nullYYYY-MM-DDTHH:MM in the workflow's local time.detailsobjectsummarystring | nullduration_secinteger | nullrow
columnsobjectCalled at, Name, Phone, Answered (Yes/No), Outcome (the label, or the call status when there is none), Note, Promise date, Promise amount, Callback at, one column per captured detail, Duration (sec), Workflow. Missing values are empty strings.Timing
call.result per attempt the step reports.End-of-call
Sent by an AI employee with an End-of-Call Webhook URL, after every call it handles — inbound, outbound, workflow and website widget calls alike.
- Method
POST, headerContent-Type: application/json. - No signature header and no event name in the body.
{
"call_id": "4a7e1c9b-2d3f-4e5a-8b6c-0d1e2f3a4b5c",
"duration_sec": 74.3,
"transcript": [
{ "role": "agent", "text": "Hi Rahul, this is Riya from Acme Finance. Is this a good time?" },
{ "role": "user", "text": "Yes, go ahead." },
{ "role": "agent", "text": "Your EMI of ₹4,500 was due on the 5th. When can you pay it?" },
{ "role": "user", "text": "By the 15th, my salary is late." }
],
"outcome": "neutral"
}Fields
call_idstringduration_secnumbertranscriptobject[]role — user (the caller) or agent (your AI employee) — and text.outcomestringvoicemail when the call reached voicemail; otherwise the caller's overall sentiment: positive, neutral or negative. This is not the workflow outcome — use call.result for that.Prefetch data
Sent by an AI employee with a Prefetch Data Webhook URL, before each conversation starts.
GET https://crm.example.com/ubiq/prefetch?caller_id=%2B919876543210Query parameters
caller_idstringunknown when it isn't available, or web for website widget calls.Respond with 2xx and any JSON object. Its contents are added to the AI employee's instructions for that call. Details and limits are in the overview.