Für Entwickler
Verbinden Sie Telofia mit den Systemen, die Sie bereits nutzen
Lesen Sie Anrufe, Transkripte, Termine und Kontakte über eine REST-API, erhalten Sie signierte Webhooks, sobald etwas passiert, lassen Sie den Assistenten während des Anrufs Live-Daten in Ihrem System prüfen und bringen Sie mit einer Zeile Code einen Sprachassistenten auf Ihre Website.
REST-API und Webhooks sind in jedem Tarif enthalten, ab Line; höhere Tarife haben höhere Limits. In der kostenlosen Testphase haben Sie Testzugang: 1 API-Schlüssel, 20 Anfragen pro Minute.
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
}
}Was Sie verbinden können
Sieben Wege, Telofia in Ihr Unternehmen einzubinden
Wählen Sie, was passt: Automatisierungen ohne Code, ein paar Zeilen Code oder eine vollständige Integration in beide Richtungen.
- In jedem Tarif
REST-API
Lesen Sie Anrufe mit Transkripten und Aufnahmen, Termine, Kontakte und Aufgaben. Legen Sie Kontakte an und ändern Sie sie, stornieren oder verschieben Sie Termine und schließen Sie Aufgaben aus Ihrem eigenen System.
- In jedem Tarif
Webhooks
Signierte JSON-Ereignisse in Echtzeit: Anruf begonnen oder beendet, Termin gebucht, storniert oder verschoben, neuer Kontakt, Rückrufaufgabe.
- In jedem Tarif
Live-Daten über MCP
Verbinden Sie Ihren MCP-Server, und der Assistent prüft während des Anrufs Lagerbestand, Verfügbarkeit oder Bestellstatus.
- Ab Office
Sprach-Widget für die Website
Ein Script-Tag fügt eine „Mit uns sprechen“-Schaltfläche hinzu. Besucher sprechen im Browser mit Ihrem Assistenten.
- In jedem Tarif
Telefon-Onboarding
Übergeben Sie neue Registrierungen aus Ihrem System, und der Assistent ruft die Personen an, um ihnen beim Start zu helfen.
- In jedem Tarif
Kalender & CRM
Google Kalender für Buchungen, HubSpot und Pipedrive für Kontakte, Anrufe, Meetings und Aufgaben.
- In jedem Tarif
Zapier, Make, n8n
No-Code-Abläufe auf Basis von Webhooks und API: Google Sheets, Slack, E-Mail, jedes CRM.
Schnellstart
Ihre erste Anfrage in zwei Minuten
- 1
API-Schlüssel erstellen
Gehen Sie im Dashboard zu Entwickler → API-Schlüssel, benennen Sie den Schlüssel und wählen Sie seine Berechtigungen. Der Schlüssel (tf_live_…) wird nur einmal angezeigt – speichern Sie ihn sofort in Ihrem Secrets-Manager.
- 2
Verbindung testen
Rufen Sie GET /me auf. Das funktioniert mit jedem gültigen Schlüssel und liefert Ihr Konto, die Berechtigungen des Schlüssels und die Limits Ihres Tarifs.
- 3
Daten lesen oder Ereignisse abonnieren
Listen Sie Anrufe, Termine und Kontakte auf oder fügen Sie unter Entwickler → Webhooks einen Endpunkt hinzu, um Ereignisse sofort zu erhalten.
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
Anrufe, Termine und Kontakte über HTTPS
JSON rein, JSON raus. Jede Anfrage wird mit einem API-Schlüssel authentifiziert und durch Ihren Tarif begrenzt. Anfragen sehen Sie im Dashboard (Methode, Pfad, Status und Zeit – nie Inhalte von Anfragen oder Antworten).
Authentifizierung
Erstellen Sie unter API-Schlüssel einen Schlüssel und senden Sie ihn als Bearer-Token. Schlüssel beginnen mit tf_live_ und gehören zu genau einem Konto.
Authorization: Bearer tf_live_…Paginierung
Listen-Endpunkte liefern bis zu limit Einträge (1–100, Standard 25) in stabiler Reihenfolge. Ist has_more true, übergeben Sie next_cursor unverändert als cursor (mit denselben Filtern), um die nächste Seite abzurufen. Ein ungültiger Cursor ergibt 400 invalid_request.
Fehler
Fehler werden mit Standard-HTTP-Statuscodes und einem JSON-Body mit Typ und verständlicher Fehlermeldung zurückgegeben.
{ "error": { "type": "…", "message": "…" } }Endpunkte
- GET
/meBerechtigung: jeder SchlüsselAktuelles Konto abrufen
Gibt das Konto zurück, zu dem der API-Schlüssel gehört. Praktisch als Verbindungstest.
- GET
/assistantsBerechtigung: jeder SchlüsselAssistenten auflisten
Ihre Assistenten mit Sprache, Status und zugewiesenen Telefonnummern. Die id können Sie als assistant_id beim Filtern von Anrufen verwenden.
- GET
/assistants/{id}Berechtigung: jeder SchlüsselAssistenten abrufen
Ein Assistent mit Sprache, Status und Telefonnummern. Mit dem Scope read:assistants (oder write:assistants) auch der Prompt: instructions, greeting, company_profile und updated_at.
- PATCH
/assistants/{id}Berechtigung: write:assistantsPrompt des Assistenten ändern
Ändert Anweisungen, Begrüßung oder Unternehmensprofil. Weggelassene Felder bleiben unverändert. Gleiche Limits wie im Dashboard: Anweisungen bis 20.000 Zeichen, Unternehmensprofil bis 10.000, Begrüßung 5–600 Zeichen und sie muss sagen, dass der Anrufer mit einer KI spricht (sonst 400 mit code ai_disclosure). Jede Änderung landet im Versionsverlauf des Assistenten (Quelle API) und gilt ab dem nächsten Anruf. Die Ehrlichkeitsregeln der Plattform (KI-Hinweis, Aufzeichnungshinweis) gelten immer zusätzlich.
- GET
/phone-numbersBerechtigung: jeder SchlüsselTelefonnummern auflisten
Die Telefonnummern Ihres Kontos mit Land, Art, Status und dem Assistenten, der sie annimmt.
- GET
/callsBerechtigung: read:callsAnrufe auflisten
Anrufe, neueste zuerst, ohne Transkripte. Demo-Anrufe sind nie enthalten.
- GET
/calls/{id}Berechtigung: read:callsEinen Anruf abrufen
Ein einzelner Anruf mit vollständigem Transkript und den währenddessen gebuchten Terminen.
- GET
/calls/{id}/recordingBerechtigung: read:callsAnrufaufnahme abrufen
Signierte Links (1 Stunde gültig) zum Abspielen und Herunterladen der MP3-Aufnahme (Stereo: Anrufer links, Assistent rechts). Eine Aufnahme, die älter als der Aufnahmeverlauf Ihres Tarifs ist, ergibt 403 plan_required mit recording_history_days; ein Anruf ohne Aufnahme ergibt 404.
- GET
/appointmentsBerechtigung: read:appointmentsTermine auflisten
Anstehende Termine, sortiert nach Beginn (standardmäßig ab jetzt).
- GET
/appointments/{id}Berechtigung: read:appointmentsTermin abrufen
Ein Termin Ihres Kontos.
- POST
/appointments/{id}/cancelBerechtigung: write:appointmentsTermin stornieren
Storniert einen anstehenden Termin, entfernt ihn aus dem verbundenen Google--Kalender und sendet den Webhook appointment.canceled. calendar_sync zeigt, ob der Kalender aktualisiert wurde.
- POST
/appointments/{id}/rescheduleBerechtigung: write:appointmentsTermin verschieben
Verschiebt einen anstehenden Termin auf eine neue Zeit. Antwortet mit 409, wenn die Zeit mit einem anderen Termin oder einer belegten Zeit im verbundenen Kalender kollidiert. Ohne ends_at bleibt die Dauer gleich. Sendet den Webhook appointment.rescheduled.
- GET
/contactsBerechtigung: read:contactsKontakte auflisten
Ihre Anrufer, neueste zuerst. Der Assistent erinnert sich über Anrufe hinweg an sie.
- GET
/contacts/{id}Berechtigung: read:contactsKontakt abrufen
Ein Kontakt mit Name, E-Mail, Tags, Notizen und der Herkunft von Name und E-Mail (call, system oder manual).
- POST
/contactsBerechtigung: write:contactsKontakt anlegen
Legt einen Kontakt an, z. B. aus Ihrem CRM, damit der Assistent den Namen des Anrufers kennt. Über die API gespeicherte Namen und E-Mails gelten als manual und werden vom Assistenten nie überschrieben. Gibt 409 mit contact_id zurück, wenn die Nummer bereits existiert. Sendet den Webhook contact.created.
- PATCH
/contacts/{id}Berechtigung: write:contactsKontakt ändern
Ändert Name, E-Mail, Notizen oder die Sperre. Weggelassene Felder bleiben unverändert, null leert ein Feld. Ein geänderter Name oder eine geänderte E-Mail gilt als manual und wird vom Assistenten nicht überschrieben. Die Telefonnummer kann nicht geändert werden.
- GET
/tasksBerechtigung: read:tasksAufgaben auflisten
Rückrufwünsche, Nachrichten und andere Aufgaben aus Anrufen, neueste zuerst.
- GET
/tasks/{id}Berechtigung: read:tasksAufgabe abrufen
Eine Aufgabe Ihres Kontos.
- PATCH
/tasks/{id}Berechtigung: write:tasksAufgabe ändern
Setzt den Status einer Aufgabe auf open, done oder dismissed, z. B. nachdem Ihr Team den Kunden zurückgerufen hat.
- POST
/calls/outboundBerechtigung: write:callsAusgehenden Anruf erstellen
Ihr Assistent ruft eine Person von Ihrer Firmennummer mit dem von Ihnen vorgegebenen Ziel an – wie ein Schritt einer Onboarding-Sequenz. Der Anruf erfolgt sofort oder zu call_at, immer innerhalb der Anrufzeiten (Mo–Sa, 9:00–20:00 Uhr in der Zeitzone Ihres Kontos; außerhalb davon wird er auf den nächsten erlaubten Zeitpunkt verschoben, den call_at anzeigt). Nimmt niemand ab, versucht er es bis zu 3-mal im Abstand von 2 Stunden. Gibt 202 mit dem Anruf im Status scheduled zurück. Fehler: 400 consent_missing, invalid_phone, number_not_callable, international oder invalid_call_at; 409 opted_out (die Person möchte nicht angerufen werden), already_scheduled (mit existing_id), assistant_not_live, over_quota oder no_outbound_number; 429 daily_outbound_limit mit limit (standardmäßig 200 Anrufe pro 24 Stunden). Das Ergebnis kommt in den Webhooks call.completed und outbound_call.finished mit Ihrer external_id.
- GET
/calls/outbound/{id}Berechtigung: write:callsAusgehenden Anruf abrufen
Status eines geplanten ausgehenden Anrufs: scheduled, dialing, done (mit call_id des Gesprächs), failed (last_error, z. B. no_answer nach allen Versuchen) oder canceled. Funktioniert auch mit der callback_id aus Webhooks für Onboarding- und Rückrufanrufe.
- DELETE
/calls/outbound/{id}Berechtigung: write:callsAusgehenden Anruf stornieren
Storniert einen über die API erstellten ausgehenden Anruf, der noch geplant ist. Kann gefahrlos wiederholt werden (bereits storniert ergibt 200). Gibt 409 not_cancelable zurück, wenn der Anruf gerade gewählt wird oder beendet ist, sowie bei Onboarding-Anrufen (entfernen Sie die Person stattdessen mit POST /api/outreach/unenroll).
- GET
/messagesBerechtigung: read:messagesSMS auflisten
Von Ihrem Konto gesendete SMS und Kundenantworten, neueste zuerst. phone ist die Nummer des Kunden (Empfänger oder Absender einer Antwort). generated_by: template, ai oder manual; charged_from: plan oder credits.
- POST
/messagesBerechtigung: write:messagesSMS senden
Sendet eine SMS an einen Ihrer Kontakte oder Anrufer: eigener Text oder eine Vorlage Ihres Kontos mit vars (date, time, name, service, link, amount). Zählt zum SMS-Kontingent Ihres Pakets und zu zusätzlichen SMS. Fehler: 400 invalid_request (consent_missing, invalid_phone, too_long, missing_vars); 409 conflict (opted_out — der Kunde hat STOP geantwortet, over_quota, recipient_not_allowed — legen Sie zuerst den Kontakt an, blocked_destination); 429 rate_limited.
Berechtigungen
Jeder Schlüssel hat die beim Erstellen gewählten Scopes. Scopes lassen sich später nicht ändern – erstellen Sie bei Bedarf einen neuen Schlüssel. Fehlt dem Schlüssel der erforderliche Scope, antworten Endpunkte mit 403 insufficient_scope. GET /me, /assistants und /phone-numbers funktionieren mit jedem gültigen Schlüssel.
read:callsAnrufe, Zusammenfassungen und Transkripte lesenread:appointmentsTermine lesenwrite:appointmentsTermine stornieren und verschiebenread:contactsKontakte lesenwrite:contactsKontakte anlegen und ändernread:tasksAufgaben und Rückrufwünsche lesenwrite:tasksStatus von Aufgaben ändernwrite:callsAusgehende Anrufe starten, abfragen und stornierenread:messagesSMS und Kundenantworten lesenwrite:messagesSMS an Ihre Kontakte sendenread:assistantsPrompt des Assistenten lesen (Anweisungen, Begrüßung, Unternehmensprofil)write:assistantsPrompt des Assistenten ändern
Fehler
400invalid_request – ein Parameter fehlt oder ist ungültig401unauthorized – API-Schlüssel fehlt, ist ungültig oder wurde widerrufen403insufficient_scope – dem Schlüssel fehlt der Scope des Endpunkts; plan_required – das Konto hat keinen aktiven Tarif oder die Aufnahme ist älter als der Aufnahmeverlauf des Tarifs404not_found – das Objekt existiert in Ihrem Konto nicht409conflict – die Zeit ist bereits belegt, der Termin kann nicht geändert werden, ein Kontakt mit dieser Telefonnummer existiert bereits oder ein ausgehender Anruf ist nicht möglich (code: opted_out, already_scheduled, assistant_not_live, over_quota, no_outbound_number, not_cancelable)429rate_limited – zu viele Anfragen; nach der Zeit im Retry-After-Header erneut versuchen500server_error – auf unserer Seite ist etwas schiefgelaufen503service_unavailable – vorübergehendes Problem; nach der Zeit im Retry-After-Header erneut versuchen
Die vollständige API-Referenz mit Parametern und Beispielantworten finden Sie im Dashboard unter Entwickler → API-Referenz.
Webhooks
Ereignisse in Echtzeit an Ihren Server
Fügen Sie im Dashboard einen https-Endpunkt hinzu, wählen Sie die Ereignisse, und wir senden einen signierten JSON-Umschlag per POST, sobald etwas passiert. „Test senden“ liefert ein Beispiel Ihres ersten Ereignisses – praktisch für die Feldzuordnung in Zapier oder Make.
Ereignisse
call.completedAnruf abgeschlossenZusammenfassung, Ergebnis, Stimmung, Transkript, Kontakt und Buchungen; bei ausgehenden Anrufen auch die Sequenz oder API-Anfrage (callback_id, external_id, outreach).
call.startedAnruf begonnenEin Anruf wurde angenommen (eingehend, ausgehend oder über das Web).
appointment.bookedTermin gebuchtDer Assistent hat einen Termin gebucht.
appointment.canceledTermin storniertEin Termin wurde telefonisch oder über die API storniert.
appointment.rescheduledTermin verschobenEin Termin wurde auf eine neue Zeit verschoben (inklusive vorheriger Zeit).
task.createdRückruf / Aufgabe erstelltEin Anrufer hat um Rückruf gebeten oder eine Aufgabe hinterlassen.
contact.createdNeuer KontaktEin Erstanrufer wurde als Kontakt gespeichert.
outbound_call.finishedAusgehender Anruf beendetEin geplanter ausgehender Anruf (API, Onboarding-Sequenz oder Rückruf) ist abgeschlossen oder fehlgeschlagen – auch wenn nach allen Versuchen niemand abgenommen hat.
Header jeder Zustellung
Telofia-SignatureSignatur: t=<Unix-Zeit>,v1=<HMAC-SHA256 hex>Telofia-EventEreignistyp, z. B. appointment.bookedTelofia-DeliveryEindeutige Zustellungs-IDUser-AgentTelofia-Webhooks/1.0 · Kennzeichnet unseren Webhook-Absender
Zustellung und Wiederholungen
- Antworten Sie innerhalb von 10 Sekunden mit einem beliebigen 2xx-Status. Weiterleitungen werden nicht verfolgt.
- Fehlgeschlagene Zustellungen wiederholen wir nach 1 Min., 5 Min., 30 Min., 2 Std., 6 Std. und 12 Std. (7 Versuche, ca. 21 Stunden).
- Dasselbe Ereignis kann mehrfach ankommen. Überspringen Sie Duplikate anhand der Ereignis-id.
- Nur öffentliche https-Adressen sind erlaubt. Nach 50 Fehlschlägen in Folge wird ein Endpunkt pausiert, und das Dashboard nennt den Grund.
- Das Dashboard führt ein Zustellprotokoll mit Statuscodes und Antworten und lässt Sie Zustellungen manuell wiederholen.
{
"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"
}
]
}
}Signaturen prüfen. Jede Zustellung enthält einen Header Telofia-Signature im Format t=timestamp,v1=signature. Berechnen Sie mit Ihrem Signaturschlüssel den HMAC-SHA256 von „timestamp.raw_body“ und vergleichen Sie ihn mit v1. Lehnen Sie Zeitstempel ab, die älter als 5 Minuten sind.
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);Stabile Werte
Diese Werte in Anrufen (API und call.*-Webhooks) und in ausgehenden Anrufen (outbound_call.finished, /calls/outbound) sind ein stabiler Vertrag: Wir können neue Werte hinzufügen, ändern oder entfernen diese aber nicht. Unbeantwortete, besetzte oder abgelehnte ausgehende Versuche erzeugen nie einen Anruf, daher gibt es dafür kein call.completed: Nach dem letzten Versuch erhalten Sie outbound_call.finished mit last_error no_answer. Ein Anruf, den die Mailbox annimmt, gilt als angenommen (meist ein kurzer Anruf mit outcome other).
Anrufstatus
in_progress- Der Anruf läuft.
completed- Die Person hat mit dem Assistenten gesprochen.
missed- Niemand hat gesprochen oder der Assistent konnte den Anruf nicht annehmen (siehe end_reason).
failed- Der Anruf konnte wegen eines Fehlers nicht bearbeitet werden.
Anrufergebnis
booked- Ein Termin wurde gebucht.
transferred- Der Anruf wurde an eine Person weitergeleitet.
message- Eine Nachricht, ein Rückrufwunsch, eine Bestellung oder ein Folgeauftrag wurde aufgenommen.
info- Die Person hat Informationen erhalten; mehr war nicht nötig.
spam- Spam, Telefonwerbung oder eine gesperrte Nummer.
other- Alles andere, z. B. ein sehr kurzer Anruf oder die Mailbox.
Grund für das Anrufende (end_reason)
caller_hangup- Die andere Person hat aufgelegt.
caller_hangup_greeting- Ausgehender Anruf: Die Person hat während oder direkt nach der Begrüßung aufgelegt, ohne ein Wort zu sagen (Gespräch bis 45 s). caller_spoke ist dann false.
agent_hangup- Der Assistent hat den Anruf nach der Verabschiedung beendet.
transferred- Der Anruf wurde an eine Person weitergeleitet.
silence- Nach langer Stille beendet.
max_duration- Die maximale Anrufdauer wurde erreicht.
spam- Als Spam beendet.
blocked- Die Nummer ist in den Kontakten gesperrt.
busy- Alle Leitungen des Kontos waren belegt.
over_quota- Keine Gesprächsminuten mehr.
trial_expired- Der kostenlose Testzeitraum ist abgelaufen.
inactive- Kein aktiver Tarif.
assistant_not_live- Der Assistent ist pausiert.
no_assistant- Kein Assistent nimmt diese Nummer an.
ai_budget- Wegen eines vorübergehenden Limits ohne KI bearbeitet; das Team wurde um Rückruf gebeten.
error- Ein technischer Fehler hat den Anruf beendet.
Status des ausgehenden Anrufs
scheduled- Wartet auf seinen Zeitpunkt (oder auf den nächsten Versuch).
dialing- Wird gerade angerufen.
done- Angenommen; call_id ist das Gespräch.
failed- Nach allen Versuchen nicht angenommen oder konnte nicht durchgeführt werden (siehe last_error).
canceled- Storniert (API, Person aus der Sequenz entfernt oder Sequenz deaktiviert).
last_error des ausgehenden Anrufs
no_answer- Niemand hat abgenommen (auch besetzt oder abgelehnt).
failed- Der Assistent war zum Anrufzeitpunkt nicht verfügbar (Tarif, Minuten oder Pause).
no_result- Kein Ergebnis des Anrufs innerhalb von 15 Minuten.
expired- Der Anruf war mehr als 2 Stunden verspätet und wurde daher nicht durchgeführt.
outside_window- Auf die nächsten Anrufzeiten verschoben (status bleibt scheduled).
dial_error- Das Telefonnetz hat den Anruf abgelehnt (gesendet als dial_<reason>).
over_quota- Keine Gesprächsminuten mehr.
inactive- Kein aktiver Tarif.
assistant_not_live- Der Assistent ist pausiert.
no_outbound_number- Keine Firmennummer zum Anrufen.
canceled_by_api- Storniert mit DELETE /calls/outbound/{id}.
unenrolled- Die Person wurde aus der Sequenz entfernt (POST /api/outreach/unenroll).
opt_out- Die Person möchte nicht angerufen werden.
replaced- Durch einen neueren Rückruf an dieselbe Nummer ersetzt.
Live-Daten über MCP
Der Assistent sieht während des Anrufs in Ihrem System nach
Verbinden Sie einen entfernten MCP-Server (Model Context Protocol), etwa Ihren Shop-Katalog, Lagerbestand, Ihr Buchungs- oder Bestellsystem. Sie legen genau fest, welche Tools und Ressourcen (nur lesend) der Assistent nutzen darf.
Vorab lernen
Für Inhalte, die sich selten ändern, etwa Preislisten, Produktbeschreibungen oder Richtlinien. Ausgewählte Ressourcen und lesende Tools werden alle 1 bis 168 Stunden oder auf Knopfdruck in das Wissen des Assistenten übernommen. Keine Verzögerung im Anruf.
Live im Anruf prüfen
Für Dinge, die sich ändern: Lagerbestand, Verfügbarkeit, Bestellstatus. Der Assistent ruft das Tool während des Gesprächs mit einem kurzen „Einen Moment, ich sehe nach“ auf. Antwortet Ihr Server nicht innerhalb von etwa 2,5 Sekunden, sagt er, dass er es gerade nicht bestätigen kann, und bietet einen Rückruf Ihres Teams an.
Was Sie brauchen
- Einen per https erreichbaren MCP-Server (Streamable HTTP, z. B. https://mcp.example.com/mcp; ältere HTTP+SSE-Server werden automatisch erkannt).
- Authentifizierung: keine, ein Bearer-Token oder ein eigener Header wie X-API-Key. Geheimnisse werden verschlüsselt (AES-256-GCM) und nie an den Browser gesendet.
- Nur lesende Tools. Als destruktiv markierte Tools sind gesperrt; bei Tools ohne Kennzeichnung „nur lesend“ bestätigen Sie, dass sie nur Daten lesen.
- Bis zu 5 Quellen und 50 Live-Tools pro Assistent. Live-Abfragen sollten in etwa 2 Sekunden antworten.
- Optional für das Website-Widget: Wählen Sie bei einem Tool „Zu Beginn des Gesprächs“ das Argument, das das Identitätstoken des angemeldeten Besuchers erhält (siehe Sprach-Widget für die Website).
Tool-Argumente werden gegen das Schema des Tools geprüft, Ergebnisse gekürzt und strikt als Daten behandelt (nie als Anweisungen), und jede Abfrage wird ohne Anruferdaten protokolliert. Private und interne Adressen werden abgelehnt.
Sprach-Widget für die Website
Ihr Assistent auf Ihrer Website – mit einer Zeile Code
Besucher klicken auf eine Schaltfläche und sprechen direkt im Browser mit demselben Assistenten, der Ihr Telefon annimmt. Web-Anrufe nutzen Ihre Tarifminuten, ohne Telefoniekosten.
Ab Office
- Ca. 14 KB, keine Abhängigkeiten, im Shadow DOM isoliert – die Styles Ihrer Website können es nicht beschädigen.
- Spricht die Sprache Ihrer Seite (<html lang>) oder die per data-lang="de" festgelegte.
- Live-Untertitel; ist das Mikrofon blockiert, können Besucher tippen, und der Assistent antwortet per Sprache.
- Beschränken Sie das Widget auf Ihre Domains – dann startet es nur auf diesen Websites.
<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? Erlauben Sie script-src https://telofia.com https://cdn.jsdelivr.net, connect-src https://telofia.com wss://*.livekit.cloud https://*.livekit.cloud und media-src blob:.
Angemeldete Nutzer (Identitätstoken)
Sind Besucher auf Ihrer Website angemeldet, weiß der Assistent, wer spricht, ohne nach der E-Mail-Adresse zu fragen.
- Ihr Server stellt dem angemeldeten Nutzer ein kurzlebiges, signiertes Token aus (z. B. HMAC, 15 Minuten gültig). Legen Sie nie eine E-Mail-Adresse oder Nutzer-ID in den Browser: Jeder könnte in der Konsole eine fremde eintragen.
- Rufen Sie TelofiaWidget.identify({ token }) jederzeit auf; ein späterer Aufruf ersetzt das Token, identify(null) löscht es. Bevor widget.js geladen ist, setzen Sie window.TelofiaIdentity = { token, expires_at }: Es wird beim Gesprächsstart gelesen, ein abgelaufenes Token wird übersprungen.
- Wählen Sie im Dashboard beim MCP-Tool „Zu Beginn des Gesprächs“ das Argument für das Identitätstoken. Telofia übergibt das Token unverändert nur an dieses Argument, speichert oder zeigt es nie und hält es aus Transkripten, Zusammenfassungen, Webhooks und Logs heraus; der Assistent sieht es nicht.
- 8 bis 400 Zeichen: Buchstaben, Ziffern und . _ ~ + / = - (z. B. base64url). Erneuern Sie es vor Ablauf, etwa alle 10 Minuten. Ihr MCP-Server prüft es; ein ungültiges oder abgelaufenes Token sollte dasselbe Ergebnis liefern wie kein Token. Anrufe und Gespräche ohne Token funktionieren wie bisher.
// 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);Telefon-Onboarding
Neue Kunden erhalten automatisch einen Willkommensanruf
Registriert sich jemand in Ihrem System, übergeben Sie die Person an eine Anrufsequenz. Der Assistent ruft von Ihrer Firmennummer an, hilft beim Start und meldet sich einige Tage später noch einmal. Angerufen wird innerhalb der Anrufzeiten der Sequenz: standardmäßig Montag bis Samstag von 9:00 bis 20:00 Uhr in Ihrer Zeitzone oder an den Tagen und zu den Uhrzeiten, die Sie festlegen.
POST/api/outreach/enroll
Authentifizierung mit dem Sequenzschlüssel (tlo_…) aus dem Dashboard, als Bearer-Token oder im Header X-Telofia-Key. Jede Sequenz hat einen eigenen Schlüssel.
Request-Body
- Telefonnummer, am besten im Format E.164. Nationale Nummern werden mit der Landesvorwahl Ihres Kontos gelesen.
phonestringerforderlich - Muss true sein: Die Person hat einer telefonischen Kontaktaufnahme zugestimmt, z. B. in Ihrem Registrierungsformular.
consenttrueerforderlich - Name, bis zu 120 Zeichen.
namestringoptional - E-Mail-Adresse.
emailstringoptional - Was der Assistent über diese Person wissen soll, bis zu 1.000 Zeichen (z. B. der gewählte Tarif).
contextstringoptional - Die ID der Person in Ihrem System (Text oder Zahl). Dieselbe ID wird nur einmal eingetragen.
external_idstring | numberoptional
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
}
}Antworten
- 201: eingetragen. enrollment_id, external_id, status, next_call_at (Zeitpunkt des ersten Anrufs), intro_sms_at (wann die SMS vor dem ersten Anruf gesendet wird, oder null) und limits { per_day, remaining_today }
- 200: bereits eingetragen (gleiche external_id oder diese Nummer läuft bereits in der Sequenz), mit duplicate: true
- 400: invalid_request, invalid_phone oder consent_required
- 401: invalid_key
- 409: sequence_disabled, consent_missing oder opted_out (die Person möchte keine Anrufe)
- 429: limit – das Tageslimit für Eintragungen in die Sequenz ist erreicht (500 neue Personen pro 24 Stunden; der Body enthält limit). rate_limited: mehr als 120 Anfragen pro Minute mit diesem Schlüssel (Retry-After-Header)
Anrufzeiten: Legen Sie im Dashboard für jede Sequenz die Tage und einen Zeitraum zwischen 7:00 und 21:00 Uhr fest. Nimmt niemand ab, versucht es der Assistent bis zu 3-mal im Abstand von 2 Stunden innerhalb dieser Zeiten.
SMS vor dem Anruf: Ist sie für die Sequenz aktiviert (Dashboard), erhält die Person eine kurze Nachricht von der Nummer, von der der Assistent anrufen wird — standardmäßig 20 Minuten vor dem ersten Anruf (5–120 Min.). Würde der erste Anruf früher stattfinden, wird er verschoben, damit die SMS immer zuerst rausgeht, innerhalb der Anrufzeiten. Eine fehlgeschlagene SMS (die Person hat mit STOP geantwortet, das SMS-Kontingent ist aufgebraucht) stoppt den Anruf nie. Jede SMS wird auf Ihr SMS-Kontingent angerechnet und erscheint im Dashboard unter SMS.
Begrüßung: Im Dashboard legen Sie für jeden Schritt (oder als Standard für die ganze Sequenz) den ersten Satz des Anrufs fest — einen festen Text mit den Variablen {first_name}, {first_name_vocative} (polnischer Vokativ, z. B. Krystianie), {assistant_name}, Telofia (Firmenname im Gespräch aus den Einstellungen des Assistenten) und {name}, mit eigener Variante für Anmeldungen ohne Namen — oder einen Modus, in dem der Assistent den ersten Satz aus name, context, dem Ziel des Schritts und dem Ergebnis der Tools „Zu Beginn des Gesprächs“ formuliert. Dass ein KI-Assistent anruft und das Gespräch aufgezeichnet wird, ergänzen wir im ersten Satz immer, wenn der Text es nicht sagt.
Person aus der Sequenz entfernen
POST/api/outreach/unenroll
Braucht jemand die Anrufe nicht mehr (z. B. nach dem ersten Kauf oder nach einer Kündigung), beenden Sie die Sequenz mit demselben Schlüssel. Geplante Anrufe werden sofort storniert. Ein laufender Anruf wird nicht unterbrochen, danach folgen aber keine weiteren Anrufe.
- Genau eines von: external_id (wie beim Eintragen gesendet), enrollment_id (aus der Antwort auf das Eintragen) oder phone.
external_id | enrollment_id | phoneerforderlich - Optionaler Grund, bis zu 200 Zeichen, wird im Audit-Log Ihres Kontos gespeichert.
reasonoptional
- 200: gestoppt (changed: true) oder bereits beendet: status stopped, completed oder failed (changed: false). Kann gefahrlos wiederholt werden.
- 400: invalid_request (keine oder mehr als eine Kennung) oder invalid_phone
- 401: invalid_key
- 404: not_found – keine solche Eintragung in dieser Sequenz
- 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
}Anrufergebnisse in Webhooks
call.started und call.completed enthalten für Anrufe aus einer Sequenz callback_id, external_id und outreach { sequence_id, enrollment_id, external_id, step } (bei eingehenden Anrufen alle null). Unbeantwortete Versuche erzeugen keinen Anruf, daher gibt es dafür kein call.completed: Nach dem letzten Versuch erhalten Sie outbound_call.finished mit status failed und last_error no_answer. Wiederholungen eines Schritts zählen als ein Ergebnis. call.completed enthält außerdem caller_spoke und caller_words (wie viele Wörter die Person gesagt hat). Hat jemand abgenommen und während oder direkt nach der Begrüßung wortlos aufgelegt, ist 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
}Kalender, CRM und Automatisierung
Fertige Integrationen, ganz ohne Code
Google Kalender
Der Assistent prüft die echte Verfügbarkeit und bucht, verschiebt und storniert Termine in Ihrem Kalender. Er liest nur belegte Zeiten, nie Termintitel.
HubSpot
Jeder Anruf wird beim Kontakt protokolliert, Buchungen werden zu Meetings und Rückrufe zu Aufgaben. Optional werden Kontakte und Deals angelegt.
Pipedrive
Anrufe als Aktivitäten oder Notizen, Buchungen als Meetings, Rückrufe als Aktivitäten. Optional werden Personen, Deals oder Leads angelegt.
Zapier, Make und n8n
Schritt-für-Schritt-Anleitungen im Dashboard. Sie basieren auf Webhooks und der API, die in jedem Tarif enthalten sind.
Tarife und Limits
Welchen Tarif Sie brauchen
Limits pro Konto: Anfragen pro Minute (auch pro Schlüssel, gleitendes Fenster) und pro Tag (UTC). Oberhalb eines Limits erhalten Sie 429 rate_limited mit einem Retry-After-Header.
Sprach-Widget für die Website: ab dem Tarif Office.
Sie brauchen höhere Limits? Schreiben Sie uns – wir können sie für Ihr Konto erhöhen.
Early Access: Gehören Sie zu den ersten Unternehmen, deren Telefon eine KI annimmt.
Bereit zum Verbinden?
Starten Sie die kostenlose Testphase, erstellen Sie einen Test-API-Schlüssel und senden Sie in wenigen Minuten Ihre erste Anfrage. Die API ist in jedem Tarif enthalten, ab Line.