Přeskočit na hlavní obsah

Místní REST API

AI Softphone má REST API pro integraci CTI: program na stejném počítači může uskutečňovat a řídit hovory, číst kontakty, historii hovorů a SIP účty a sledovat probíhající hovory. Žádné SDK, žádný cloudový prostředník a žádný posluchač otevřený do sítě. Požadavky i odpovědi jsou JSON, takže stačí curl nebo jakýkoli HTTP klient.

API je po instalaci vypnuté; nic neposlouchá, dokud ho nezapnete. Pak poslouchá jen na rozhraní loopback — malé webové rozhraní, které odpovídá jen tomuto počítači — a z kancelářské sítě, VPN ani jiného stroje není dostupné.

API použijte, když váš program potřebuje data z telefonu nebo musí řídit hovor. Webhooky použijte, když musí na hovory reagovat v okamžiku, kdy se dějí, bez dotazování. Většina integrací používá obojí; jsou na sobě nezávislé.

Zapnutí​

Otevřete Nastavení → Integrace a přejděte na Místní ovládání.

  1. Zapněte Nechat jiné programy v tomto počítači ovládat telefon. Server se spustí hned.
  2. Ponechte výchozí Port 8377, pokud ho už nepoužívá jiný program.
  3. Volitelně nastavte Token. Po uložení pole ukazuje Uloženo — pište, ať to nahradíte.
  4. V části Přístup vyberte skupiny, které otevřít: Kontakty, Historie hovorů, Hovory a jejich ovládání, Účty, Nastavení, Čítače (metriky). Vypnutá skupina se nefiltruje, ale vůbec se neobsluhuje.
  5. Vyzkoušejte: curl http://127.0.0.1:8377/accounts. Pokud je odpovědí JSON, API funguje.

Neinstaluje se žádná samostatná služba a není potřeba restart. Část programu, která to dělá, lze vypnout v Modulech (Integrace).

Vlastní stránka API​

Otevřít vlastní stránku API otevře http://127.0.0.1:8377 v prohlížeči. Adresa odpoví seznamem všeho, co obsluhuje, anglicky; adresy, které něco čtou, jsou odkazy, na které lze kliknout.

Přístup a token​

Co program smí, závisí na tom, zda mění uložená data, ne na tom, zda čte:

  • Bez tokenu smí jakýkoli program v počítači číst vše v povolených skupinách a řídit hovory: uskutečnit, přijmout, zavěsit, přidržet, obnovit, přepojit a poslat DTMF.
  • S tokenem v hlavičce Authorization smí používat i koncové body, které mění, co je uloženo. Bez tokenu se tyto koncové body neobsluhují ani nejsou uvedeny na vlastní stránce API.

Token se ukládá do klíčenky počítače, ne do souboru s nastavením, a /settings ho nikdy nevrací.

caution

Bez tokenu může telefon ovládat jakýkoli program běžící v tomto počítači, včetně přijímání hovorů. Na osobní pracovní stanici je to obvykle přijatelné. Na sdíleném nebo spravovaném počítači token nastavte a zacházejte s ním jako s každým jiným heslem.

Koncové body​

Základní adresa je http://127.0.0.1:8377. Koncové body níže token nepotřebují.

MetodaCestaDělá
GET/metricsČítače ve formátu Prometheus.
GET/uiSeznam nahrávek jako stránka HTML.
GET/ui/recordings/{id}Nahrávka s přepisem jako stránka HTML.
GET/ui/recordings/{id}/audioZvuk pro stránku výše.
GET/contactsKontakty.
GET/contacts/{id}Jeden kontakt.
GET/historyHistorie hovorů, nejnovější první. Přijímá ?limit=, ?missed=true a ?declined=true.
GET/callsProbíhající hovory.
POST/callsUskuteční hovor: {"number": "...", "account_id": "..."}.
POST/calls/{id}/answerPřijme hovor.
POST/calls/{id}/hangupZavěsí hovor.
POST/calls/{id}/holdPřidrží hovor.
POST/calls/{id}/resumeObnoví přidržený hovor.
POST/calls/{id}/dtmfPošle tóny: {"digits": "..."}.
POST/calls/{id}/transferPřepojí hovor: {"target": "..."}.
GET/accountsSIP účty a stav jejich registrace. Nikdy heslo.
GET/settingsCelá konfigurace bez tajných údajů.
GET/taxonomyKategorie, štítky a varovné signály s jejich kódy.

