Integrations
Send results to your tools
Add a Send results step to a workflow and Daffy Voice posts the result of every call — outcome, notes, promised dates and the details your AI employee captured — to your own server or straight into a Google Sheet.
What gets sent
After each call in a workflow, Daffy Voice sends one JSON POST with the event call.result. Answered calls are sent once the transcript is ready and the call has been summarised and classified, so expect a short delay after hang-up. Calls that didn't connect (no answer, busy, voicemail, failed) are sent straight away.
Each payload has two views of the same result: structured fields (contact, result) for code, and a flat row of column → value for spreadsheets and "add row" automations. The full schema is in Events & payloads.
Add a Send results step
Open the workflow and add Send results
In the workflow builder, add a Send results action and click it to open its settings.
Choose where to send
Send to: Webhook for your own server, Zapier, Make, n8n or a CRM that accepts webhooks; or Google Sheet to add one row per call to a spreadsheet (see below).
Choose which calls
- Every call — a result for every contact the workflow calls, whatever happened.
- Only connected branches — only when the call's outcome matches an outcome branch that is wired to this step. An unwired step sends nothing.
Add a signing secret (recommended)
For webhooks, enter any secret text in Signing secret. Each request then carries an
X-Ubiq-Signatureheader you can check, so you know it came from Daffy Voice. See Verifying signatures.Send a test
Press Send a test. Daffy Voice posts a sample result (with
"test": trueadded) to your URL and tells you whether it was accepted. Use it to map fields in your tool before real calls start.
Allowed URLs
http:// or https:// and must point to a public address. Private, loopback and other internal network addresses (for example localhost, 10.x.x.x, 192.168.x.x) are refused. To test locally, expose your server with a tunnel and use its public HTTPS URL.Send to a Google Sheet
The sheet receives the flat row from each result. A small Apps Script inside your sheet accepts the request, adds any column it hasn't seen before to the header row of the first tab, and appends the result there.
Open Apps Script
In your Google Sheet, open Extensions → Apps Script.
Paste the script
Replace the contents of the editor with this script and save. The same script is shown in the step's settings.
Code.gsfunction doPost(e) { var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0]; var row = JSON.parse(e.postData.contents).row || {}; var lastCol = sheet.getLastColumn(); var headers = lastCol ? sheet.getRange(1, 1, 1, lastCol).getValues()[0] : []; Object.keys(row).forEach(function (k) { if (headers.indexOf(k) < 0) { headers.push(k); sheet.getRange(1, headers.length).setValue(k); } }); sheet.appendRow(headers.map(function (h) { return row[h] === undefined ? "" : row[h]; })); return ContentService.createTextOutput("ok"); }Deploy it as a web app
Deploy → New deployment, choose Web app, set Who has access to Anyone, and deploy. Approve the permissions Google asks for.
Paste the Web app URL
Copy the Web app URL (it ends in
/exec) into the Send results step, then press Send a test. A row should appear in the sheet.
Anyone with the URL can add rows
Receive results on your server
Return any 2xx status within 10 seconds to confirm delivery. Do slow work (CRM updates, emails) after you respond. Failed deliveries are retried twice — see Errors & retries.
Node.js (Express)
import express from "express";
const app = express();
app.post("/ubiq/results", express.json(), (req, res) => {
const { event, contact, result } = req.body;
if (event !== "call.result") return res.sendStatus(204);
// e.g. update the lead in your CRM
console.log(contact.phone, result.answered ? result.outcome : result.call_status);
res.sendStatus(200); // answer quickly — anything other than 2xx counts as a failure
});
app.listen(3000);Python (Flask)
from flask import Flask, request
app = Flask(__name__)
@app.post("/ubiq/results")
def ubiq_results():
event = request.get_json()
if event.get("event") != "call.result":
return "", 204
contact, result = event["contact"], event["result"]
print(contact["phone"], result["outcome"] if result["answered"] else result["call_status"])
return "", 200These minimal examples don't check the signature. In production, verify X-Ubiq-Signature against the raw body before trusting the data — the security page has complete Node and Python code.
Useful fields
Most-used fields
result.answeredbooleanresult.outcomestring | nullPromised to pay. null if the call wasn't classified.result.call_statusstring | nullno_answer, busy, voicemail.result.detailsobjectcontactobjectrowobject