Voor ontwikkelaars
Koppel Telofia aan de systemen die je al gebruikt
Haal gesprekken, transcripties, afspraken en contacten op via een REST API, ontvang ondertekende webhooks zodra er iets gebeurt, laat de assistent tijdens het gesprek live gegevens in je systeem opzoeken en zet met één regel code een spraakassistent op je website.
De REST API en webhooks zitten in elk abonnement, vanaf Line; hogere abonnementen hebben hogere limieten. Tijdens de gratis proefperiode heb je testtoegang: 1 API-sleutel, 20 verzoeken per minuut.
https://telofia.com/api/v1curl https://telofia.com/api/v1/me \
-H "Authorization: Bearer tf_live_…"{
"object": "account",
"id": "1f2e3d4c-5b6a-4789-8a7b-6c5d4e3f2a1b",
"name": "Smile Dental",
"timezone": "Europe/Warsaw",
"locale": "en",
"country": "PL",
"currency": "PLN",
"status": "active",
"plan": {
"id": "team",
"billing_cycle": "month",
"trial_ends_at": null,
"period_start": "2026-10-01T00:00:00Z",
"period_end": "2026-11-01T00:00:00Z",
"recording_history_days": 365
},
"minutes": {
"included": 3000,
"extra": 100,
"total": 3100,
"used": 412.5,
"remaining": 2687.5
},
"api_key_id": "2a3b4c5d-6e7f-4a8b-9c0d-1e2f3a4b5c6d",
"scopes": [
"read:calls",
"read:appointments",
"read:contacts",
"read:tasks"
],
"limits": {
"plan": "team",
"requests_per_minute": 180,
"requests_per_day": 50000,
"max_api_keys": 10,
"max_webhook_endpoints": 10
}
}Wat je kunt koppelen
Zeven manieren om Telofia in je bedrijf in te passen
Kies wat past: automatiseringen zonder code, een paar regels code of een volledige integratie in twee richtingen.
- In elk abonnement
REST API
Haal gesprekken met transcriptie en opname, afspraken, contacten en taken op. Maak en wijzig contacten, annuleer of verplaats afspraken en sluit taken af vanuit je eigen systeem.
- In elk abonnement
Webhooks
Ondertekende JSON-events in realtime: gesprek gestart of afgerond, afspraak geboekt, geannuleerd of verplaatst, nieuw contact, terugbeltaak.
- In elk abonnement
Live gegevens via MCP
Koppel je MCP-server en de assistent controleert tijdens het gesprek voorraad, beschikbaarheid of orderstatus.
- Vanaf Office
Spraakwidget voor je website
Eén scripttag voegt een knop “Praat met ons” toe. Bezoekers spreken in de browser met je assistent.
- In elk abonnement
Telefonische onboarding
Stuur nieuwe aanmeldingen vanuit je systeem door en de assistent belt ze om ze op weg te helpen.
- In elk abonnement
Agenda & CRM
Google Agenda voor boekingen, HubSpot en Pipedrive voor contacten, gesprekken, afspraken en taken.
- In elk abonnement
Zapier, Make, n8n
Flows zonder code op basis van webhooks en de API: Google Sheets, Slack, e-mail, elk CRM.
Snel starten
Je eerste verzoek in twee minuten
- 1
Maak een API-sleutel aan
Ga in het dashboard naar Ontwikkelaars → API-sleutels, geef de sleutel een naam en kies de rechten. De sleutel (tf_live_…) wordt maar één keer getoond, dus bewaar hem meteen in je secretsmanager.
- 2
Test de verbinding
Roep GET /me aan. Dat werkt met elke geldige sleutel en geeft je account, de rechten van de sleutel en de limieten van je abonnement terug.
- 3
Lees gegevens of abonneer je op events
Vraag lijsten met gesprekken, afspraken en contacten op, of voeg onder Ontwikkelaars → Webhooks een endpoint toe om events direct te ontvangen.
curl https://telofia.com/api/v1/me \
-H "Authorization: Bearer tf_live_…"const API = "https://telofia.com/api/v1";
const headers = { Authorization: `Bearer ${process.env.TELOFIA_API_KEY}` };
// All calls booked since 1 October, page by page
let cursor = null;
while (true) {
const url = new URL(`${API}/calls`);
url.searchParams.set("outcome", "booked");
url.searchParams.set("since", "2026-10-01T00:00:00Z");
url.searchParams.set("limit", "100");
if (cursor) url.searchParams.set("cursor", cursor);
const res = await fetch(url, { headers });
if (res.status === 429) {
// rate limited: wait for the number of seconds in Retry-After, then retry the same page
await new Promise((r) => setTimeout(r, Number(res.headers.get("Retry-After") ?? 1) * 1000));
continue;
}
if (!res.ok) throw new Error((await res.json()).error.message);
const page = await res.json();
for (const call of page.data) console.log(call.started_at, call.from, call.summary);
if (!page.has_more) break;
cursor = page.next_cursor;
}REST API v1
Gesprekken, afspraken en contacten via HTTPS
JSON erin, JSON eruit. Elk verzoek wordt geauthenticeerd met een API-sleutel en valt binnen de limieten van je abonnement. Verzoeken zie je in het dashboard (methode, pad, status en duur — nooit de inhoud van verzoeken of antwoorden).
Authenticatie
Maak een sleutel aan bij API-sleutels en stuur hem mee als Bearer-token. Sleutels beginnen met tf_live_ en horen bij één account.
Authorization: Bearer tf_live_…Paginering
Lijst-endpoints geven tot limit items terug (1–100, standaard 25) in een vaste volgorde. Is has_more true, geef dan next_cursor ongewijzigd mee als cursor (met dezelfde filters) voor de volgende pagina. Een ongeldige cursor geeft 400 invalid_request.
Fouten
Fouten gebruiken standaard HTTP-statuscodes en een JSON-body met een type en een leesbare foutmelding.
{ "error": { "type": "…", "message": "…" } }Endpoints
- GET
/meRecht: elke sleutelHuidig account ophalen
Geeft het account terug waar de API-sleutel bij hoort. Handig om de verbinding te testen.
- GET
/assistantsRecht: elke sleutelAssistenten ophalen
Je assistenten met taal, status en toegewezen telefoonnummers. Gebruik de id als assistant_id om gesprekken te filteren.
- GET
/assistants/{id}Recht: elke sleutelEen assistent ophalen
Eén assistent met taal, status en telefoonnummers. Met de scope read:assistants (of write:assistants) ook de prompt: instructions, greeting, company_profile en updated_at.
- PATCH
/assistants/{id}Recht: write:assistantsDe prompt van de assistent wijzigen
Wijzigt de instructies, begroeting of het bedrijfsprofiel. Weggelaten velden blijven ongewijzigd. Dezelfde limieten als in het dashboard: instructies tot 20.000 tekens, bedrijfsprofiel tot 10.000, begroeting 5–600 tekens en die moet zeggen dat de beller met een AI praat (anders 400 met code ai_disclosure). Elke wijziging komt in de versiegeschiedenis van de assistent (bron API) en geldt vanaf het volgende gesprek. De eerlijkheidsregels van het platform (AI-melding, opnamemelding) gelden altijd.
- GET
/phone-numbersRecht: elke sleutelTelefoonnummers ophalen
De telefoonnummers van je account met land, type, status en de assistent die ze beantwoordt.
- GET
/callsRecht: read:callsGesprekken weergeven
Gesprekken met de nieuwste eerst, zonder transcripties. Demogesprekken worden nooit meegenomen.
- GET
/calls/{id}Recht: read:callsEen gesprek ophalen
Eén gesprek met de volledige transcriptie en de afspraken die tijdens het gesprek zijn geboekt.
- GET
/calls/{id}/recordingRecht: read:callsGespreksopname ophalen
Ondertekende links (1 uur geldig) om de MP3-opname af te spelen en te downloaden (stereo: beller links, assistent rechts). Een opname die ouder is dan de opnamegeschiedenis van je abonnement geeft 403 plan_required met recording_history_days; een gesprek zonder opname geeft 404.
- GET
/appointmentsRecht: read:appointmentsAfspraken weergeven
Aankomende afspraken op starttijd (standaard vanaf nu).
- GET
/appointments/{id}Recht: read:appointmentsEen afspraak ophalen
Eén afspraak van je account.
- POST
/appointments/{id}/cancelRecht: write:appointmentsEen afspraak annuleren
Annuleert een komende afspraak, verwijdert die uit de gekoppelde Google--agenda en stuurt de webhook appointment.canceled. calendar_sync laat zien of de agenda is bijgewerkt.
- POST
/appointments/{id}/rescheduleRecht: write:appointmentsEen afspraak verzetten
Verzet een komende afspraak naar een nieuw tijdstip. Antwoordt met 409 als het tijdstip overlapt met een andere afspraak of bezette tijd in de gekoppelde agenda. Zonder ends_at blijft de duur gelijk. Stuurt de webhook appointment.rescheduled.
- GET
/contactsRecht: read:contactsContacten weergeven
Je bellers, met de nieuwste eerst. De assistent onthoudt ze over gesprekken heen.
- GET
/contacts/{id}Recht: read:contactsContact ophalen
Eén contact met naam, e-mail, tags, notities en de herkomst van naam en e-mail (call, system of manual).
- POST
/contactsRecht: write:contactsContact aanmaken
Voegt een contact toe, bijvoorbeeld vanuit je CRM, zodat de assistent de naam van de beller kent. Naam en e-mail die via de API zijn opgeslagen, zijn gemarkeerd als manual en worden nooit door de assistent overschreven. Geeft 409 met contact_id als het nummer al bestaat. Verstuurt de webhook contact.created.
- PATCH
/contacts/{id}Recht: write:contactsContact wijzigen
Wijzigt naam, e-mail, notities of blokkering. Weggelaten velden blijven ongewijzigd en null maakt een veld leeg. Een gewijzigde naam of e-mail is gemarkeerd als manual, dus de assistent overschrijft die niet. Het telefoonnummer kan niet worden gewijzigd.
- GET
/tasksRecht: read:tasksTaken ophalen
Terugbelverzoeken, berichten en andere taken uit gesprekken, nieuwste eerst.
- GET
/tasks/{id}Recht: read:tasksTaak ophalen
Eén taak van je account.
- PATCH
/tasks/{id}Recht: write:tasksTaak wijzigen
Zet de status van de taak op open, done of dismissed, bijvoorbeeld nadat je team de klant heeft teruggebeld.
- POST
/calls/outboundRecht: write:callsEen uitgaand gesprek aanmaken
Je assistent belt één persoon vanaf je bedrijfsnummer met het doel dat jij opgeeft, net als een stap in een onboardingreeks. Het gesprek gaat nu uit of op call_at, altijd binnen de beltijden (ma–za, 9:00–20:00 in de tijdzone van je account; daarbuiten schuift het op naar het eerstvolgende toegestane moment en laat call_at zien wanneer). Neemt niemand op, dan probeert de assistent het tot 3 keer, met 2 uur ertussen. Geeft 202 terug met het gesprek in status scheduled. Fouten: 400 consent_missing, invalid_phone, number_not_callable, international of invalid_call_at; 409 opted_out (de persoon wil niet gebeld worden), already_scheduled (met existing_id), assistant_not_live, over_quota of no_outbound_number; 429 daily_outbound_limit met limit (standaard 200 gesprekken per 24 uur). Het resultaat komt binnen via de webhooks call.completed en outbound_call.finished met je external_id.
- GET
/calls/outbound/{id}Recht: write:callsEen uitgaand gesprek ophalen
Status van een ingepland uitgaand gesprek: scheduled, dialing, done (met call_id van het gesprek), failed (last_error, bijv. no_answer na alle pogingen) of canceled. Werkt ook met de callback_id uit webhooks voor onboarding- en terugbelgesprekken.
- DELETE
/calls/outbound/{id}Recht: write:callsEen uitgaand gesprek annuleren
Annuleert een via de API aangemaakt uitgaand gesprek dat nog ingepland staat. Veilig om te herhalen (al geannuleerd geeft 200). Geeft 409 not_cancelable als er al gebeld wordt of het gesprek is afgerond, of bij onboardinggesprekken (verwijder de persoon dan met POST /api/outreach/unenroll).
- GET
/messagesRecht: read:messagesSms’en weergeven
Sms’en die je account heeft verstuurd en antwoorden van klanten, nieuwste eerst. phone is het nummer van de klant (ontvanger of afzender van een antwoord). generated_by: template, ai of manual; charged_from: plan of credits.
- POST
/messagesRecht: write:messagesEen sms versturen
Stuurt een sms naar een van je contacten of bellers: je eigen tekst, of een sjabloon van je account met vars (date, time, name, service, link, amount). Telt mee voor het sms-tegoed van je abonnement en extra sms’en. Fouten: 400 invalid_request (consent_missing, invalid_phone, too_long, missing_vars); 409 conflict (opted_out — de klant antwoordde STOP, over_quota, recipient_not_allowed — maak eerst het contact aan, blocked_destination); 429 rate_limited.
Rechten
Elke sleutel heeft de scopes die bij het aanmaken zijn gekozen. Scopes kun je later niet wijzigen — maak een nieuwe sleutel aan als je meer nodig hebt. Mist de sleutel de vereiste scope, dan antwoorden endpoints met 403 insufficient_scope. GET /me, /assistants en /phone-numbers werken met elke geldige sleutel.
read:callsGesprekken, samenvattingen en transcripties lezenread:appointmentsAfspraken lezenwrite:appointmentsAfspraken annuleren en verzettenread:contactsContacten lezenwrite:contactsContacten aanmaken en wijzigenread:tasksTaken en terugbelverzoeken lezenwrite:tasksStatus van taken wijzigenwrite:callsUitgaande gesprekken starten, opvragen en annulerenread:messagesSms’en en antwoorden van klanten lezenwrite:messagesSms’en naar je contacten sturenread:assistantsDe prompt van de assistent lezen (instructies, begroeting, bedrijfsprofiel)write:assistantsDe prompt van de assistent wijzigen
Fouten
400invalid_request — een parameter ontbreekt of is ongeldig401unauthorized — API-sleutel ontbreekt, is ongeldig of is ingetrokken403insufficient_scope — de sleutel mist de scope van het endpoint; plan_required — het account heeft geen actief abonnement of de opname is ouder dan de opnamegeschiedenis van het abonnement404not_found — het object bestaat niet in je account409conflict — het tijdstip is al bezet, de afspraak kan niet worden gewijzigd, er bestaat al een contact met dit telefoonnummer of een uitgaand gesprek kan niet worden gevoerd (code: opted_out, already_scheduled, assistant_not_live, over_quota, no_outbound_number, not_cancelable)429rate_limited — te veel verzoeken; probeer opnieuw na de tijd in de Retry-After-header500server_error — er ging aan onze kant iets mis503service_unavailable — tijdelijk probleem; probeer opnieuw na de tijd in de Retry-After-header
De volledige API-referentie met parameters en voorbeeldantwoorden staat in het dashboard onder Ontwikkelaars → API-referentie.
Webhooks
Events in realtime naar je server
Voeg in het dashboard een https-endpoint toe, kies de events en wij sturen een ondertekende JSON-envelop (POST) zodra er iets gebeurt. “Test versturen” levert een voorbeeld van je eerste event, handig om velden te koppelen in Zapier of Make.
Events
call.completedGesprek voltooidSamenvatting, resultaat, sentiment, transcriptie, contact en boekingen; bij uitgaande gesprekken ook de reeks of het API-verzoek (callback_id, external_id, outreach).
call.startedGesprek gestartEr is een gesprek aangenomen (inkomend, uitgaand of via het web).
appointment.bookedAfspraak geboektDe assistent heeft een afspraak geboekt.
appointment.canceledAfspraak geannuleerdEen afspraak is telefonisch of via de API geannuleerd.
appointment.rescheduledAfspraak verzetEen afspraak is naar een nieuw tijdstip verzet (inclusief het vorige tijdstip).
task.createdTerugbelverzoek / taak aangemaaktEen beller vroeg om teruggebeld te worden of liet een taak achter.
contact.createdNieuw contactIemand die voor het eerst belde, is als contact opgeslagen.
outbound_call.finishedUitgaand gesprek afgerondEen ingepland uitgaand gesprek (API, onboardingreeks of terugbelverzoek) is afgerond of mislukt — ook als niemand opnam na alle pogingen.
Headers bij elke levering
Telofia-SignatureHandtekening: t=<unix-tijd>,v1=<HMAC-SHA256 hex>Telofia-EventType event, bijv. appointment.bookedTelofia-DeliveryUnieke leverings-IDUser-AgentTelofia-Webhooks/1.0 · Identificeert onze webhookafzender
Levering en nieuwe pogingen
- Antwoord binnen 10 seconden met een willekeurige 2xx-status. Doorverwijzingen worden niet gevolgd.
- Mislukte leveringen proberen we opnieuw na 1 min, 5 min, 30 min, 2 u, 6 u en 12 u (7 pogingen, ongeveer 21 uur).
- Hetzelfde event kan vaker dan één keer binnenkomen. Gebruik de id van het event om dubbele te negeren.
- Alleen openbare https-adressen zijn toegestaan. Na 50 mislukte pogingen op rij wordt een endpoint gepauzeerd en toont het dashboard waarom.
- Het dashboard houdt een leveringslog bij met statuscodes en antwoorden en laat je een levering handmatig opnieuw versturen.
{
"id": "evt_3f9a1c7e5b2d4f6a8c0e1b3d5f7a9c1e",
"type": "call.completed",
"created_at": "2026-10-01T09:17:45Z",
"account_id": "1f2e3d4c-5b6a-4789-8a7b-6c5d4e3f2a1b",
"data": {
"id": "5b1f0c6e-2a8d-4f3e-9d51-7c0a4e2b9f10",
"object": "call",
"assistant_id": "0e6f4c8a-3b2d-4a1e-8f7c-5d9b2a1c3e4f",
"phone_number_id": "4d3c2b1a-0f9e-4d8c-b7a6-958473625140",
"contact_id": "9a8b7c6d-5e4f-4a3b-2c1d-0e9f8a7b6c5d",
"direction": "inbound",
"status": "completed",
"from": "+48601234567",
"to": "+48221234567",
"started_at": "2026-10-01T09:14:03Z",
"ended_at": "2026-10-01T09:17:41Z",
"duration_sec": 218,
"summary": "Anna Kowalska booked a first consultation for Friday 10:00.",
"outcome": "booked",
"sentiment": "positive",
"tags": [
"new-customer"
],
"has_recording": true,
"end_reason": "caller_hangup",
"callback_id": null,
"external_id": null,
"outreach": null,
"caller_spoke": true,
"caller_words": 6,
"transcript": [
{
"role": "caller",
"text": "I'd like to book a consultation this week.",
"at": 4.1
}
],
"contact": {
"id": "9a8b7c6d-5e4f-4a3b-2c1d-0e9f8a7b6c5d",
"object": "contact",
"phone": "+48601234567",
"name": "Anna Kowalska",
"name_source": "call",
"email": "anna.kowalska@example.com",
"email_source": "call",
"notes": null,
"tags": [
"customer"
],
"blocked": false,
"calls_count": 3,
"last_call_at": "2026-10-01T09:14:03Z",
"created_at": "2026-08-12T15:02:11Z"
},
"appointments": [
{
"id": "c3d2e1f0-a9b8-4c7d-8e6f-5a4b3c2d1e0f",
"object": "appointment",
"call_id": "5b1f0c6e-2a8d-4f3e-9d51-7c0a4e2b9f10",
"contact_id": "9a8b7c6d-5e4f-4a3b-2c1d-0e9f8a7b6c5d",
"customer_name": "Anna Kowalska",
"customer_phone": "+48601234567",
"customer_email": "anna.kowalska@example.com",
"service": "First consultation",
"starts_at": "2026-10-03T08:00:00Z",
"ends_at": "2026-10-03T08:30:00Z",
"status": "booked",
"external_id": "google:7h3k9s2l1m0n",
"created_at": "2026-10-01T09:16:20Z"
}
]
}
}Handtekeningen verifiëren. Elke levering bevat een header Telofia-Signature in de vorm t=timestamp,v1=signature. Bereken de HMAC-SHA256 van “timestamp.raw_body” met je ondertekeningssleutel en vergelijk die met v1. Weiger timestamps die ouder zijn dan 5 minuten.
import crypto from "node:crypto";
// Express: app.post("/webhook", express.raw({ type: "application/json" }), handler)
export function verify(rawBody, signatureHeader, secret) {
// Telofia-Signature: t=1727774265,v1=5f2c…
const parts = Object.fromEntries(signatureHeader.split(",").map((p) => p.split("=")));
const expected = crypto.createHmac("sha256", secret).update(`${parts.t}.${rawBody}`).digest("hex");
const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
const given = Buffer.from(parts.v1 ?? "");
// timingSafeEqual needs buffers of the same length
return fresh && given.length === expected.length && crypto.timingSafeEqual(Buffer.from(expected), given);
}import hmac, hashlib, time
def verify(raw_body: bytes, signature_header: str, secret: str) -> bool:
parts = dict(p.split("=", 1) for p in signature_header.split(","))
expected = hmac.new(secret.encode(), f"{parts['t']}.".encode() + raw_body, hashlib.sha256).hexdigest()
fresh = abs(time.time() - int(parts["t"])) < 300
return fresh and hmac.compare_digest(expected, parts.get("v1", ""))import express from "express";
import { verify } from "./verify.js";
const app = express();
const seen = new Set(); // use your database in production
app.post("/telofia/webhook", express.raw({ type: "application/json" }), (req, res) => {
const raw = req.body.toString("utf8");
if (!verify(raw, req.get("Telofia-Signature") ?? "", process.env.TELOFIA_WEBHOOK_SECRET)) return res.sendStatus(401);
const event = JSON.parse(raw);
if (seen.has(event.id)) return res.sendStatus(200); // retried delivery, already handled
seen.add(event.id);
if (event.type === "appointment.booked") {
const a = event.data;
console.log("New booking:", a.customer_name, a.service, a.starts_at);
}
res.sendStatus(200); // answer with 2xx within 10 seconds
});
app.listen(3000);Stabiele waarden
Deze waarden in gesprekken (API en call.*-webhooks) en in uitgaande gesprekken (outbound_call.finished, /calls/outbound) zijn een stabiel contract: we kunnen nieuwe waarden toevoegen, maar deze wijzigen of verwijderen we niet. Uitgaande pogingen die niet worden opgenomen, in gesprek zijn of worden geweigerd, leveren nooit een gesprek op, dus daarvoor komt er geen call.completed: na de laatste poging ontvang je outbound_call.finished met last_error no_answer. Een gesprek dat door de voicemail wordt aangenomen, telt als opgenomen (meestal een kort gesprek met outcome other).
Status van het gesprek
in_progress- Het gesprek loopt.
completed- De persoon heeft met de assistent gesproken.
missed- Niemand sprak, of de assistent kon het gesprek niet aannemen (zie end_reason).
failed- Het gesprek kon door een fout niet worden afgehandeld.
Resultaat van het gesprek
booked- Er is een afspraak geboekt.
transferred- Het gesprek is doorverbonden met een medewerker.
message- Er is een bericht, terugbelverzoek, bestelling of opvolging genoteerd.
info- De persoon kreeg informatie; verder was er niets nodig.
spam- Spam, telemarketing of een geblokkeerd nummer.
other- Al het overige, bijv. een heel kort gesprek of voicemail.
Reden van beëindiging (end_reason)
caller_hangup- De andere persoon heeft opgehangen.
caller_hangup_greeting- Uitgaand gesprek: de persoon hing op tijdens of direct na de begroeting zonder iets te zeggen (gesprek tot 45 s). caller_spoke is dan false.
agent_hangup- De assistent beëindigde het gesprek na het afscheid.
transferred- Het gesprek is doorverbonden met een medewerker.
silence- Beëindigd na een lange stilte.
max_duration- De maximale gespreksduur is bereikt.
spam- Beëindigd als spam.
blocked- Het nummer is geblokkeerd in contacten.
busy- Alle lijnen van het account waren bezet.
over_quota- Geen belminuten meer over.
trial_expired- De gratis proefperiode is afgelopen.
inactive- Geen actief abonnement.
assistant_not_live- De assistent is gepauzeerd.
no_assistant- Geen assistent neemt dit nummer op.
ai_budget- Zonder AI afgehandeld vanwege een tijdelijke limiet; het team is gevraagd terug te bellen.
error- Een technische fout heeft het gesprek beëindigd.
Status van het uitgaande gesprek
scheduled- Wacht op het ingestelde tijdstip (of op de volgende poging).
dialing- Wordt nu gebeld.
done- Opgenomen; call_id is het gesprek.
failed- Niet opgenomen na alle pogingen, of kon niet worden uitgevoerd (zie last_error).
canceled- Geannuleerd (API, persoon uit de reeks verwijderd of reeks uitgezet).
last_error van het uitgaande gesprek
no_answer- Niemand nam op (ook bij in gesprek of geweigerd).
failed- De assistent was op het moment van bellen niet beschikbaar (abonnement, minuten of pauze).
no_result- Binnen 15 minuten geen resultaat van het gesprek.
expired- Het gesprek was meer dan 2 uur te laat en is daarom niet gevoerd.
outside_window- Verplaatst naar de volgende beltijden (status blijft scheduled).
dial_error- Het telefoonnetwerk weigerde het gesprek (verstuurd als dial_<reason>).
over_quota- Geen belminuten meer over.
inactive- Geen actief abonnement.
assistant_not_live- De assistent is gepauzeerd.
no_outbound_number- Geen bedrijfsnummer om mee te bellen.
canceled_by_api- Geannuleerd met DELETE /calls/outbound/{id}.
unenrolled- De persoon is uit de reeks verwijderd (POST /api/outreach/unenroll).
opt_out- De persoon wil niet gebeld worden.
replaced- Vervangen door een nieuwer terugbelverzoek naar hetzelfde nummer.
Live gegevens via MCP
De assistent zoekt tijdens het gesprek dingen op in je systeem
Koppel een externe MCP-server (Model Context Protocol), zoals je webwinkelcatalogus, voorraad, boekings- of ordersysteem. Jij bepaalt precies welke tools en bronnen (alleen-lezen) de assistent mag gebruiken.
Vooraf leren
Voor inhoud die zelden verandert, zoals prijslijsten, productbeschrijvingen of voorwaarden. Gekozen bronnen en alleen-lezen-tools worden elke 1 tot 168 uur of op verzoek in de kennis van de assistent geïmporteerd. Geen vertraging tijdens het gesprek.
Live controleren tijdens het gesprek
Voor dingen die veranderen: voorraad, beschikbaarheid, orderstatus. De assistent roept de tool tijdens het gesprek aan met een kort “een moment, ik kijk het even na”. Antwoordt je server niet binnen ongeveer 2,5 seconden, dan zegt hij dat hij het nu niet kan bevestigen en biedt hij aan dat je team contact opneemt.
Wat je nodig hebt
- Een MCP-server die via https bereikbaar is (Streamable HTTP, bijv. https://mcp.example.com/mcp; oudere HTTP+SSE-servers worden automatisch herkend).
- Authenticatie: geen, een Bearer-token of een eigen header zoals X-API-Key. Geheimen worden versleuteld (AES-256-GCM) en nooit naar de browser gestuurd.
- Alleen-lezen-tools. Tools die als destructief gemarkeerd zijn, worden geblokkeerd; voor tools zonder markering ‘alleen-lezen’ bevestig je dat ze alleen gegevens lezen.
- Tot 5 bronnen en 50 live-tools per assistent. Live opvragingen moeten binnen ongeveer 2 seconden antwoorden.
- Optioneel voor de websitewidget: kies bij een tool „Aan het begin van het gesprek” het argument dat het identiteitstoken van de ingelogde bezoeker krijgt (zie Spraakwidget voor je website).
Argumenten worden gecontroleerd tegen het schema van de tool, resultaten worden ingekort en uitsluitend als gegevens behandeld (nooit als instructies), en elke opvraging wordt gelogd zonder gegevens van de beller. Privé- en interne adressen worden geweigerd.
Spraakwidget voor je website
Je assistent op je website, met één regel code
Bezoekers klikken op een knop en praten in de browser met dezelfde assistent die je telefoon opneemt. Webgesprekken gebruiken de minuten van je abonnement, zonder telefoniekosten.
Vanaf Office
- Ongeveer 14 KB, zonder afhankelijkheden, geïsoleerd in Shadow DOM zodat de stijlen van je site hem niet kunnen breken.
- Spreekt de taal van je pagina (<html lang>) of de taal die je instelt met data-lang="de".
- Live ondertiteling; is de microfoon geblokkeerd, dan kan de bezoeker typen en antwoordt de assistent met spraak.
- Beperk de widget tot je eigen domeinen, dan start hij alleen op die sites.
<script src="https://telofia.com/widget.js" data-key="tfw_…" async></script>// optional: open the widget from your own button
document.querySelector("#talk-to-us").addEventListener("click", () => window.TelofiaWidget?.open());
window.TelofiaWidget?.close(); // close the panel
window.TelofiaWidget?.destroy(); // remove the widget from the pageStrenge Content Security Policy? Sta script-src https://telofia.com https://cdn.jsdelivr.net, connect-src https://telofia.com wss://*.livekit.cloud https://*.livekit.cloud en media-src blob: toe.
Ingelogde gebruikers (identiteitstoken)
Als bezoekers op je website zijn ingelogd, weet de assistent wie er spreekt zonder naar het e-mailadres te vragen.
- Je server geeft de ingelogde gebruiker een kortlevend, ondertekend token (bijv. HMAC, 15 minuten geldig). Zet nooit een e-mailadres of gebruikers-ID in de browser: iedereen zou in de console dat van een ander kunnen invullen.
- Roep TelofiaWidget.identify({ token }) op elk moment aan; een latere aanroep vervangt het token en identify(null) wist het. Voordat widget.js is geladen, stel je window.TelofiaIdentity = { token, expires_at } in: dit wordt gelezen zodra het gesprek begint, en een verlopen token wordt overgeslagen.
- Kies in het dashboard het argument voor het identiteitstoken bij je MCP-tool „Aan het begin van het gesprek”. Telofia geeft het token ongewijzigd alleen aan dat argument door, slaat het nooit op, toont het nooit en houdt het buiten transcripties, samenvattingen, webhooks en logs; de assistent ziet het nooit.
- 8 tot 400 tekens: letters, cijfers en . _ ~ + / = - (bijv. base64url). Vernieuw het voordat het verloopt, bijvoorbeeld elke 10 minuten. Je MCP-server controleert het; een ongeldig of verlopen token moet hetzelfde resultaat geven als geen token. Telefoongesprekken en gesprekken zonder token werken zoals voorheen.
// signed-in user: a short-lived, signed token issued by YOUR server (never an e-mail or user ID)
const { token, expires_at } = await fetch("/api/telofia-identity").then((r) => r.json());
window.TelofiaIdentity = { token, expires_at }; // read when the conversation starts, also before widget.js loads
window.TelofiaWidget?.identify({ token, expires_at }); // replaces the token at any time
// refresh before it expires (e.g. every 10 minutes); on sign-out:
window.TelofiaIdentity = null;
window.TelofiaWidget?.identify(null);Telefonische onboarding
Je nieuwe klanten krijgen automatisch een welkomstgesprek
Meldt iemand zich aan in je systeem, stuur die persoon dan door naar een belreeks. De assistent belt vanaf je bedrijfsnummer, helpt op weg en belt een paar dagen later nog eens. Er wordt gebeld binnen de beltijden van de reeks: standaard van maandag tot zaterdag, van 9:00 tot 20:00 in jouw tijdzone, of op de dagen en tijden die je zelf instelt.
POST/api/outreach/enroll
Authenticeer met de sleutel van de reeks (tlo_…) uit het dashboard, als Bearer-token of in de header X-Telofia-Key. Elke reeks heeft een eigen sleutel.
Body van het verzoek
- Telefoonnummer, bij voorkeur in E.164-formaat. Nationale nummers worden gelezen met de landcode van je account.
phonestringverplicht - Moet true zijn: de persoon heeft toestemming gegeven om gebeld te worden, bijv. in je aanmeldformulier.
consenttrueverplicht - Naam, maximaal 120 tekens.
namestringoptioneel - E-mailadres.
emailstringoptioneel - Wat de assistent over deze persoon moet weten, maximaal 1.000 tekens (bijv. het gekozen abonnement).
contextstringoptioneel - De ID van de persoon in je systeem (tekst of getal). Dezelfde ID wordt maar één keer ingeschreven.
external_idstring | numberoptioneel
curl -X POST "https://telofia.com/api/outreach/enroll" \
-H "Authorization: Bearer tlo_…" \
-H "Content-Type: application/json" \
-d '{"phone":"+48601234567","name":"Anna Kowalska","email":"anna@example.com","context":"Signed up today, Start plan","external_id":"user_123","consent":true}'{
"ok": true,
"enrollment_id": "8f7e6d5c-4b3a-4c2d-9e1f-0a9b8c7d6e5f",
"external_id": "user_123",
"status": "active",
"next_call_at": "2026-10-09T10:20:00Z",
"limits": {
"per_day": 500,
"remaining_today": 499
}
}Antwoorden
- 201: ingeschreven. enrollment_id, external_id, status, next_call_at (tijdstip van het eerste gesprek), intro_sms_at (wanneer de sms vóór het eerste gesprek verstuurd wordt, of null) en limits { per_day, remaining_today }
- 200: al ingeschreven (zelfde external_id, of dit nummer loopt al in de reeks), met duplicate: true
- 400: invalid_request, invalid_phone of consent_required
- 401: invalid_key
- 409: sequence_disabled, consent_missing of opted_out (de persoon wil niet gebeld worden)
- 429: limit, de daglimiet voor inschrijvingen in de reeks is bereikt (500 nieuwe personen per 24 uur; de body bevat limit). rate_limited: meer dan 120 verzoeken per minuut met deze sleutel (Retry-After-header)
Beltijden: stel in het dashboard per reeks de dagen en een tijdvak tussen 7:00 en 21:00 in. Neemt niemand op, dan probeert de assistent het tot 3 keer, met 2 uur ertussen, binnen die tijden.
Sms vóór het gesprek: als dit voor de reeks aanstaat (dashboard), krijgt de persoon een kort bericht van het nummer waarmee de assistent gaat bellen, standaard 20 minuten vóór het eerste gesprek (5–120 min). Zou het eerste gesprek eerder plaatsvinden, dan schuift het op zodat de sms altijd eerst verstuurd wordt, binnen de beltijden. Een mislukte sms (de persoon antwoordde STOP, de sms-bundel is op) houdt het gesprek nooit tegen. Elke sms telt mee voor je sms-bundel en is in het dashboard te zien onder Sms.
Begroeting: in het dashboard stelt u per stap (of als standaard voor de hele reeks) de eerste zin van het gesprek in — een vaste tekst met de variabelen {first_name}, {first_name_vocative} (Poolse vocatief, bijv. Krystianie), {assistant_name}, Telofia (bedrijfsnaam in gesprekken uit de instellingen van de assistent) en {name}, met een aparte variant voor aanmeldingen zonder naam — of een modus waarin de assistent de eerste zin opstelt uit name, context, het doel van de stap en het resultaat van de tools „Aan het begin van het gesprek”. Dat er een AI-assistent belt en dat het gesprek wordt opgenomen, voegen we altijd toe aan de eerste zin als de tekst het niet zegt.
Iemand uit de reeks verwijderen
POST/api/outreach/unenroll
Heeft iemand de gesprekken niet meer nodig (bijvoorbeeld na de eerste verkoop, of na opzegging), stop dan zijn of haar reeks met dezelfde sleutel. Geplande gesprekken worden direct geannuleerd. Een gesprek dat al loopt wordt niet onderbroken, maar daarna volgen geen gesprekken meer.
- Precies één van: external_id (zoals verstuurd bij inschrijving), enrollment_id (uit het antwoord op de inschrijving) of phone.
external_id | enrollment_id | phoneverplicht - Optionele reden, maximaal 200 tekens, bewaard in het auditlogboek van je account.
reasonoptioneel
- 200: gestopt (changed: true) of al afgerond: status stopped, completed of failed (changed: false). Veilig om te herhalen.
- 400: invalid_request (geen of meer dan één identificatie) of invalid_phone
- 401: invalid_key
- 404: not_found, deze inschrijving bestaat niet in deze reeks
- 429: rate_limited
curl -X POST "https://telofia.com/api/outreach/unenroll" \
-H "Authorization: Bearer tlo_…" \
-H "Content-Type: application/json" \
-d '{"external_id":"user_123","reason":"first_sale"}'{
"ok": true,
"enrollment_id": "8f7e6d5c-4b3a-4c2d-9e1f-0a9b8c7d6e5f",
"external_id": "user_123",
"status": "stopped",
"changed": true
}Gespreksresultaten in webhooks
call.started en call.completed voor gesprekken uit een reeks bevatten callback_id, external_id en outreach { sequence_id, enrollment_id, external_id, step } (allemaal null bij inkomende gesprekken). Onbeantwoorde pogingen leveren geen gesprek op, dus daarvoor komt er geen call.completed: na de laatste poging ontvang je outbound_call.finished met status failed en last_error no_answer. Herhaalpogingen van één stap tellen als één resultaat. call.completed bevat ook caller_spoke en caller_words (hoeveel woorden de persoon zei). Als iemand opnam en tijdens of direct na de begroeting zonder iets te zeggen ophing, is end_reason caller_hangup_greeting.
{
"id": "5b1f0c6e-2a8d-4f3e-9d51-7c0a4e2b9f10",
"object": "call",
"assistant_id": "0e6f4c8a-3b2d-4a1e-8f7c-5d9b2a1c3e4f",
"phone_number_id": "4d3c2b1a-0f9e-4d8c-b7a6-958473625140",
"contact_id": "9a8b7c6d-5e4f-4a3b-2c1d-0e9f8a7b6c5d",
"direction": "outbound",
"status": "completed",
"from": "+48732127980",
"to": "+48601234567",
"started_at": "2026-10-01T09:14:03Z",
"ended_at": "2026-10-01T09:17:41Z",
"duration_sec": 218,
"summary": "Jan added the company name and tax ID; asked for a call tomorrow at 10:00 about payments.",
"outcome": "message",
"sentiment": "positive",
"tags": [],
"has_recording": true,
"end_reason": "agent_hangup",
"callback_id": "0f1e2d3c-4b5a-4968-8776-655443322110",
"external_id": "gapli-evt-1842",
"outreach": {
"sequence_id": "11111111-1111-4111-8111-111111111111",
"enrollment_id": "8f7e6d5c-4b3a-4c2d-9e1f-0a9b8c7d6e5f",
"external_id": "gapli-evt-1842",
"step": 1
},
"caller_spoke": true,
"caller_words": 112
}Agenda, CRM en automatisering
Kant-en-klare koppelingen, zonder code
Google Agenda
De assistent controleert de echte beschikbaarheid en boekt, verplaatst en annuleert afspraken in je agenda. Hij leest alleen bezette tijden, nooit titels van afspraken.
HubSpot
Elk gesprek wordt bij het contact vastgelegd, boekingen worden meetings en terugbelverzoeken taken. Optioneel maakt hij contacten en deals aan.
Pipedrive
Gesprekken als activiteiten of notities, boekingen als afspraken, terugbelverzoeken als activiteiten. Optioneel maakt hij personen, deals of leads aan.
Zapier, Make en n8n
Stapsgewijze handleidingen in het dashboard. Ze draaien op webhooks en de API, die in elk abonnement zitten.
Abonnementen en limieten
Welk abonnement je nodig hebt
Limieten per account: verzoeken per minuut (ook per sleutel, glijdend venster) en per dag (UTC). Boven een limiet krijg je 429 rate_limited met een Retry-After-header.
Spraakwidget voor je website: vanaf het Office-abonnement.
Hogere limieten nodig? Neem contact op, dan verhogen we ze voor je account.
Early access: hoor bij de eerste bedrijven die de telefoon door AI laten opnemen.
Klaar om te koppelen?
Start de gratis proefperiode, maak een test-API-sleutel aan en verstuur binnen een paar minuten je eerste verzoek. De API zit in elk abonnement, vanaf Line.