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.result
Timeout 10 s per attempt. Up to 3 attempts: right away, then 2 s and 8 s after a failure. 4xx errors other than 408 and 429 are not retried. Redirects are followed.
End-of-callPOST
Timeout 10 s per attempt. Up to 4 attempts: right away, then 1 s, 3 s and 10 s after a failure. Every non-2xx status is retried, including 4xx. Redirects are not followed.
Prefetch dataGET
Timeout 5 s. One attempt, no retries. On failure the call goes ahead without caller context. Redirects are not followed.

What 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

Return 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

2xxdelivered
Done. No more attempts.
408, 429retried
Treated as temporary.
other 4xxnot retried
Treated as a permanent rejection — for example 400, 401 (bad signature), 404. Fix the problem on your side; that result is not sent again.
5xxretried
Treated as temporary.
timeout / network errorretried
No response within 10 seconds, DNS failure, connection refused or reset.
3xxfollowed
Redirects are followed to the final URL, so Google Apps Script /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 a 401 from your server is tried four times.
  • Redirects are not followed: a 301 or 302 counts as a failure. Use the final URL, including https:// 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)

handler.js
// 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.