Telofy

Telofy is an AI phone receptionist for businesses in Germany, from sole traders to mid-sized companies. It answers calls on the existing phone number whenever nobody can pick up, clarifies the caller's request, books, reschedules or cancels appointments via cal.com (which also connects Google and Outlook calendars), takes orders and forwards urgent calls to a phone number based on rules. After every call, a ready-to-act summary appears in the portal and the business is notified by e-mail, Telegram or push. 30 voices, more than 20 languages, conversation templates for 62 professions, open API, signed webhooks and an MCP server. Callers are told they are speaking with an AI, no audio is stored and data is processed in the EU. Plans start at 79 EUR per month with annual billing.

เอกสาร

Telofy für Entwickler.

Schnittstelle, Webhooks und MCP: Anrufe, Anliegen und Termine holen, Ereignisse sicher empfangen, KI-Assistenten anbinden. Basis-Adresse https://api.telofy.app.

Überblick

Die Telofy-Schnittstelle richtet sich an Server, nicht an Browser. Sie ist für den Zugriff von Ihrer Software, Ihrer Agentur oder Ihrem KI-Assistenten gedacht. Browser-Zugriff (CORS) ist nicht vorgesehen.

Basis-Adressehttps://api.telofy.app
FormatJSON, UTF-8
AnmeldungAuthorization: Bearer tlfy_…
LesenAnrufe, Anliegen, Termine
SchreibenAusgehenden Anruf auslösen (ab Business)
Ratenlimit60 Anfragen pro Minute und Schlüssel
EreignisseWebhooks, signiert
KI-AssistentenMCP, Streamable HTTP unter /mcp

Eine OpenAPI-Beschreibung gibt es derzeit nicht. Diese Seite ist die Referenz. Sie gibt es auch als reinen Markdown-Text unter /entwickler.md.

Authentifizierung

Jede Anfrage trägt einen API-Schlüssel im Kopf-Feld Authorization.

Authorization: Bearer tlfy_IhrSchluessel
  • Einen Schlüssel erstellen Sie im Portal unter Integrationen > API-Schlüssel (Knopf „Schlüssel erstellen“). Er beginnt mit tlfy_ und wird nur einmal vollständig angezeigt. Bewahren Sie ihn wie ein Passwort auf.
  • Es sind bis zu drei aktive Schlüssel möglich. Ein widerrufener Schlüssel funktioniert nicht mehr.
  • Der Betrieb, auf den sich eine Anfrage bezieht, ergibt sich allein aus dem Schlüssel. Eine Angabe in der Anfrage kann ihn nicht ändern.
  • Es gibt keine Berechtigungsstufen. Derselbe Schlüssel liest und kann, ab dem Tarif Business, Anrufe auslösen. Geben Sie ihn deshalb nur an Systeme, denen Sie beides zutrauen.
  • Fehlt der Schlüssel, ist er falsch oder widerrufen, antwortet Telofy immer gleich:
{ "error": "unauthorized" }

Endpunkte

MethodePfadZweck
GET/api/v1/callsAnrufe, neueste zuerst
GET/api/v1/outcomesAnliegen aus Anrufen: Lead, Ticket, Termin, Auskunft, Bestellung, Sonstiges
GET/api/v1/appointmentsNur Anliegen vom Typ Termin
POST/api/v1/outbound-callsAusgehenden Anruf auslösen (ab Business)
POST/mcpMCP für KI-Assistenten

Anrufe abrufen

GET /api/v1/calls

