Zum Hauptinhalt springen

Lokale REST-API

AI Softphone hat eine REST-API für die CTI-Integration: Ein Programm auf demselben Rechner kann Anrufe tätigen und steuern, Kontakte, Anrufliste und SIP-Konten lesen und die laufenden Anrufe beobachten. Kein SDK, kein Vermittler in der Cloud und kein ins Netz geöffneter Listener. Anfragen und Antworten sind JSON, curl oder jeder HTTP-Client genügt also.

Die API ist nach der Installation aus; nichts lauscht, bis Sie sie einschalten. Dann lauscht sie nur an der Loopback-Schnittstelle — eine kleine Weboberfläche, die nur diesem Rechner antwortet — und ist aus dem Büronetz, einem VPN oder von einem anderen Rechner aus nicht erreichbar.

Verwenden Sie die API, wenn Ihr Programm Daten vom Telefon braucht oder einen Anruf steuern muss. Verwenden Sie Webhooks, wenn es auf Anrufe reagieren muss, während sie geschehen, ohne abzufragen. Die meisten Integrationen nutzen beides; sie sind voneinander unabhängig.

Einschalten​

Öffnen Sie Einstellungen → Integration und gehen Sie zu Lokale Steuerung.

  1. Schalten Sie Anderen Programmen auf diesem Rechner erlauben, das Telefon zu steuern ein. Der Server startet sofort.
  2. Behalten Sie den Standard-Port 8377, sofern ihn nicht schon ein anderes Programm verwendet.
  3. Setzen Sie bei Bedarf ein Token. Nach dem Speichern zeigt das Feld Gespeichert — tippen Sie, um es zu ersetzen.
  4. Wählen Sie unter Zugang die Gruppen, die geöffnet werden: Kontakte, Anrufliste, Anrufe und ihre Steuerung, Konten, Einstellungen, Zähler (die Metriken). Eine ausgeschaltete Gruppe wird nicht gefiltert, sondern gar nicht bedient.
  5. Testen Sie: curl http://127.0.0.1:8377/accounts. Ist die Antwort JSON, funktioniert die API.

Es wird kein eigener Dienst installiert, und es braucht keinen Neustart. Der Teil des Programms, der das tut, lässt sich unter Module abschalten (Integration).

Die eigene Seite der Schnittstelle​

Die eigene Seite der Schnittstelle öffnen öffnet http://127.0.0.1:8377 in einem Browser. Die Adresse antwortet mit einer Liste von allem, was sie bereitstellt, auf Englisch; die Adressen, die etwas lesen, sind Links, denen Sie folgen können.

Zugang und das Token​

Was ein Programm darf, hängt davon ab, ob es gespeicherte Daten ändert, nicht davon, ob es liest:

  • Ohne Token darf jedes Programm auf dem Rechner alles in den freigegebenen Gruppen lesen und Anrufe steuern: tätigen, annehmen, auflegen, halten, fortsetzen, weiterleiten und DTMF senden.
  • Mit dem Token in der Kopfzeile Authorization darf es zusätzlich die Endpunkte verwenden, die Gespeichertes ändern. Ohne Token werden diese Endpunkte weder bedient noch auf der eigenen Seite der Schnittstelle aufgeführt.

Das Token liegt im Schlüsselbund des Rechners, nicht in der Einstellungsdatei, und wird von /settings nie zurückgegeben.

vorsicht

Ohne Token kann jedes Programm, das auf diesem Rechner läuft, das Telefon steuern, auch Anrufe annehmen. Auf einem persönlichen Arbeitsplatz ist das meist vertretbar. Auf einem geteilten oder verwalteten Rechner setzen Sie ein Token und behandeln es wie jedes andere Passwort.

Endpunkte​

Die Basisadresse ist http://127.0.0.1:8377. Die Endpunkte unten brauchen kein Token.

