Alert API (Beta)

# @ CallAPI ~ 9 min
#alert #alarm #voice #dtmf #api #beta

Alert API (Beta)

Beta. Alert API jest w fazie testów. Format zapytań, odpowiedzi i webhooków może się jeszcze zmienić. Prosimy nie traktować go jako ostatecznego i zgłaszać wszelkie uwagi.

Alert API dzwoni na jeden lub wiele numerów, odtwarza komunikat (syntezator mowy lub plik dźwiękowy), opcjonalnie zbiera potwierdzenie z klawiatury telefonu (DTMF), wykrywa automatyczną sekretarkę i raportuje wynik każdego połączenia.

W odróżnieniu od Voice API nie trzeba budować logiki połączenia z obiektów - wystarczy podać numery, komunikat i sposób potwierdzenia. Typowe zastosowania:

  • alarm z potwierdzeniem - dzwonimy do wszystkich naraz, pierwsza osoba, która naciśnie 1, potwierdza alarm, a pozostałe połączenia są kończone,
  • lista eskalacyjna - dzwonimy po kolei, aż ktoś potwierdzi,
  • powiadomienie - komunikat do odsłuchania, bez potwierdzania.

Zasady wspólne z pozostałymi API:

  • autoryzacja tokenem JWT w nagłówku Authorization: Bearer <token> (patrz Autentykacja),
  • każda odpowiedź zawiera pole result o wartości OK lub error; w przypadku błędu pole error zawiera jego opis,
  • numery telefonów w formacie E.164 bez znaku + i bez 00, np. 48501000000.

Utworzenie alertu

POST https://api.vpbx.pl/api/v1/alerts

Alarm z potwierdzeniem - dzwonimy do dwóch osób jednocześnie, pierwsze naciśnięcie 1 potwierdza alarm:

curl --header "Content-Type: application/json" \
--header "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAoMS0Iiwi" \
--request POST \
--data '{
    "to": ["48600100200", "48600100201"],
    "from": "48221234567",
    "message": {
        "tts": {
            "text": "Alarm w serwerowni. Naciśnij 1 aby potwierdzić, 0 aby odrzucić.",
            "lang": "pl-PL/Maja"
        }
    },
    "dtmf": {"accept": "1", "reject": "0", "timeout": 10},
    "confirmation": {
        "accepted": {"tts": {"text": "Dziękujemy, alarm potwierdzony.", "lang": "pl-PL/Maja"}}
    },
    "amd": {"on_machine": "hangup"},
    "webhook": "https://twoj.serwer.pl/alerty",
    "reference": "incydent-4711"
}' \
https://api.vpbx.pl/api/v1/alerts

Lista eskalacyjna z nagranym komunikatem - dzwonimy po kolei, a gdy odbierze poczta głosowa, zostawiamy wiadomość:

{
    "to": ["48600100200", "48600100201", "48600100202"],
    "from": "48221234567",
    "mode": "sequential",
    "message": {"playfile": {"url": "https://twoj.serwer.pl/alarm.wav"}},
    "dtmf": {},
    "amd": {"on_machine": "leave_message"}
}

Powiadomienie na jeden numer, bez potwierdzania:

{
    "to": "48600100200",
    "from": "48221234567",
    "message": {"tts": {"text": "Dziś o 22:00 planowana przerwa serwisowa.", "lang": "pl-PL/Maja"}},
    "repeat": 2
}

Parametry zapytania