curl -H "Authorization: Bearer tlfy_IhrSchluessel" "https://api.telofy.app/api/v1/calls?limit=2"
{
  "page": 1,
  "limit": 2,
  "calls": [
    {
      "id": "call_9f2a1b",
      "startedAt": "2026-07-14T10:15:00.000Z",
      "endedAt": "2026-07-14T10:19:30.000Z",
      "direction": "inbound",
      "status": "completed",
      "callerNumber": "+4930555099",
      "durationSeconds": 270,
      "summary": "Anrufer möchte einen Termin für nächsten Dienstag.",
      "outcomeType": "appointment",
      "employeeName": "Anna"
    },
    {
      "id": "call_3c7d2e",
      "startedAt": "2026-07-14T09:00:00.000Z",
      "endedAt": "2026-07-14T09:04:12.000Z",
      "direction": "inbound",
      "status": "completed",
      "callerNumber": "+4930555010",
      "durationSeconds": 252,
      "summary": "Anrufer fragt nach Öffnungszeiten am Samstag.",
      "outcomeType": "lead",
      "employeeName": "Anna"
    }
  ]
}
FeldBedeutung
idKennung des Anrufs
startedAt, endedAtBeginn und Ende, ISO 8601
directioninbound oder outbound
statusStatus des Anrufs, zum Beispiel completed
callerNumberRufnummer des Anrufers
durationSecondsDauer in Sekunden
summaryZusammenfassung des Gesprächs
outcomeTypeArt des Anliegens, falls eines entstand
employeeNameName des Mitarbeiters, der den Anruf geführt hat

Das Gesprächsprotokoll ist nicht Teil der Antwort. Es steht nur im Portal und nur, wenn Sie es dort eingeschaltet haben.

Anliegen abrufen

GET /api/v1/outcomes

curl -H "Authorization: Bearer tlfy_IhrSchluessel" "https://api.telofy.app/api/v1/outcomes?limit=2"
{
  "page": 1,
  "limit": 2,
  "outcomes": [
    {
      "id": "outcome_7b1a4c",
      "type": "appointment",
      "title": "Termin am Dienstag, 10:00 Uhr",
      "structuredData": {
        "requestedTime": "2026-07-21T10:00:00.000Z",
        "contactName": "Frau Beispiel"
      },
      "createdAt": "2026-07-14T10:19:35.000Z",
      "callId": "call_9f2a1b",
      "callerNumber": "+4930555099",
      "callStartedAt": "2026-07-14T10:15:00.000Z",
      "callSummary": "Anrufer möchte einen Termin für nächsten Dienstag.",
      "handlingStatus": "open"
    },
    {
      "id": "outcome_2e9f31",
      "type": "lead",
      "title": "Interesse an Beratungstermin",
      "structuredData": {
        "interesse": "Erstberatung"
      },
      "createdAt": "2026-07-14T09:04:20.000Z",
      "callId": "call_3c7d2e",
      "callerNumber": "+4930555010",
      "callStartedAt": "2026-07-14T09:00:00.000Z",
      "callSummary": "Anrufer fragt nach Öffnungszeiten am Samstag.",
      "handlingStatus": "open"
    }
  ]
}
FeldBedeutung
typelead, ticket, appointment, info, other, order oder emergency
titleKurzer Titel des Anliegens
structuredDataErfasste Angaben. Die Felder hängen von der Art des Anliegens ab
callIdVerweis auf den Anruf
handlingStatusBearbeitungsstand im Portal: open (offen), in Arbeit oder erledigt

Termine abrufen

GET /api/v1/appointments

Wie /api/v1/outcomes, aber auf Anliegen vom Typ Termin beschränkt. Die Liste heißt in der Antwort appointments.

curl -H "Authorization: Bearer tlfy_IhrSchluessel" "https://api.telofy.app/api/v1/appointments"
{
  "page": 1,
  "limit": 50,
  "appointments": [
    {
      "id": "outcome_7b1a4c",
      "type": "appointment",
      "title": "Termin am Dienstag, 10:00 Uhr",
      "structuredData": {
        "requestedTime": "2026-07-21T10:00:00.000Z",
        "contactName": "Frau Beispiel"
      },
      "createdAt": "2026-07-14T10:19:35.000Z",
      "callId": "call_9f2a1b",
      "callerNumber": "+4930555099",
      "callStartedAt": "2026-07-14T10:15:00.000Z",
      "callSummary": "Anrufer möchte einen Termin für nächsten Dienstag.",
      "handlingStatus": "open"
    }
  ]
}

Paginierung