MethodePfadBewirkt
GET/metricsDie Zähler im Prometheus-Format.
GET/uiDie Liste der Aufnahmen als HTML-Seite.
GET/ui/recordings/{id}Eine Aufnahme mit ihrem Transkript als HTML-Seite.
GET/ui/recordings/{id}/audioDer Ton für die Seite darüber.
GET/contactsDie Kontakte.
GET/contacts/{id}Ein einzelner Kontakt.
GET/historyDie Anrufliste, neueste zuerst. Nimmt ?limit=, ?missed=true und ?declined=true an.
GET/callsDie laufenden Anrufe.
POST/callsTätigt einen Anruf: {"number": "...", "account_id": "..."}.
POST/calls/{id}/answerNimmt einen Anruf an.
POST/calls/{id}/hangupLegt einen Anruf auf.
POST/calls/{id}/holdHält einen Anruf.
POST/calls/{id}/resumeSetzt ihn fort.
POST/calls/{id}/dtmfSendet Töne: {"digits": "..."}.
POST/calls/{id}/transferLeitet den Anruf weiter: {"target": "..."}.
GET/accountsDie SIP-Konten und ihr Anmeldezustand. Nie ein Passwort.
GET/settingsDie gesamte Konfiguration, ohne die Geheimnisse.
GET/taxonomyKategorien, Label und Auffälligkeiten mit ihren Codes.

Jede Kennung ist eine vom Telefon vergebene UUID: Eine Anruf-id kommt aus /calls oder aus der Antwort auf POST /calls, eine Konto-id aus /accounts.

Feldnamen sind in snake_case, und die Endung sagt den Typ: _id ist ein Verweis auf eine UUID, _ts ist ein Zeitpunkt in Unix-Millisekunden (UTC), _s ist eine Dauer in Sekunden. Dasselbe gilt für Webhooks; nur /settings behält eigene Namen. In der REST-API sind diese Werte JSON-Zahlen, und ein unbekannter Zeitpunkt ist null.

Beispiel: einen Anruf tätigen​

POST /calls tätigt einen ausgehenden Anruf. Der Körper ist JSON mit der zu wählenden number und optional der account_id des Kontos, von dem aus angerufen wird:

curl --location 'http://127.0.0.1:8377/calls' \
--header 'Content-Type: application/json' \
--data '{
"number": "1020",
"account_id": "0d7a4c52-6f2e-4a51-8f46-7d9a3e1b2c90"
}'

Die Antwort ist die Kennung des neuen Anrufs:

{ "id": "9a3e5c71-2d48-4b6f-8e10-3c5f7a1b9d24" }
  • number ist Pflicht. Ohne sie lautet die Antwort 400 {"error":"a call needs a number"}, und es wird nichts gewählt.
  • Die Nummer wird auf dem gewählten Konto so ergänzt, wie die Wählhilfe sie ergänzt: 1020 wird als sip:1020@pbx.example.com gesendet. POST /calls/{id}/transfer ergänzt sein target genauso; ein Ziel, das schon ein Schema oder ein @ hat, wird unverändert gesendet.
  • account_id ist optional; nehmen Sie sie aus GET /accounts. Ohne sie geht der Anruf über das im Hauptfenster gewählte Konto hinaus.
  • Verwenden Sie die id in /calls/{id}/…: hangup, hold, resume, dtmf und transfer. Die Webhooks dieses Anrufs tragen dieselbe id.

Von einer Webseite: Klick zum Anrufen​

Eine Seite, die 127.0.0.1 aufruft, erreicht den Rechner, auf dem der Browser läuft — denselben, auf dem das Telefon läuft —, eine Klick-zum-Anrufen-Taste in einem CRM braucht also keinen eigenen Server:

async function dial(number) {
const r = await fetch("http://127.0.0.1:8377/calls", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ number }),
});
if (!r.ok) console.warn("softphone:", (await r.json()).error);
}

Was die Antworten enthalten​

Laufende Anrufe: GET /calls​

