Version 1.2.0 • Headless Telephony Reference
Talki Talki provides a bi-directional messaging API. Use the **Outbound API** to send texts and the **Inbound Webhooks** to receive real-time notifications when your numbers receive messages.
Security: All requests are signed and keys are hashed for your protection.
Authentication is handled via a Bearer Token in the Authorization header. Generate keys in the Developer tab of your dashboard.
# Header Format
Authorization: Bearer tt_sk_your_secret_key_here
Send an SMS from a number you own.
| Property | Type | Description |
|---|---|---|
| to | string | Recipient in E.164 (+1555...). |
| from | string | Your Talki Talki number. |
| body | string | Text content. |
To receive messages, provide a URL in your settings. We will POST a JSON object when a text arrives.
# Payload received by your server
{
"event": "sms.received",
"data": {
"from": "+15551234567",
"to": "+15559876543",
"body": "Hello! I saw your ad.",
"mediaUrl": null,
"createdAt": "2026-02-20T14:30:00Z"
}
}Talki Talki signs every webhook request. To ensure authenticity, verify the X-TT-Signature header in incoming requests using your Signing Secret.
// Node.js Verification Example
const crypto = require('crypto');
const hmac = crypto.createHmac('sha256', YOUR_SIGNING_SECRET);
const signature = hmac.update(JSON.stringify(request.body)).digest('hex');
if (signature === request.headers['x-tt-signature']) {
// Request is authentic
}Let your other software react to what happens on your phone line. When the AI Receptionist answers a call, takes a message, texts the caller or finishes a call, Talki Talki can send a signed JSON event to any HTTPS endpoint — a Zapier Catch Hook, Make, n8n, your booking system, your CRM. A small REST API lets apps read past calls and manage endpoints. Everything is off by default: nothing is sent until you add an endpoint. Available on VIP; enabled per account.
Machine-readable: OpenAPI 3.1 · llms.txt
call.completed; map fields like takenMessages[0].message or call.from into Google Sheets, Slack, HubSpot, your calendar — anything.Make, n8n and any app with an incoming-webhook URL work the same way.
| Event | When | Adds |
|---|---|---|
call.started | The AI picked up | — |
message.taken | The AI recorded a message or callback request | message: name, phone, message |
text.sent | The AI texted the caller | text: to, body |
transaction.created | A create/update/cancel tool succeeded (booking, order, reservation…) | transaction: tool, kind, externalId, summary |
call.completed | The call ended | counts, takenMessages, transactions; transcript in full mode |
# call.completed — standard mode
POST https://your-app.example/talki X-TT-Event: call.completed
{
"id": "evt_9b1f…", "type": "call.completed", "version": 1, "createdAt": "2026-09-06T14:02:11Z",
"account": "idHA5…",
"call": { "sid": "CA0e34…", "from": "+15550100123", "to": "+15879000000",
"startedAt": "2026-09-06T14:01:19Z", "endedAt": "2026-09-06T14:02:11Z",
"durationSeconds": 52, "endReason": "caller said goodbye", "language": "en", "recordingId": "RE1eaa…" },
"counts": { "messages": 6, "takenMessages": 1, "textsSent": 1 },
"takenMessages": [ { "name": "Sam Carter", "phone": "+15550100123",
"message": "Wants a quote for a kitchen renovation; call back after 5 pm.", "at": "…" } ],
"transactions": []
}Records are always written in your language — a Persian- or French-speaking caller still produces an English note and a Latin-letter name (spelled back to the caller to confirm). call.language tells you what they spoke.
+1555•••0123). For clinics, law firms, and strict data-minimization policies. Fetch details on demand with the REST API, which is audited.Bodies are never logged on our side; only delivery metadata (status, attempts) is kept. Per-call erasure is available via DELETE /sessions/{callSid}.
Every request carries X-TT-Timestamp (unix seconds), X-TT-Signature-256 (t=<ts>,v1=<hex> — HMAC-SHA256 ofts + "." + rawBody with your endpoint's secret) and, for compatibility with the SMS webhook, the legacy X-TT-Signature(HMAC-SHA256 of the raw body). Verify v1 and reject requests older than 5 minutes.
# Node.js
const crypto = require('crypto');
function verify(rawBody, headers, secret) {
const ts = headers['x-tt-timestamp'];
const v1 = (headers['x-tt-signature-256'] || '').split(',').find(p => p.startsWith('v1='))?.slice(3);
if (!ts || !v1 || Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;
const expected = crypto.createHmac('sha256', secret).update(ts + '.' + rawBody).digest('hex');
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1));
}Return a 2xx quickly (10 s timeout). Failed deliveries are retried at +10 s, +1 min, +5 min and +15 min; an endpoint that fails 100 times in a row is disabled automatically and shown in your dashboard.
Authenticate with your account API key (Authorization: Bearer tt_sk_…, created in Developer). Keys may carry scopes: receptionist:read or receptionist:manage; keys without scopes keep full access.
| Endpoint | Scope | Purpose |
|---|---|---|
GET /api/v1/receptionist/sessions | read | List calls, newest first (limit, before, from). Shaped by your payload mode. |
GET /api/v1/receptionist/sessions/{callSid} | read | One call. |
DELETE /api/v1/receptionist/sessions/{callSid} | manage | Erase transcript, messages and taken messages (timing kept). |
GET · POST /api/v1/receptionist/webhooks | read · manage | List / create endpoints (url, events, payloadMode). The secret is returned once. |
PATCH · DELETE /api/v1/receptionist/webhooks/{id} | manage | Enable/disable · delete. |
POST /api/v1/receptionist/webhooks/{id}/test | manage | Send a signed sample call.completed. |
GET · POST /api/v1/receptionist/tools | read · manage | List / register tools the AI can call during a call (see below). The secret is returned once. |
PATCH · DELETE /api/v1/receptionist/tools/{id} · POST …/test | manage | Enable/disable · delete · call your endpoint once with sample arguments. |
GET /api/v1/receptionist/me | read | Identify the account behind a key (connection test for Zapier/Make). |
# List recent calls
curl https://talkitalki.ca/api/v1/receptionist/sessions?limit=20 \
-H "Authorization: Bearer tt_sk_your_key"
{ "version": 1, "payloadMode": "standard",
"sessions": [ { "callSid": "CA0e34…", "startedAt": "…", "durationSeconds": 52, "endReason": "caller said goodbye",
"language": "en", "engine": "live", "from": "+15550100123",
"counts": { "messages": 6, "takenMessages": 1, "textsSent": 1 },
"takenMessages": [ … ], "transactions": [] } ],
"nextBefore": "2026-09-05T20:19:22.603Z" }Register a tool: a name, a plain-English description the AI reads, a JSON Schema for its arguments, and an HTTPS endpoint in your app. When a caller wants something the tool can do — check availability, book an appointment, reserve a table, place an order, look up an order — the receptionist calls your endpoint mid-conversation with JSON arguments and speaks from the result. Booking systems, reservation books, ordering platforms and CRMs all use the same contract. Register in Dashboard → Developer → AI Receptionist Tools or via REST.
# Register a tool (POST /api/v1/receptionist/tools)
{
"name": "get_available_slots",
"kind": "query", // query | create | update | cancel
"description": "List open appointment times for a date. Include the client's address when the service is at their home.",
"parameters": { "type": "object", "properties": {
"date": { "type": "string", "format": "date", "description": "YYYY-MM-DD in the business timezone" },
"address": { "type": "string" } }, "required": ["date"] },
"url": "https://your-app.example/talki/tools/get_available_slots",
"timeoutMs": 2500
}
→ 201 { "tool": { "id": "…", … }, "secret": "toolsec_…" } // secret shown once# What Talki Talki sends to your endpoint
POST https://your-app.example/talki/tools/get_available_slots
X-TT-Timestamp / X-TT-Signature-256 / X-TT-Tool: get_available_slots / Idempotency-Key: CA0e34…:get_available_slots:1
{
"id": "tc_CA0e34…_1", "tool": "get_available_slots", "kind": "query", "account": "idHA5…",
"business": { "timezone": "America/Edmonton", "name": "Sunny Cleaning Co." },
"call": { "sid": "CA0e34…", "from": "+15550100123", "to": "+15879000000", "language": "en", "callerName": "Sam Carter" },
"arguments": { "date": "2026-09-08", "address": "45 King Street, Calgary" }
}
// business.timezone is the account's business timezone — interpret dates/times in it.
// call.callerName is present when the number matches a saved contact (null otherwise).# What you return (within the timeout)
200 { "ok": true,
"result": { "slots": [ { "id": "s1", "startISO": "2026-09-08T10:00:00-06:00", "label": "10:00 AM" } ] },
"speak": "We have 10, 11 or 2 available." } // optional: exact words for the AI to say
200 { "ok": false, "error": "No openings that day",
"alternatives": [ { "startISO": "2026-09-09T10:00:00-06:00", "label": "Tuesday 10:00 AM" } ] }
// create / update / cancel tools: return an id so it appears in the call's transactions
200 { "ok": true, "result": { "id": "bk_9f2…", "summary": "Cleaning, Tue Sep 8 10:00" } }| Pattern | Typical tools | Notes |
|---|---|---|
| Appointments | get_available_slots · book_appointment · reschedule_appointment · cancel_appointment | Return slots with an id/startISO; the AI offers up to three and reads details back before booking. |
| Reservations | check_table_availability · reserve_table · modify_reservation | Party size, time window, special requests; deposits via a link texted to the caller. |
| Orders | get_menu · check_item_availability · quote_order · place_order · order_status | Nested modifiers, sold-out items, pickup/delivery; never take card numbers by voice — text a pay link. |
| Anything else | status lookups, account notes, quotes, waitlists… | Any JSON in, any JSON out. |
ok:false is a normal conversational outcome ("that time is taken") and is spoken to the caller. Only timeouts, network errors and 4xx/5xx responses count as failures toward auto-disable.business.timezone (IANA) is sent on every call and on call.completed; dates the AI passes as arguments are already in that timezone.create, update and cancel tools are read back to the caller and confirmed before they're called, then called exactly once.Idempotency-Key so a repeated call can never double-book or double-order.transactions in call.completed and fire a transaction.created event. Raw results are not stored.call.language says what they spoke.Authorization) can be attached for platforms that can't verify HMAC.id — make your handler idempotent, since a retried delivery repeats the same event.Developer support: api@pandacat.ca