Alle Listen liefern die neuesten Einträge zuerst.

  • page beginnt bei 1 (Vorgabe 1).
  • limit ist 50 (Vorgabe) und höchstens 100.
  • Die Antwort enthält page und limit, aber keinen Gesamtzähler und keinen Cursor. Blättern Sie weiter, bis eine Seite weniger Einträge als limit liefert.
  • Filterparameter gibt es nicht. Es werden nur page und limit gelesen.
curl -H "Authorization: Bearer tlfy_IhrSchluessel" "https://api.telofy.app/api/v1/calls?page=2&limit=25"

Ratenlimit

Pro Schlüssel sind 60 Anfragen pro Minute erlaubt, zusammengezählt über alle Endpunkte und alle MCP-Aufrufe. Gezählt wird erst nach erfolgreicher Anmeldung. Wird das Limit überschritten, antwortet Telofy bis zum Ende der Minute mit Status 429:

{ "error": "rate_limited" }

Warten Sie nach einer 429-Antwort kurz, bevor Ihr Programm es erneut versucht.

Fehlercodes

Fehler kommen als JSON mit dem Feld error, zum Beispiel { "error": "unauthorized" }.

StatuserrorBedeutung
400invalid_request, purpose_required, invalid_number, employee_id_requiredDie Anfrage ist unvollständig oder fehlerhaft
401unauthorizedSchlüssel fehlt, ist falsch oder widerrufen
403plan_not_eligibleDie Funktion gibt es im aktuellen Tarif nicht
404not_found, employee_not_foundPfad unbekannt, oder die employeeId passt zu keinem Mitarbeiter
405method_not_allowedFalsche Methode. Der Kopf Allow nennt die erlaubte
409duplicate_external_referenceDie externalReference wurde schon verwendet
429rate_limitedMehr als 60 Anfragen in einer Minute
500internal_errorFehler bei Telofy
502provider_request_failedDer Anruf konnte technisch nicht gestartet werden

Ausgehender Anruf

POST /api/v1/outbound-calls. Verfügbar ab dem Tarif Business. In anderen Tarifen antwortet Telofy mit 403 plan_not_eligible.

FeldPflichtBedeutung
toNumberjaZielnummer im internationalen Format, zum Beispiel +4930123456
purposejaFreitext: worum es im Anruf geht
employeeIdneinNur nötig, wenn mehr als ein Mitarbeiter eine Nummer hat
externalReferenceneinIhre eigene Kennung, zum Beispiel eine Auftragsnummer. Ein zweiter Aufruf mit demselben Wert wird mit 409 abgelehnt

Der Mitarbeiter, der anruft, braucht eine zugewiesene Rufnummer.

curl -X POST -H "Authorization: Bearer tlfy_IhrSchluessel" -H "Content-Type: application/json" \
  -d '{"toNumber": "+4930111111", "purpose": "Ihr Auto ist fertig zur Abholung."}' \
  "https://api.telofy.app/api/v1/outbound-calls"
{ "callId": "call_9f2a1b" }

Die Antwort (Status 202) bestätigt nur, dass der Anruf gestartet wurde. Was besprochen wurde, erfahren Sie asynchron über den Webhook call.completed, sobald das Gespräch beendet und ausgewertet ist. Der MCP-Server löst keine Anrufe aus.

Webhook-Ereignisse

Webhooks legen Sie im Portal unter Integrationen > Webhooks an (Knopf „Webhook anlegen“). Sie tragen eine https-Adresse ein, wählen die Ereignisse und optional einen einzelnen Mitarbeiter. Das Signatur-Geheimnis (whsec_…) erscheint nur einmal in der Antwort.

Telofy sendet die Ereignisse nach der Anrufauswertung, nicht im Moment des Auflegens, und stellt sie asynchron zu. Planen Sie also mit einigen Minuten Abstand zum Ende des Gesprächs und nicht mit „in Echtzeit“.