Pole Typ Domyślnie Opis
to String lub Array of Strings wymagane Jeden numer, np. "48600100200", lub lista numerów, np. ["48600100200", "48600100201"], w formacie E.164 bez +. Duplikaty są usuwane. Maksymalnie 10 numerów
from String wymagane Numer prezentowany odbiorcy (CLI), w tym samym formacie. Musi być numerem dozwolonym dla Twojego konta
mode String parallel Sposób dzwonienia: parallel - do wszystkich jednocześnie (w ramach limitu jednoczesnych połączeń konta), sequential - po kolei
stop_on_accept Boolean true Tylko z dtmf. Pierwsze potwierdzenie kończy alert: w trybie parallel pozostałe połączenia są rozłączane, w trybie sequential kolejne numery są pomijane. false - dzwonimy do wszystkich
message Object wymagane Komunikat. Dokładnie jedno z: tts lub playfile
message.tts.text String Tekst do odczytania (maks. 3000 znaków)
message.tts.lang String Głos, np. pl-PL/Maja - dostępne są wszystkie głosy z listy Voice API
message.playfile.url String Publicznie dostępny adres URL pliku WAV lub MP3 (http/https)
repeat Int 2 Ile razy odtworzyć komunikat (1-5)
dtmf Object brak Zbieranie potwierdzenia z klawiatury. Pominięcie oznacza alert typu powiadomienie
dtmf.accept String 1 Klawisz potwierdzenia: 0-9, * lub #
dtmf.reject String 0 Klawisz odrzucenia: 0-9, * lub #
dtmf.timeout Int 10 Czas oczekiwania na klawisz po ostatnim odtworzeniu komunikatu, w sekundach (1-60)
confirmation.accepted Object Komunikat (tts lub playfile) odtwarzany po potwierdzeniu, przed rozłączeniem. Wymaga dtmf
confirmation.rejected Object Komunikat odtwarzany po odrzuceniu, przed rozłączeniem. Wymaga dtmf
amd.enabled Boolean true Wykrywanie automatycznej sekretarki / poczty głosowej
amd.on_machine String hangup Co zrobić, gdy odbierze automat: hangup - rozłączyć, leave_message - poczekać na sygnał, odtworzyć komunikat raz i rozłączyć
ring_timeout Int 30 Czas dzwonienia w sekundach (5-120)
webhook String Adres URL, na który wysyłane są wyniki. Bez niego wyniki można pobrać zapytaniem GET
webhook_method String POST Metoda HTTP webhooka: POST lub PUT
reference String Własny identyfikator (maks. 128 znaków), zwracany w odpowiedziach i webhookach

Nieznane pola w zapytaniu powodują błąd 400.

Z włączonym wykrywaniem automatycznej sekretarki (amd) komunikat zaczyna się po rozpoznaniu, że odebrał człowiek - zwykle 1-3 sekundy po odebraniu połączenia.

Odpowiedź - alert przyjęty

Połączenia są kolejkowane i wykonywane w tle; odpowiedź wraca od razu.

{
    "result": "OK",
    "alert_id": "1f0c6a52-3a7e-4b8e-9d0a-5c1b2f7e8a90",
    "status": "in_progress",
    "mode": "parallel",
    "confirmable": true,
    "stop_on_accept": true,
    "reference": "incydent-4711",
    "created_at": "2026-11-05T10:00:00Z",
    "calls": [
        {"call_id": "8a2e4c1d-5b6f-4e7a-9c8d-1e2f3a4b5c6d", "to": "48600100200", "from": "48221234567", "status": "queued"},
        {"call_id": "93b17f0e-2c3d-4a5b-8e9f-0a1b2c3d4e5f", "to": "48600100201", "from": "48221234567", "status": "queued"}
    ]
}

Odpowiedź - odrzucenie alertu

{
    "result": "error",
    "error": "to: \"+48600100200\" is not valid, use E.164 digits without \"+\" or \"00\": 3-15 digits starting with the country code, e.g. 48600100200"
}

Przy braku stawki lub środków odpowiedź zawiera dodatkowo listę numerów, których dotyczy błąd:

{
    "result": "error",
    "error": "insufficient credit",
    "numbers": ["48600100201"]
}

Kody błędów HTTP

Kod HTTP Opis
200 Alert przyjęty, połączenia zakolejkowane
400 Nieprawidłowe zapytanie. Komunikat wskazuje pole, np. unknown field "numbers" lub repeat has an invalid type
401 Brak tokenu, token nieprawidłowy (invalid token) lub wygasły (token expired), nieznany użytkownik
402 Brak stawki dla numeru (no rate for destination) lub brak środków (insufficient credit); pole numbers zawiera numery, których dotyczy błąd
403 Numer from niedozwolony dla konta (caller id not allowed) lub konto zablokowane (account disabled)
413 Zbyt duże zapytanie
429 Przekroczony limit zapytań na sekundę (Too Many Requests. Exceeded limit of N CPS), nagłówek Retry-After: 1
503 Usługa chwilowo niedostępna (service temporarily unavailable) - należy ponowić zapytanie później

Środki sprawdzane są dla każdego numeru przed przyjęciem alertu oraz ponownie tuż przed wykonaniem każdego połączenia. Czas trwania połączenia jest ograniczony do kwoty dostępnej na koncie.

Status alertu

GET https://api.vpbx.pl/api/v1/alerts/[alert_id]
curl --header "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAoMS0Iiwi" \
--request GET \
https://api.vpbx.pl/api/v1/alerts/1f0c6a52-3a7e-4b8e-9d0a-5c1b2f7e8a90

