Guide
Get call results
After every call Voximate writes a summary, an outcome, the caller’s sentiment and action items. Here is how to get them — and the transcript — into your own system.
What you get
A finished call carries an analysis object a few seconds after it ends. Until then — and on every item in a list — analysis is null.
GET /calls/{call_id} · 200
{
"object": "call",
"id": "3c27f11c-8a1e-4d55-9b0e-1f4c2a7d9e10",
"agent_id": "7c3f1e90-5a44-4b21-9c07-2ed5b8f41a66",
"direction": "outbound",
"channel": "phone",
"status": "completed",
"from": "+4721000000",
"to": "+4712345678",
"started_at": "2026-10-12T10:04:12.000Z",
"ended_at": "2026-10-12T10:07:33.000Z",
"duration_seconds": 201,
"end_reason": "caller_hangup",
"metadata": {
"crm_contact_id": "48213"
},
"has_recording": true,
"analysis": {
"summary": "The caller booked a check-up on Tuesday 14 October at 12:00.",
"outcome": "resolved",
"sentiment": "positive",
"action_items": [
"Send booking confirmation by SMS"
],
"created_at": "2026-10-12T10:07:51.000Z"
}
}analysis.summary— what happened, in a few sentences.analysis.outcome— for exampleresolved,escalatedorabandoned.analysis.sentiment—positive,neutralornegative.analysis.action_items— follow-ups the agent promised or the caller asked for.
Webhook or polling
| Webhook (call.completed) | Polling (GET /calls) | |
|---|---|---|
| Speed | Seconds after the analysis is ready | As often as you poll |
| Requests | One per call, sent to you | Many, most of them returning nothing new |
| Needs | A public https endpoint | Nothing but a key |
| Best for | Live integrations and CRMs | Nightly reports and backfills |
Use both: webhooks for the live flow, and a nightly
GET /calls?created_after=… to fill any gap while your endpoint was down.Polling
List the calls since your last run, then fetch each one for its analysis and its transcript. Lists return analysis: null to stay fast.
Calls since yesterday
curl "https://api.voximate.no/public/v1/calls?created_after=2026-10-12T00:00:00Z&limit=100" \
-H "Authorization: Bearer $VOXIMATE_API_KEY"
curl "https://api.voximate.no/public/v1/calls/3c27f11c-8a1e-4d55-9b0e-1f4c2a7d9e10" \
-H "Authorization: Bearer $VOXIMATE_API_KEY"
curl "https://api.voximate.no/public/v1/calls/3c27f11c-8a1e-4d55-9b0e-1f4c2a7d9e10/transcript" \
-H "Authorization: Bearer $VOXIMATE_API_KEY"Example: notes in your CRM
Start each call with your CRM’s contact id in metadata. When call.completed arrives, it is right there on data.object.metadata: write the summary, the outcome and the transcript to that contact. crm.addNote stands in for your CRM’s own API.
server.js
// npm install express
import crypto from 'node:crypto';
import express from 'express';
const app = express();
const API = 'https://api.voximate.no/public/v1';
const KEY = process.env.VOXIMATE_API_KEY;
const SECRET = process.env.VOXIMATE_WEBHOOK_SECRET;
const seen = new Set(); // use your database in production
function verified(req) {
const parts = Object.fromEntries((req.get('Voximate-Signature') ?? '').split(',').map((p) => p.split('=')));
const t = Number(parts.t);
if (!Number.isFinite(t) || Math.abs(Date.now() / 1000 - t) > 300) return false;
const expected = crypto.createHmac('sha256', SECRET).update(`${t}.`).update(req.body).digest();
const given = Buffer.from(parts.v1 ?? '', 'hex');
return given.length === expected.length && crypto.timingSafeEqual(given, expected);
}
app.post('/voximate/webhooks', express.raw({ type: 'application/json' }), (req, res) => {
if (!verified(req)) return res.sendStatus(400);
const event = JSON.parse(req.body.toString('utf8'));
res.sendStatus(200); // acknowledge first
if (seen.has(event.id)) return;
seen.add(event.id);
if (event.type === 'call.completed') void saveToCrm(event.data.object);
});
async function saveToCrm(call) {
const contactId = call.metadata.crm_contact_id; // what you sent with POST /calls
if (!contactId) return;
const res = await fetch(`${API}/calls/${call.id}/transcript`, {
headers: { Authorization: `Bearer ${KEY}` },
});
const transcript = (await res.json()).data
.map((line) => `${line.role === 'agent' ? 'Agent' : 'Caller'}: ${line.text}`)
.join('\n');
await crm.addNote(contactId, {
title: `Call ${call.analysis?.outcome ?? call.status} (${call.duration_seconds ?? 0}s)`,
body: `${call.analysis?.summary ?? ''}\n\n${transcript}`,
tasks: call.analysis?.action_items ?? [],
});
}
app.listen(3000);Read more about signatures, retries and duplicates on Webhooks.