EreignisWann
call.completedNach jedem ausgewerteten Anruf
outcome.createdWenn der Anruf ein Anliegen vom Typ lead, ticket, appointment oder order ergab
appointment.createdZusätzlich zu outcome.created, wenn das Anliegen ein Termin war

Jede Nachricht ist ein POST mit Content-Type: application/json und dem Kopf X-Telofy-Signature. Der Rumpf hat die Form { "event": …, "data": … }. Ein Gesprächsprotokoll enthält er nie, nur den Link callUrl ins Portal. Weitere Köpfe oder eine eigene Ereignis-Kennung gibt es nicht. Entdoppeln Sie über callId und outcomeId im Rumpf.

call.completed

{
  "event": "call.completed",
  "data": {
    "callId": "call_9f2a1b",
    "completedAt": "2026-07-14T09:04:12.000Z",
    "durationSeconds": 252,
    "employeeName": "Anna",
    "summary": "Anrufer fragt nach Öffnungszeiten am Samstag.",
    "outcomeType": "lead",
    "callUrl": "https://telofy.app/calls/call_9f2a1b"
  }
}

outcome.created

{
  "event": "outcome.created",
  "data": {
    "outcomeId": "outcome_2e9f31",
    "callId": "call_3c7d2e",
    "createdAt": "2026-07-14T09:04:20.000Z",
    "outcomeType": "lead",
    "summary": "Interesse an Beratungstermin",
    "callUrl": "https://telofy.app/calls/call_3c7d2e"
  }
}

appointment.created

{
  "event": "appointment.created",
  "data": {
    "outcomeId": "outcome_7b1a4c",
    "callId": "call_9f2a1b",
    "createdAt": "2026-07-14T10:19:35.000Z",
    "requestedTime": "2026-07-21T10:00:00.000Z",
    "contactName": "Frau Beispiel",
    "contactPhone": "+4930555099",
    "callUrl": "https://telofy.app/calls/call_9f2a1b"
  }
}

Zustellung

  • Als Erfolg gilt jeder Status 2xx. Antworten Sie innerhalb von 10 Sekunden.
  • Bei Misserfolg gibt es bis zu fünf Versuche insgesamt, mit Wartezeiten von 1, 5, 30 und 120 Minuten dazwischen. Danach gilt die Zustellung als gescheitert.
  • Weiterleitungen (Redirects) werden nicht verfolgt. Die Adresse muss direkt antworten.
  • Nur https://, ohne Zugangsdaten in der Adresse und ohne private oder interne Hostnamen wie localhost oder .local. Telofy prüft das beim Versand erneut.
  • Die Reihenfolge ist nicht garantiert. Ein Wiederholungsversuch trägt denselben Rumpf, aber einen neuen Zeitstempel in der Signatur.
  • Bis zu fünf Webhooks je Betrieb. Das Zustellprotokoll zeigt die letzten 50 Zustellungen je Webhook und wird nach 30 Tagen gelöscht.
  • Der Knopf „Test-Ereignis senden“ im Portal schickt ein call.completed mit "test": true auf der obersten Ebene, callId test und outcomeType test. Höchstens einmal pro Minute und Webhook. Ein Wiederholen vergangener Ereignisse gibt es nicht.

Signatur prüfen

Jede Nachricht trägt den Kopf

X-Telofy-Signature: t=1752482652,v1=<64 Hexzeichen>

t ist die Unix-Zeit in Sekunden des Versandversuchs. v1 ist der HMAC-SHA256 als Hexwert über den Text <t>.<Rohkörper>, gebildet mit dem Geheimnis Ihres Webhooks (whsec_…).

So prüfen Sie:

  1. Lesen Sie den Anfragetext unverändert als Text. Wenn Ihr Framework ihn schon in ein Objekt umgewandelt hat, stimmt die Prüfsumme nicht mehr. In Express nutzen Sie dafür express.raw({ type: "application/json" }), in Flask request.get_data().
  2. Trennen Sie t und v1 aus dem Kopf.
  3. Bilden Sie den HMAC-SHA256 über t, einen Punkt und den Rohkörper.
  4. Vergleichen Sie mit v1 in konstanter Zeit.
  5. Lehnen Sie Nachrichten ab, deren Zeitstempel mehr als fünf Minuten von Ihrer Uhr abweicht.

