Zum Inhalt springen

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.

Basis-URL der API
https://telofia.com/api/v1
curl · Schlüssel testen
curl https://telofia.com/api/v1/me \
  -H "Authorization: Bearer tf_live_…"
200 OK
{
  "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
  }
}

Schnellstart

Ihre erste Anfrage in zwei Minuten

  1. 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. 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. 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 · Schlüssel testen
curl https://telofia.com/api/v1/me \
  -H "Authorization: Bearer tf_live_…"
JavaScript: alle Anrufe mit Buchung, Seite für Seite
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üssel

    Aktuelles Konto abrufen

    Gibt das Konto zurück, zu dem der API-Schlüssel gehört. Praktisch als Verbindungstest.

  • GET/assistantsBerechtigung: jeder Schlüssel

    Assistenten 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üssel

    Assistenten 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:assistants

    Prompt 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üssel

    Telefonnummern auflisten

    Die Telefonnummern Ihres Kontos mit Land, Art, Status und dem Assistenten, der sie annimmt.

  • GET/callsBerechtigung: read:calls

    Anrufe auflisten

    Anrufe, neueste zuerst, ohne Transkripte. Demo-Anrufe sind nie enthalten.

  • GET/calls/{id}Berechtigung: read:calls

    Einen Anruf abrufen

    Ein einzelner Anruf mit vollständigem Transkript und den währenddessen gebuchten Terminen.

  • GET/calls/{id}/recordingBerechtigung: read:calls

    Anrufaufnahme 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:appointments

    Termine auflisten

    Anstehende Termine, sortiert nach Beginn (standardmäßig ab jetzt).

  • GET/appointments/{id}Berechtigung: read:appointments

    Termin abrufen

    Ein Termin Ihres Kontos.

  • POST/appointments/{id}/cancelBerechtigung: write:appointments

    Termin 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:appointments

    Termin 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:contacts

    Kontakte auflisten

    Ihre Anrufer, neueste zuerst. Der Assistent erinnert sich über Anrufe hinweg an sie.

  • GET/contacts/{id}Berechtigung: read:contacts

    Kontakt abrufen

    Ein Kontakt mit Name, E-Mail, Tags, Notizen und der Herkunft von Name und E-Mail (call, system oder manual).

  • POST/contactsBerechtigung: write:contacts

    Kontakt 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:contacts

    Kontakt ä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:tasks

    Aufgaben auflisten

    Rückrufwünsche, Nachrichten und andere Aufgaben aus Anrufen, neueste zuerst.

  • GET/tasks/{id}Berechtigung: read:tasks

    Aufgabe abrufen

    Eine Aufgabe Ihres Kontos.

  • PATCH/tasks/{id}Berechtigung: write:tasks

    Aufgabe ä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:calls

    Ausgehenden 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:calls

    Ausgehenden 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:calls

    Ausgehenden 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:messages

    SMS 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:messages

    SMS 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 lesen
  • read:appointmentsTermine lesen
  • write:appointmentsTermine stornieren und verschieben
  • read:contactsKontakte lesen
  • write:contactsKontakte anlegen und ändern
  • read:tasksAufgaben und Rückrufwünsche lesen
  • write:tasksStatus von Aufgaben ändern
  • write:callsAusgehende Anrufe starten, abfragen und stornieren
  • read:messagesSMS und Kundenantworten lesen
  • write:messagesSMS an Ihre Kontakte senden
  • read:assistantsPrompt des Assistenten lesen (Anweisungen, Begrüßung, Unternehmensprofil)
  • write:assistantsPrompt des Assistenten ändern

Fehler

  • 400invalid_request – ein Parameter fehlt oder ist ungültig
  • 401unauthorized – API-Schlüssel fehlt, ist ungültig oder wurde widerrufen
  • 403insufficient_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 Tarifs
  • 404not_found – das Objekt existiert in Ihrem Konto nicht
  • 409conflict – 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 versuchen
  • 500server_error – auf unserer Seite ist etwas schiefgelaufen
  • 503service_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 abgeschlossen

    Zusammenfassung, Ergebnis, Stimmung, Transkript, Kontakt und Buchungen; bei ausgehenden Anrufen auch die Sequenz oder API-Anfrage (callback_id, external_id, outreach).

  • call.startedAnruf begonnen

    Ein Anruf wurde angenommen (eingehend, ausgehend oder über das Web).

  • appointment.bookedTermin gebucht

    Der Assistent hat einen Termin gebucht.

  • appointment.canceledTermin storniert

    Ein Termin wurde telefonisch oder über die API storniert.

  • appointment.rescheduledTermin verschoben

    Ein Termin wurde auf eine neue Zeit verschoben (inklusive vorheriger Zeit).

  • task.createdRückruf / Aufgabe erstellt

    Ein Anrufer hat um Rückruf gebeten oder eine Aufgabe hinterlassen.

  • contact.createdNeuer Kontakt

    Ein Erstanrufer wurde als Kontakt gespeichert.

  • outbound_call.finishedAusgehender Anruf beendet

    Ein 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.booked
  • Telofia-DeliveryEindeutige Zustellungs-ID
  • User-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.
Beispiel-Zustellung: call.completed
{
  "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.

Node.js · Signatur prüfen
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);
}
Python · Signatur prüfen
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", ""))
Beispiel-Empfänger (Express)
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.
Einbettungscode (Ihren Schlüssel finden Sie im Dashboard)
<script src="https://telofia.com/widget.js" data-key="tfw_…" async></script>
JavaScript-API
// 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 page

Strenge 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.
Identitätstoken
// 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

  • phonestringerforderlich
    Telefonnummer, am besten im Format E.164. Nationale Nummern werden mit der Landesvorwahl Ihres Kontos gelesen.
  • consenttrueerforderlich
    Muss true sein: Die Person hat einer telefonischen Kontaktaufnahme zugestimmt, z. B. in Ihrem Registrierungsformular.
  • namestringoptional
    Name, bis zu 120 Zeichen.
  • emailstringoptional
    E-Mail-Adresse.
  • contextstringoptional
    Was der Assistent über diese Person wissen soll, bis zu 1.000 Zeichen (z. B. der gewählte Tarif).
  • external_idstring | numberoptional
    Die ID der Person in Ihrem System (Text oder Zahl). Dieselbe ID wird nur einmal eingetragen.
curl
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}'
201 Created
{
  "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.

  • external_id | enrollment_id | phoneerforderlich
    Genau eines von: external_id (wie beim Eintragen gesendet), enrollment_id (aus der Antwort auf das Eintragen) oder phone.
  • reasonoptional
    Optionaler Grund, bis zu 200 Zeichen, wird im Audit-Log Ihres Kontos gespeichert.
  • 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
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"}'
200 OK
{
  "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.

call.completed für einen Sequenzanruf
{
  "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.

PaketAnfragen / Min.Anfragen / TagAPI-SchlüsselWebhooks
Kostenlose Testphase (Testzugang)201.00011
Line302.00022
Desk6010.00055
Office18050.0001010
Network600200.0002520

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.