Call Results API
Retrieve call outcomes, transcripts, and recordings programmatically. Set up webhooks to get notified the moment a call ends. No more SSH into your server to parse transcript files.
Authentication
All read endpoints require a Bearer API key with the voice:read scope. Webhook configuration (PUT) requires the voice:call scope. Create keys in your voice portal API keys page.
Authorization: Bearer mbn_live_...The voice:read scope gives read-only access to call data and recordings. It cannot place calls. To place calls AND read results, your key needs both voice:call and voice:read scopes.
Retrieve a single call
Get the full details of a completed call by its ID. The call ID is the callControlId returned when you placed the call, or found in the call log.
GET https://api.talktomyagent.io/v1/voice/calls/{callId}
Authorization: Bearer mbn_live_...Response
{
"data": {
"id": "v3:abc123...",
"direction": "outbound",
"status": "completed",
"endedReason": "callerHangup",
"callerNumber": "+16473705600",
"calledNumber": "+16475551234",
"startedAt": "2026-05-25T14:30:00.000Z",
"endedAt": "2026-05-25T14:35:12.000Z",
"durationSeconds": 312,
"recordingAvailable": true,
"hasTranscript": true,
"transcript": "[Agent] Hi, this is Sol... [Caller] Yeah...",
"task": {
"goal": "Collect $154.56 from Bobby",
"script": "debt-collection",
"facts": { "debtor_name": "Bobby", "amount_due": "$154.56" },
"escalation": null
},
"usage": {
"turnCount": 8,
"inputTokens": 4521,
"outputTokens": 2130
},
"quality": {
"grade": "A",
"p50ResponseLatencyMs": 340,
"p95ResponseLatencyMs": 890
}
}
}The transcript field contains the full conversation text. For inbound calls, task is null. For calls made before this feature launched, endedReason and transcript may be null.
Transcripts are only available via the API when the Ship Transcripts toggle is enabled in your voice portal settings. When disabled, transcripts are still saved as text files on your server but are not sent to the cloud. hasTranscript will be false and transcript will be null for calls made while shipping was off. Recordings are controlled independently by the Call Recording toggle.
Call end reasons
The endedReason field tells you exactly why a call ended. Use it to route post-call workflows, flag failed calls, or track agent performance.
| Reason | Meaning |
|---|---|
callerHangup | The other party hung up |
botHangup | Your agent ended the call (said goodbye) |
callTimeout | Maximum call duration reached |
silenceTimeout | Extended silence on the line |
idleTimeout | No activity detected |
gatewayShutdown | Voice gateway restarted |
aiConnectionFailed | Could not connect to AI backend |
aiSessionLost | AI session dropped and could not reconnect |
mediaStreamDropped | Phone audio stream was interrupted |
setupFailed | Call could not be set up (network/dial error) |
unknown | Reason not available |
List calls
Retrieve your call history with cursor-based pagination. Filter by direction, end reason, or date range.
GET https://api.talktomyagent.io/v1/voice/calls?limit=25
Authorization: Bearer mbn_live_...Query parameters
| Param | Description |
|---|---|
limit | Results per page (1-100, default 25) |
cursor | Opaque cursor from a previous response |
direction | Filter: "inbound" or "outbound" |
endedReason | Filter by end reason (e.g. "callerHangup") |
after | Only calls after this ISO 8601 timestamp |
before | Only calls before this ISO 8601 timestamp |
Only one of direction or endedReason can be used per request. The transcript field is omitted from list responses to keep them compact. Use the single-call endpoint to get the full transcript.
Pagination
The response includes a pagination object. When hasMore is true, pass the cursor value to the next request:
GET /v1/voice/calls?limit=25&cursor=eyJtcyI6MTcx...Download recordings
Get a short-lived signed URL to download the call recording as an MP3 file. The URL expires in 5 minutes.
GET https://api.talktomyagent.io/v1/voice/calls/{callId}/recording
Authorization: Bearer mbn_live_...Response
{
"data": {
"url": "https://storage.googleapis.com/...",
"expiresAt": "2026-05-25T14:40:00.000Z"
}
}Returns 404 if no recording is available. The recordingAvailable field on the call object tells you in advance whether a recording exists.
Webhooks
Instead of polling, configure a webhook URL to receive a POST request automatically every time a call ends. You get the full call data, including the transcript and task results, the moment they are ready.
Configure your webhook
Use your agent's phone number (from) to identify which deployment to configure:
PUT https://api.talktomyagent.io/v1/voice/webhooks
Authorization: Bearer mbn_live_...
Content-Type: application/json
{
"from": "+16473705600",
"url": "https://your-server.com/webhooks/ttma",
"secret": "your-signing-secret-at-least-32-characters-long",
"enabled": true
}Your webhook URL must use HTTPS and must not point to private IP ranges or localhost. The signing secret is stored securely and never returned in full (only the last 4 characters are shown).
Webhook payload
When a call ends, we POST the following JSON to your URL:
{
"event": "call.ended",
"deliveryId": "a1b2c3d4-...",
"timestamp": "2026-05-25T14:35:12.000Z",
"data": {
"id": "v3:abc123...",
"direction": "outbound",
"status": "completed",
"endedReason": "botHangup",
"callerNumber": "+16473705600",
"calledNumber": "+16475551234",
"startedAt": "2026-05-25T14:30:00.000Z",
"endedAt": "2026-05-25T14:35:12.000Z",
"durationSeconds": 312,
"recordingAvailable": true,
"hasTranscript": true,
"transcript": "[Agent] Hi, this is Sol...",
"task": { "goal": "...", "facts": { ... } },
"usage": { "turnCount": 8 },
"quality": { "grade": "A" }
}
}The data object is the same shape as the single-call GET response. Headers included with every delivery:
| Header | Description |
|---|---|
X-TTMA-Event | Event type (always "call.ended") |
X-TTMA-Delivery-Id | Unique delivery ID for deduplication |
X-TTMA-Signature | HMAC-SHA256 signature for verification |
Webhook payloads may include task.facts which can contain PII (names, payment amounts, phone numbers). Use HTTPS endpoints, validate the HMAC signature, and handle the payload in a secure environment.
Verify the signature
Every webhook delivery is signed with your secret using HMAC-SHA256. Always verify the signature before processing the payload:
// Node.js verification example
const crypto = require("crypto");
function verifyWebhook(rawBody, signatureHeader, secret) {
// Parse the header: "t=1716648912,v1=abc123..."
const parts = {};
signatureHeader.split(",").forEach(p => {
const [k, v] = p.split("=");
parts[k] = v;
});
// Reject old deliveries (replay defense)
const age = Math.abs(Date.now() / 1000 - Number(parts.t));
if (age > 300) return false; // 5-minute window
// Compute expected signature
const expected = crypto
.createHmac("sha256", secret)
.update(parts.t + "." + rawBody)
.digest("hex");
// Constant-time comparison
return crypto.timingSafeEqual(
Buffer.from(expected, "hex"),
Buffer.from(parts.v1, "hex")
);
}Delivery behavior
Webhooks wait for the recording to be ready before delivering (up to 60 seconds). If a recording doesn't arrive in time, the webhook fires anyway with recordingAvailable: false. Failed deliveries are retried up to 5 times. If your endpoint returns 410 Gone, the webhook is automatically disabled.
Using with n8n and automation tools
The webhook is standard JSON over HTTPS, making it easy to connect to any automation platform. For n8n:
1. Create a Webhook node with your URL
2. Configure your TTMA webhook to point at the n8n webhook URL
3. The payload arrives as structured JSON. No regex parsing needed. Access fields directly: {{ $json.data.endedReason }}, {{ $json.data.task.facts.debtor_name }}, etc.
This replaces the previous approach of SSHing into the server and parsing transcript text files. All the same data is available in structured JSON, delivered automatically.
Full API reference
For the complete endpoint specification including all fields, error codes, and rate limits, see the interactive API reference.