Node.js

import { createHmac, timingSafeEqual } from "node:crypto";

// rohKoerper: der unveränderte Anfragetext als String, nicht das geparste Objekt.
export function istTelofySignaturGueltig(
  geheimnis,
  rohKoerper,
  kopfzeile,
  jetzt = Math.floor(Date.now() / 1000)
) {
  const teile = {};
  for (const stueck of kopfzeile.split(",")) {
    const i = stueck.indexOf("=");
    if (i > 0) teile[stueck.slice(0, i).trim()] = stueck.slice(i + 1).trim();
  }

  const t = Number(teile.t);
  if (!Number.isFinite(t) || !teile.v1) return false;
  if (Math.abs(jetzt - t) > 300) return false; // höchstens fünf Minuten alt

  const erwartet = createHmac("sha256", geheimnis)
    .update(\`${teile.t}.${rohKoerper}\`, "utf8")
    .digest("hex");

  const a = Buffer.from(erwartet);
  const b = Buffer.from(teile.v1);
  return a.length === b.length && timingSafeEqual(a, b);
}

Python

import hashlib
import hmac
import time

def telofy_signatur_gueltig(geheimnis, roh_koerper, kopfzeile, jetzt=None):
    """roh_koerper: der unveränderte Anfragetext als bytes."""
    teile = {}
    for stueck in kopfzeile.split(","):
        schluessel, _, wert = stueck.partition("=")
        teile[schluessel.strip()] = wert.strip()

    t_text = teile.get("t")
    v1 = teile.get("v1")
    if not t_text or not v1:
        return False
    try:
        t = int(t_text)
    except ValueError:
        return False

    jetzt = time.time() if jetzt is None else jetzt
    if abs(jetzt - t) > 300:  # höchstens fünf Minuten alt
        return False

    erwartet = hmac.new(
        geheimnis.encode("utf-8"),
        t_text.encode("utf-8") + b"." + roh_koerper,
        hashlib.sha256,
    ).hexdigest()
    return hmac.compare_digest(erwartet.encode("utf-8"), v1.encode("utf-8"))

MCP für KI-Assistenten

Der MCP-Server liegt unter https://api.telofy.app/mcp (Streamable HTTP, ohne Sitzungszustand, Antwort als JSON). Er nutzt denselben API-Schlüssel und dasselbe Ratenlimit wie die Schnittstelle.

WerkzeugZweckParameter
list_callsAnrufe, neueste zuerstpage, limit (optional)
list_outcomesAnliegen aus Anrufenpage, limit (optional)
list_appointmentsTerminepage, limit (optional)

Alle drei Werkzeuge lesen nur. Der MCP-Server löst keine Anrufe aus und ändert nichts.

Eine übliche Konfiguration für Assistenten, die MCP-Server über eine Adresse einbinden:

{
  "mcpServers": {
    "telofy": {
      "url": "https://api.telofy.app/mcp",
      "headers": {
        "Authorization": "Bearer tlfy_IhrSchluessel"
      }
    }
  }
}

Wo Sie das eintragen, unterscheidet sich je Assistent. Schauen Sie in dessen Anleitung nach der Stelle für entfernte MCP-Server. Ein Fehler beim Werkzeugaufruf, etwa durch einen ungültigen Schlüssel, kommt als Werkzeugfehler zurück und nicht als HTTP-Fehler.

Eigene Werkzeuge

Mit eigenen Werkzeugen ruft Telofy während eines Gesprächs eine https-Adresse Ihres Systems auf und liest die Antwort vor. Das ist die umgekehrte Richtung: Sie liefern Telofy Daten, statt welche abzuholen.

Anlegen im Portal unter Integrationen > Eigene Werkzeuge (Knopf „Werkzeug anlegen“). Sie legen fest:

  • Funktionsname: Nur der Assistent sieht ihn, ohne Leerzeichen, zum Beispiel lookup_shipping_status.
  • Beschreibung: Sie entscheidet, ob Telofy das Werkzeug im richtigen Moment aufruft. Schreiben Sie, wann es genutzt werden soll.
  • Parameter-Schema (JSON): Welche Angaben Telofy aus dem Gespräch mitschickt.
  • Ziel-Adresse: https, öffentlich erreichbar.
  • Auth-Header-Name und Geheimnis (optional): Telofy sendet sie bei jedem Aufruf mit. Das Geheimnis wird verschlüsselt gespeichert.

Beispiel für ein Parameter-Schema:

{
  "type": "OBJECT",
  "properties": {
    "order_number": { "type": "STRING", "description": "Bestellnummer" }
  },
  "required": ["order_number"]
}

Telofy sendet einen POST mit den Angaben unter arguments:

POST /versandstatus HTTP/1.1
Host: ihr-system.de
Content-Type: application/json

{ "arguments": { "order_number": "A-1042" } }

Ihr System antwortet mit Status 2xx und genau diesem Aufbau:

{ "result": "Die Bestellung A-1042 wurde heute mit DHL verschickt und kommt voraussichtlich morgen an." }

Grenzen:

  • Bis zu 10 Werkzeuge je Betrieb.
  • 4 Sekunden Zeit je Aufruf. Die Antwort darf höchstens 4.000 Byte groß sein, der Text in result wird auf 800 Zeichen gekürzt.
  • Weiterleitungen werden nicht verfolgt. Private und interne Adressen werden abgelehnt.
  • Schlägt ein Aufruf fehl, sagt Telofy dem Anrufer ehrlich, dass es das gerade nicht nachsehen kann, und bietet an, das Anliegen aufzunehmen. Das Gespräch bricht nicht ab.
  • Reservierte Namen lassen sich nicht vergeben.

Was die Schnittstelle nicht kann

  • Sie schreibt keine Termine, Kontakte, Katalogpositionen oder Wissenseinträge. Die Pflege dieser Daten läuft über das Portal und den Import. Schreibend ist nur der ausgehende Anruf.
  • Es gibt keine Filter, keinen Gesamtzähler und keine Berechtigungsstufen je Schlüssel.
  • MCP liest nur.
  • Webhooks haben keine Ereignis-Kennung im Kopf, keine Reihenfolgegarantie und keine Wiederholung vergangener Ereignisse auf Knopfdruck.
  • Es gibt kein Programmierpaket (SDK) und keine OpenAPI-Datei.
  • Eine eigene App für Zapier, Make oder n8n gibt es nicht. Die Anbindung läuft über deren Webhook-Bausteine, siehe Integrationen.

Fragen von Entwicklern.

Wie lautet die Basis-Adresse der Schnittstelle?

https://api.telofy.app. Die Anfragen gehen an Pfade wie /api/v1/calls und /mcp. Jede Anfrage braucht den Kopf Authorization: Bearer mit Ihrem Schlüssel.

Wie bekomme ich einen API-Schlüssel?

Im Portal unter Integrationen > API-Schlüssel erstellen Sie bis zu drei Schlüssel. Er wird nur einmal vollständig angezeigt und beginnt mit tlfy_.

Kann ich per Schnittstelle einen Anruf auslösen?

Ja, ab dem Tarif Business mit POST /api/v1/outbound-calls. Das Ergebnis des Gesprächs kommt anschließend über den Webhook call.completed.

Wie prüfe ich, dass ein Webhook von Telofy kommt?

Über den Kopf X-Telofy-Signature. Er enthält die Zeitangabe t und den HMAC-SHA256 v1 über t, einen Punkt und den Rohkörper, gebildet mit dem Geheimnis Ihres Webhooks. Beispiele für Node.js und Python stehen auf dieser Seite.

Sind Webhooks in Echtzeit?

Nein. Telofy sendet sie nach der Anrufauswertung und stellt sie asynchron zu. Bei Misserfolg gibt es bis zu fünf Versuche.