Jeder Anruf hat seine id, seance_id, account_id, direction, state, number, name, uri, dialed, muted, event_ts, callstart_ts und callstate_ts.

  • state ist dialing, ringing-out, ringing-in, active, hold (von diesem Telefon gehalten), onhold (von der Gegenseite gehalten), conference oder ended. Trifft mehr als einer zu, geht conference vor hold und hold vor onhold.
  • muted sagt, ob das Mikrofon im Gespräch stummgeschaltet ist; Stummschalten ändert state nicht.
  • seance_id ist das Gespräch: Durch Weiterleitung, Rückfrage oder Konferenz verbundene Anrufe teilen sie.
  • event_ts ist der Zeitpunkt, zu dem die Antwort erstellt wurde. Vergleichen Sie ihn mit callstate_ts, um zu sehen, wie lange der Anruf schon in seinem Zustand ist, ohne sich auf Ihre eigene Uhr zu verlassen.

Konten: GET /accounts​

Jedes Konto hat seine id (überall sonst die account_id), seine Einstellungen — transport (udp, tcp oder tls), port, registrar, outbound_proxy, expiry_s und weitere —, ob es enabled ist, und seinen state an der Telefonanlage: registered, solange die Leitung steht. Passwörter sind nie enthalten.

Anrufliste: GET /history​

Neueste zuerst, 100 Einträge, sofern ?limit= nichts anderes sagt. ?missed=true liefert nur verpasste Anrufe, ?declined=true nur die von diesem Telefon abgelehnten.

FeldBedeutung
idDie eigene Kennung des Verlaufseintrags. Sie ist nicht die Anruf-id aus /calls und den Webhooks; seance_id verbindet beide.
outcomeDie Hauptklassifizierung: answered, missed, declined oder failed.
answeredtrue oder false.
duration_s0 für einen Anruf, der nie verbunden wurde.
number, uriDie Gegenseite, als Nummer und als SIP-Adresse.
nameAus den Kontakten, wenn die Nummer bekannt ist, sonst leer. Gleichen Sie über number ab, nicht hierüber.
dialedDie gewählten Ziffern bei einem ausgehenden Anruf; leer bei einem eingehenden.
account, account_idDie Leitung, auf der der Anruf lief.
reasonWie er endete: local-hangup, remote-hangup, cancelled…
answered_byno, wenn ein Mensch angenommen hat; sonst das, was den Anruf angenommen hat.

Kontakte: GET /contacts​

Jeder Kontakt hat seine id, name, number und die Leitung, zu der er gehört, account_id und account; ein leeres account bedeutet, dass der Kontakt an keine Leitung gebunden ist.

Aufnahmen​

Aufnahmen und Transkripte werden nicht als JSON herausgegeben. Die API stellt sie als HTML-Seiten bereit, /ui und /ui/recordings/{id}: Verlinken Sie aus Ihrem CRM auf diese Seiten, statt Ton herumzuschieben. Der Link öffnet sich auf dem Rechner, der die Aufnahme aufbewahrt, und der Ton verlässt ihn nie.

Taxonomie und Einstellungen​

Jeder Eintrag von /taxonomy hat einen festen code, einen title und eine description in der Oberflächensprache, eine kind (category, tag oder red_flag) und bei Auffälligkeiten eine severity. Gleichen Sie über code ab, nie über title: Titel kommen in der Sprache, auf die das Telefon eingestellt ist. Ein Eintrag mit retired: true wird aufbewahrt, damit ältere Anrufe noch aufgelöst werden; neuen Anrufen wird er nicht mehr gegeben. Laden Sie die Taxonomie einmal beim Start, um die Wörter des Telefons Ihren eigenen Feldern zuzuordnen.

/settings liefert die Konfiguration ohne die Geheimnisse: Audiogeräte und Lautstärken, Codec-Priorität, Erscheinungsbild und Sprache, Start, Tastenkürzel, Diagnosestufe und den Zustand beider Integrationen — nützlich für ein Support-Werkzeug, das einen Arbeitsplatz prüfen muss, ohne den Bildschirm zu teilen. api.disabled listet die ausgeschalteten Zugangsgruppen und webhooks.silenced die ausgeschalteten Ereignisse; leere Listen bedeuten, dass alles an ist. Es enthält nie das SIP-Passwort, das API-Token oder den Kopfzeilenwert des Webhooks.

