Webhooks
Errors & retries
What counts as a successful delivery, how long Daffy Voice waits for your server, and when it tries again. Each webhook type behaves differently.
Summary
Per webhook
Send resultscall.resultEnd-of-callPOSTPrefetch dataGETWhat counts as delivered
Any HTTP status from 200 to 299. The response body is ignored (except for prefetch, where it must be JSON). Anything else — another status, a timeout, a refused connection, a TLS error — is a failed attempt.
Respond first, work later
200 as soon as you have stored the request, then update your CRM, send emails and so on in the background. A handler that takes longer than 10 seconds counts as failed and is sent again, even if it later succeeds.Send results (call.result)
Your response → what Daffy Voice does
2xxdelivered408, 429retriedother 4xxnot retried400, 401 (bad signature), 404. Fix the problem on your side; that result is not sent again.5xxretriedtimeout / network errorretried3xxfollowed/exec URLs work.- The wait between attempts starts when the previous attempt ends. In the worst case all three attempts finish within about 50 seconds.
- All attempts carry the same body and the same
X-Ubiq-Signature(same timestamp). - If the URL is not allowed (not
http/https, or a private or internal address), nothing is sent and there is no retry. Send a test shows this error when you set up the step. - If one step sends to several URLs, each URL is delivered and retried on its own.
End-of-call
- Up to 4 attempts, with waits of 1, 3 and 10 seconds between them.
- Every failure is retried, including
4xx— so a401from your server is tried four times. - Redirects are not followed: a
301or302counts as a failure. Use the final URL, includinghttps://and any trailing slash your server expects.
Prefetch data
- One attempt, 5-second timeout. The caller hears nothing until Daffy Voice gets your answer or gives up, so keep it fast.
- On a timeout, a non-2xx status or a body that isn't valid JSON, the call simply starts without the extra context.
After the last attempt
Daffy Voice does not queue failed deliveries for later. If your server was down for longer than the retry window, those results are not sent again automatically. The calls themselves are not affected: outcomes, notes and transcripts are still in Daffy Voice. Recover them from Call logs (Download CSV).
Handle duplicates
A delivery can arrive twice — for example when your server saved the data but answered after the 10-second timeout. Daffy Voice sends no delivery ID, so build your own key. For call.result, run_id + contact phone + attempt identifies one call attempt. (call_id also works when it isn't null.) For end-of-call, use call_id.
Example (Node.js + PostgreSQL)
// Store a key per result and ignore repeats.
const key = `${event.run_id}:${event.contact.phone}:${event.attempt}`;
const isNew = await db.query(
"INSERT INTO ubiq_results (key, payload) VALUES ($1, $2) ON CONFLICT (key) DO NOTHING",
[key, event],
).then((r) => r.rowCount === 1);
if (isNew) await updateCrm(event);
res.sendStatus(200);Checklist when deliveries fail
- Is the URL public and reachable over HTTPS from the internet (not behind a VPN or firewall allow-list)?
- Does it answer within 10 seconds (5 for prefetch)?
- Is the signature check reading the raw body? See Verifying signatures.
- For end-of-call and prefetch: does the URL redirect? Use the final address.
- Press Send a test on the Send results step — it shows whether your server accepted the request.