Každý identifikátor je UUID vydané telefonem: id hovoru pochází z /calls nebo z odpovědi na POST /calls, id účtu z /accounts.

Názvy polí jsou v snake_case a koncovka říká typ: _id je odkaz na UUID, _ts je okamžik v unixových milisekundách (UTC), _s je délka v sekundách. Totéž platí pro webhooky; vlastní názvy si ponechává jen /settings. V REST API jsou tyto hodnoty čísla JSON a okamžik, který není znám, je null.

Příklad: uskutečnění hovoru​

POST /calls uskuteční odchozí hovor. Tělo je JSON s číslem number, které se vytočí, a volitelně account_id účtu, ze kterého se volá:

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

Odpovědí je identifikátor nového hovoru:

{ "id": "9a3e5c71-2d48-4b6f-8e10-3c5f7a1b9d24" }
  • number je povinné. Bez něj je odpovědí 400 {"error":"a call needs a number"} a nic se nevytočí.
  • Číslo se na zvoleném účtu doplní stejně, jako ho doplňuje vytáčení: 1020 se pošle jako sip:1020@pbx.example.com. POST /calls/{id}/transfer doplňuje svůj target stejně; cíl, který už má schéma nebo @, se pošle tak, jak je.
  • account_id je nepovinné; vezměte ho z GET /accounts. Bez něj hovor odejde z účtu vybraného v hlavním okně.
  • id použijte v /calls/{id}/…: hangup, hold, resume, dtmf a transfer. Webhooky tohoto hovoru nesou stejné id.

Z webové stránky: volání kliknutím​

Stránka, která volá 127.0.0.1, se dostane k počítači, na kterém běží prohlížeč — tomu samému, na kterém běží telefon — takže tlačítko pro volání kliknutím v CRM nepotřebuje vlastní 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);
}

Co odpovědi obsahují​

Probíhající hovory: GET /calls​

Každý hovor má id, seance_id, account_id, direction, state, number, name, uri, dialed, muted, event_ts, callstart_ts a callstate_ts.

  • state je dialing, ringing-out, ringing-in, active, hold (přidržel tento telefon), onhold (přidržela druhá strana), conference nebo ended. Když platí víc z nich, conference má přednost před hold a hold před onhold.
  • muted říká, zda je v hovoru ztlumený mikrofon; ztlumení state nemění.
  • seance_id je rozhovor: hovory spojené přepojením, konzultací nebo konferencí ho sdílejí.
  • event_ts je okamžik, kdy byla odpověď vytvořena. Porovnejte ho s callstate_ts a uvidíte, jak dlouho je hovor ve svém stavu, aniž byste se spoléhali na vlastní hodiny.

Účty: GET /accounts​

Každý účet má své id (všude jinde account_id), nastavení — transport (udp, tcp nebo tls), port, registrar, outbound_proxy, expiry_s a další — zda je enabled a svůj state na ústředně: registered, dokud je linka v provozu. Hesla nejsou nikdy součástí.

Historie hovorů: GET /history​

Nejnovější první, 100 položek, pokud ?limit= neřekne jinak. ?missed=true vrátí jen zmeškané hovory, ?declined=true jen hovory, které tento telefon odmítl.

PoleVýznam
idVlastní identifikátor položky historie. Není to id hovoru z /calls a webhooků; spojuje je seance_id.
outcomeHlavní zařazení: answered, missed, declined nebo failed.
answeredtrue nebo false.
duration_s0 pro hovor, který nebyl nikdy spojen.
number, uriDruhá strana jako číslo a jako SIP adresa.
nameZ Kontaktů, pokud je číslo známé, jinak prázdné. Párujte podle number, ne podle tohoto.
dialedVytočené číslice u odchozího hovoru; u příchozího prázdné.
account, account_idLinka, na které hovor byl.
reasonJak skončil: local-hangup, remote-hangup, cancelled…
answered_byno, pokud ho přijal člověk; jinak to, co hovor přijalo.

Kontakty: GET /contacts​

Každý kontakt má id, name, number a linku, ke které patří, account_id a account; prázdné account znamená, že kontakt není vázán na linku.

Nahrávky​

Nahrávky a přepisy se nevydávají jako JSON. API je obsluhuje jako stránky HTML, /ui a /ui/recordings/{id}: odkazujte na tyto stránky z CRM, místo abyste přesouvali zvuk. Odkaz se otevře na počítači, který nahrávku uchovává, a zvuk ho nikdy neopustí.