Fehler​

Jeder Fehler ist JSON mit einem einzigen Schlüssel error, für Menschen gedacht, nicht zum Auswerten.

StatusKörperBedeutung
404{"error":"no such endpoint"}Der Pfad existiert nicht, oder seine Zugangsgruppe ist aus; beides gibt mit Absicht dieselbe Antwort.
404{"error":"no contact with that id"}Der Pfad stimmt, die Kennung nicht.
400{"error":"no call with that id"}Der Anruf ist beendet oder hat nie existiert.
400{"error":"a call needs a number"}POST /calls ohne Nummer. Es wurde nichts gewählt.
400{"error":"no digits to send"}POST /calls/{id}/dtmf ohne Ziffern.
400{"error":"a transfer needs a target"}POST /calls/{id}/transfer ohne Ziel.
400{"error":"the account this call is on is no longer set up"}Das Konto des Anrufs wurde während des Gesprächs entfernt, das Ziel lässt sich also nicht ergänzen. An die Telefonanlage wurde nichts gesendet.

Abgelehnte Anfragen werden in api_requests_refused_total gezählt, sodass eine Integration, die still scheitert, in den Metriken auffällt, nicht nur in Ihren eigenen Protokollen.

Metriken​

GET /metrics liefert jeden Zähler des Telefons, jeden mit einem Hilfetext. Lesen Sie sie mit Prometheus aus oder von Hand.

ZählerZählt
calls_incoming_totalEmpfangene eingehende Anrufe.
calls_outgoing_totalGetätigte ausgehende Anrufe.
calls_answered_totalAngenommene Anrufe.
calls_missed_totalEingehende Anrufe, die nicht angenommen wurden.
calls_declined_totalHier oder von der Gegenseite abgelehnte Anrufe.
calls_failed_totalAnrufe, die nicht aufgebaut werden konnten.
registrations_succeeded_totalErfolgreiche SIP-Anmeldungen.
registrations_failed_totalAbgelehnte oder zeitlich abgelaufene SIP-Anmeldungen.
webhooks_delivered_totalVom Empfänger angenommene Webhooks.
webhooks_failed_totalAbgelehnte oder nicht zugestellte Webhooks.
webhooks_dropped_totalVerworfene Webhooks, weil die Warteschlange voll war.
api_requests_totalVon der API bearbeitete Anfragen.
api_requests_refused_totalAbgelehnte Anfragen: falsches Token, Gruppe aus oder unbekannter Pfad.

Eine ältere Integration aktualisieren​

Frühere Versionen verwendeten Namen in camelCase und kurze Kennungen. accountId heißt jetzt account_id, startedAt ist callstart_ts, durationSeconds ist duration_s, answeredBy ist answered_by, und das Webhook-Feld at ist event_ts. Anrufe und Konten werden nur noch über UUID gekennzeichnet: runtimeId und Kennungen wie call-3 oder account-2 werden weder zurückgegeben noch angenommen.

Wenn es nicht funktioniert​

SymptomWas zu prüfen ist
Verbindung zu 127.0.0.1:8377 abgelehntDie lokale Steuerung ist aus, das Telefon läuft nicht, oder der Port wurde geändert.
404 {"error":"no such endpoint"} für einen Pfad auf dieser SeiteSeine Zugangsgruppe ist aus.
Lesen geht, Schreiben wird abgelehntDie Endpunkte, die gespeicherte Daten ändern, brauchen das Token in der Kopfzeile Authorization.
Kategorietitel sind nicht englischTitel folgen der Oberflächensprache. Gleichen Sie über code aus /taxonomy ab.
accountId, startedAt oder at fehlenDie Integration wurde für die früheren Namen geschrieben; siehe oben.

Bei einem Problem mit der Anmeldung oder einem Anruf selbst öffnen Sie die Diagnose.