Odpowiedź ma ten sam format co przy utworzeniu alertu, z aktualnym stanem połączeń:

{
    "result": "OK",
    "alert_id": "1f0c6a52-3a7e-4b8e-9d0a-5c1b2f7e8a90",
    "status": "completed",
    "outcome": "confirmed",
    "mode": "parallel",
    "confirmable": true,
    "stop_on_accept": true,
    "accepted_by": "8a2e4c1d-5b6f-4e7a-9c8d-1e2f3a4b5c6d",
    "reference": "incydent-4711",
    "created_at": "2026-11-05T10:00:00Z",
    "completed_at": "2026-11-05T10:00:22Z",
    "calls": [
        {
            "call_id": "8a2e4c1d-5b6f-4e7a-9c8d-1e2f3a4b5c6d",
            "to": "48600100200",
            "from": "48221234567",
            "status": "completed",
            "result": "accepted",
            "digit": "1",
            "amd_result": "human",
            "dialed_at": "2026-11-05T10:00:00Z",
            "answered_at": "2026-11-05T10:00:06Z",
            "ended_at": "2026-11-05T10:00:21Z",
            "duration_total": 21,
            "duration_answered": 15
        },
        {
            "call_id": "93b17f0e-2c3d-4a5b-8e9f-0a1b2c3d4e5f",
            "to": "48600100201",
            "from": "48221234567",
            "status": "completed",
            "result": "cancelled",
            "dialed_at": "2026-11-05T10:00:00Z",
            "ended_at": "2026-11-05T10:00:16Z",
            "duration_total": 16,
            "duration_answered": 0
        }
    ]
}

Wyniki dostępne są przez 7 dni od zakończenia alertu. Alert innego konta zwraca 404 (alert not found).

Pola alertu

Pole Typ Opis
result String Status zapytania. [OK | error]
alert_id String Identyfikator alertu (UUID)
status String [in_progress | completed]
outcome String Wynik, po zakończeniu: confirmed / not_confirmed (z dtmf), delivered / not_delivered (powiadomienie), cancelled (anulowany)
mode String [parallel | sequential]
confirmable Boolean true, gdy alert zbiera potwierdzenie (dtmf)
stop_on_accept Boolean Czy pierwsze potwierdzenie kończy alert
accepted_by String call_id pierwszego połączenia, które potwierdziło alert
reference String Własny identyfikator z zapytania
created_at, completed_at String Czas utworzenia i zakończenia, RFC3339 UTC
calls Array Połączenia, po jednym na numer, w kolejności z zapytania

Pola połączenia

Pole Typ Opis
call_id String Identyfikator połączenia (UUID)
to, from String Numer docelowy i prezentowany
status String [queued | dialing | in_progress | completed]
result String Wynik połączenia - patrz Wyniki połączeń
result_reason String Przyczyna wyniku busy, no_answer lub failed
digit String Naciśnięty klawisz
amd_result String Wynik wykrywania automatu: [human | machine | no_speech | not_sure | timeout]
dialed_at, answered_at, ended_at String Czas wybrania numeru, odebrania i zakończenia, RFC3339 UTC
duration_total Int Czas od wybrania numeru do rozłączenia, w sekundach. Obecne tylko w zakończonych połączeniach
duration_answered Int Czas rozmowy od odebrania do rozłączenia, w sekundach. Obecne tylko w zakończonych połączeniach

Wyniki połączeń

result Opis
accepted Naciśnięto klawisz potwierdzenia
rejected Naciśnięto klawisz odrzucenia
delivered Powiadomienie: odebrał człowiek i odsłuchał komunikat do końca co najmniej raz
no_input Komunikat odsłuchany, nie naciśnięto żadnego klawisza
invalid_input Naciśnięto wyłącznie inne klawisze
machine Odebrał automat, rozłączono
machine_message_left Odebrał automat, komunikat zostawiony po sygnale
no_answer Brak odebrania
busy Zajęte lub odrzucone
failed Połączenia nie udało się zestawić lub komunikatu nie udało się odtworzyć
hangup_before_input Odebrano, ale rozmówca rozłączył się przed wyborem / przed końcem komunikatu
cancelled Przerwane, bo alert potwierdził ktoś inny (tryb parallel) lub alert został anulowany
skipped Tryb sequential: numer pominięty, bo wcześniejsza osoba potwierdziła alert

Przyczyny (result_reason)

Pole result_reason wyjaśnia, dlaczego połączenie zakończyło się wynikiem busy, no_answer lub failed, zgodnie z informacją z sieci odbiorcy:

result result_reason Opis
busy busy Linia zajęta
busy declined Rozmówca odrzucił połączenie
no_answer ring_timeout Brak odebrania w czasie ring_timeout
no_answer no_response Sieć odbiorcy nie odpowiedziała na czas
no_answer unavailable Telefon wyłączony lub poza zasięgiem
failed number_not_found Numer nie istnieje lub jest nieaktywny
failed invalid_number Numer niepełny lub nieprawidłowy
failed call_rejected Sieć odbiorcy odmówiła połączenia (np. numer zablokowany)
failed incompatible_media Sieć odbiorcy nie obsługuje formatu dźwięku połączenia
failed network_unavailable Sieć odbiorcy nieosiągalna lub przeciążona - warto ponowić później
failed playback_failed Nie udało się odtworzyć komunikatu, np. nie można pobrać pliku z playfile.url
failed cli_not_allowed Numer from przestał być dozwolony przed wykonaniem połączenia
failed account_disabled Konto zostało zablokowane w trakcie alertu
failed no_rate, insufficient_credit Brak stawki lub środków w chwili wykonywania połączenia
failed service_unavailable Chwilowy problem po naszej stronie - warto ponowić później
failed unknown Inna przyczyna

Anulowanie alertu

DELETE https://api.vpbx.pl/api/v1/alerts/[alert_id]
curl --header "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAoMS0Iiwi" \
--request DELETE \
https://api.vpbx.pl/api/v1/alerts/1f0c6a52-3a7e-4b8e-9d0a-5c1b2f7e8a90
{
    "result": "OK",
    "alert_id": "1f0c6a52-3a7e-4b8e-9d0a-5c1b2f7e8a90",
    "status": "cancelling"
}

Zakolejkowane połączenia są anulowane, trwające - rozłączane. status ma wartość cancelling, dopóki trwa rozłączanie, lub completed, jeśli alert był już zakończony. Dla każdego rozłączonego połączenia wysyłany jest webhook alert-call-completed, a na końcu alert-completed z outcome: cancelled (lub confirmed, jeśli ktoś zdążył potwierdzić).

Webhooki

Wyniki wysyłane są na adres z pola webhook, metodą z webhook_method. W razie niepowodzenia dostarczenie jest ponawiane. Ten sam webhook może dotrzeć więcej niż raz - do wykrywania duplikatów należy używać event_type oraz call.call_id / alert_id.

alert-call-completed

Wysyłany raz dla każdego numeru, po zakończeniu połączenia.

{
    "event_type": "alert-call-completed",
    "timestamp": "2026-11-05T10:00:21Z",
    "alert_id": "1f0c6a52-3a7e-4b8e-9d0a-5c1b2f7e8a90",
    "reference": "incydent-4711",
    "call": {
        "call_id": "8a2e4c1d-5b6f-4e7a-9c8d-1e2f3a4b5c6d",
        "to": "48600100200",
        "from": "48221234567",
        "status": "completed",
        "result": "accepted",
        "digit": "1",
        "amd_result": "human",
        "dialed_at": "2026-11-05T10:00:00Z",
        "answered_at": "2026-11-05T10:00:06Z",
        "ended_at": "2026-11-05T10:00:21Z",
        "duration_total": 21,
        "duration_answered": 15
    }
}

alert-completed

Wysyłany raz, po zakończeniu wszystkich połączeń alertu (także tych rozłączonych, bo potwierdził ktoś inny).

{
    "event_type": "alert-completed",
    "timestamp": "2026-11-05T10:00:22Z",
    "alert_id": "1f0c6a52-3a7e-4b8e-9d0a-5c1b2f7e8a90",
    "reference": "incydent-4711",
    "outcome": "confirmed",
    "accepted_by": "8a2e4c1d-5b6f-4e7a-9c8d-1e2f3a4b5c6d",
    "summary": {"accepted": 1, "cancelled": 1},
    "calls": [ ... ]
}
Pole Typ Opis
event_type String [alert-call-completed | alert-completed]
timestamp String Czas zdarzenia, RFC3339 UTC
alert_id String Identyfikator alertu
reference String Własny identyfikator z zapytania
call Object alert-call-completed: końcowy stan połączenia (pola jak w Pola połączenia)
outcome String alert-completed: wynik alertu
accepted_by String alert-completed: call_id pierwszego potwierdzenia
summary Object alert-completed: liczba połączeń dla każdego wyniku
calls Array alert-completed: końcowy stan wszystkich połączeń