Taxonomie a nastavení​

Každá položka /taxonomy má neměnný code, title a description v jazyce rozhraní, kind (category, tag nebo red_flag) a u varovných signálů severity. Párujte podle code, nikdy podle title: názvy přicházejí v jazyce, na který je telefon nastaven. Položka s retired: true se uchovává, aby starší hovory stále šlo přiřadit; novým hovorům se už nedává. Taxonomii načtěte jednou při startu a namapujte slova telefonu na svá vlastní pole.

/settings vrací konfiguraci kromě tajných údajů: zvuková zařízení a hlasitosti, prioritu kodeků, vzhled a jazyk, spouštění, klávesové zkratky, úroveň diagnostiky a stav obou integrací — užitečné pro nástroj podpory, který musí pracovní stanici zkontrolovat bez sdílení obrazovky. api.disabled uvádí vypnuté přístupové skupiny a webhooks.silenced vypnuté události; prázdné seznamy znamenají, že je vše zapnuto. Nikdy neobsahuje heslo SIP, token API ani hodnotu hlavičky webhooku.

Chyby​

Každá chyba je JSON s jediným klíčem error, určená lidem, ne pro strojové zpracování.

StavTěloVýznam
404{"error":"no such endpoint"}Cesta neexistuje, nebo je její přístupová skupina vypnutá; obojí záměrně dává stejnou odpověď.
404{"error":"no contact with that id"}Cesta je správná, identifikátor ne.
400{"error":"no call with that id"}Hovor skončil, nebo nikdy neexistoval.
400{"error":"a call needs a number"}POST /calls bez čísla. Nic nebylo vytočeno.
400{"error":"no digits to send"}POST /calls/{id}/dtmf bez číslic.
400{"error":"a transfer needs a target"}POST /calls/{id}/transfer bez cíle.
400{"error":"the account this call is on is no longer set up"}Účet hovoru byl během hovoru odebrán, takže cíl nelze doplnit. Ústředně se nic neposlalo.

Odmítnuté požadavky se počítají v api_requests_refused_total, takže integrace, která tiše selhává, se ukáže v metrikách, nejen ve vašich protokolech.

Metriky​

GET /metrics vrací každý čítač telefonu s textem nápovědy. Sbírejte je Prometheem nebo je čtěte ručně.

ČítačPočítá
calls_incoming_totalPřijaté příchozí hovory.
calls_outgoing_totalUskutečněné odchozí hovory.
calls_answered_totalHovory, které byly přijaty.
calls_missed_totalPříchozí hovory, které nebyly přijaty.
calls_declined_totalHovory odmítnuté zde nebo druhou stranou.
calls_failed_totalHovory, které nebylo možné sestavit.
registrations_succeeded_totalÚspěšné registrace SIP.
registrations_failed_totalRegistrace SIP odmítnuté nebo s vypršením času.
webhooks_delivered_totalWebhooky, které příjemce přijal.
webhooks_failed_totalWebhooky odmítnuté nebo nedoručené.
webhooks_dropped_totalWebhooky zahozené, protože byla fronta plná.
api_requests_totalPožadavky obsloužené API.
api_requests_refused_totalOdmítnuté požadavky: špatný token, vypnutá skupina nebo neznámá cesta.

Aktualizace starší integrace​

Dřívější verze používaly názvy v camelCase a krátké identifikátory. accountId je teď account_id, startedAt je callstart_ts, durationSeconds je duration_s, answeredBy je answered_by a pole webhooku at je event_ts. Hovory a účty se identifikují jen pomocí UUID: runtimeId a identifikátory jako call-3 nebo account-2 se už nevracejí ani nepřijímají.

Když to nefunguje​

PříznakCo zkontrolovat
Spojení na 127.0.0.1:8377 je odmítnutoMístní ovládání je vypnuté, telefon neběží, nebo byl změněn port.
404 {"error":"no such endpoint"} pro cestu z této stránkyJejí přístupová skupina je vypnutá.
Čtení funguje, zápis je odmítnutKoncové body, které mění uložená data, potřebují token v hlavičce Authorization.
Názvy kategorií nejsou anglickyNázvy se řídí jazykem rozhraní. Párujte podle code z /taxonomy.
Chybí accountId, startedAt nebo atIntegrace byla napsána pro dřívější názvy; viz výše.

Při problému s registrací nebo samotným hovorem otevřete Diagnostiku.