Przejdź do treści

Jak zadzwonić do klienta z workflow n8n

September 2, 2026 · 7 minut czytania

Z n8n można zadzwonić do klienta bez pisania ani jednej linijki kodu: proszę dodać do workflow węzeł HTTP Request i wskazać w nim POST https://volai.cz/v1/calls z nagłówkiem Authorization: Bearer vk_... i treścią {to, agentId} - a agent głosowy wykręci numer dokładnie wtedy, gdy workflow tego potrzebuje. Poniżej cała konfiguracja: jak przenieść dane z workflow do promptu agenta, jak odebrać wynik rozmowy z powrotem w n8n oraz jak dzwonić w ogóle bez agenta, łącząc bezpośrednio dwa numery.

Czego potrzeba

  • Instancji n8n - w chmurze lub self-hosted, w dowolnej nowszej wersji.
  • Konta volai i klucza API z portalu (Ustawienia -> API i MCP) w postaci vk_.... Proszę zapisać go w n8n jako credential typu Header Auth (nagłówek Authorization, wartość Bearer vk_...), żeby nie wpisywać klucza osobno w każdym węźle.
  • Własnego numeru telefonu z przypisanym agentem głosowym - można go utworzyć w portalu lub przez /docs/agent. Nie ma jeszcze agenta? Nadal można dzwonić, łącząc bezpośrednio dwa numery (wariant z polem from poniżej).

1Dodanie węzła HTTP Request

Metoda POST, adres URL https://volai.cz/v1/calls, uwierzytelnianie zapisanym credentialem, typ treści (Body Content Type) JSON. W treści wymagane są to (numer klienta) i agentId (identyfikator agenta z portalu) - oba obowiązkowe, a z agentId/from/systemPrompt można podać dokładnie JEDNO, nigdy więcej niż jedno.

json
{
  "to": "={{ $json.phone }}",
  "agentId": "ag_kx91fa2b",
  "variables": {
    "customer_name": "={{ $json.name }}",
    "order_number": "={{ $json.order }}"
  },
  "ringingTimeoutSecs": 30
}

Wartości zapisane jako ={{...}} to wyrażenia n8n - $json odczytuje pola z poprzedniego węzła workflow (na przykład z webhooka „nowe zamówienie” albo z wiersza arkusza).

2Przeniesienie danych z workflow do promptu agenta

variables to obiekt typu tekst-na-tekst, który agent otrzymuje jako zmienne dynamiczne - w prompcie systemowym odwołuje się się do nich jako {{customer_name}}. Limity: najwyżej 20 kluczy, klucze do 64 znaków, wartości do 512 znaków. Nazwy attempt_id, caller_number i called_number są zarezerwowane - volai wypełnia je samo, a pasujące wartości z workflow zostałyby odrzucone.

ringingTimeoutSecs (od 5 do 60 sekund, domyślnie 25) określa, jak długo telefon ma dzwonić, zanim nieodebrane połączenie zakończy się wartością endReason: "no_answer". Dłuższy czas zwiększa szansę, że klient odbierze, ale też dłużej zajmuje wychodzący slot.

3Odczyt odpowiedzi i opcjonalne czekanie na wynik

Poprawna odpowiedź wraca od razu, ale rozmowa dopiero się zaczęła - status to initiated, a nie stan końcowy:

json
{
  "id": "c_9d4e2b7f",
  "status": "initiated"
}

Jeśli na wynik ma się czekać w tym samym przebiegu workflow, zamiast korzystać z osobnego webhooka, wystarczy dodać tuż po węźle HTTP Request drugi węzeł, który wywołuje GET /v1/calls/{id} z parametrem waitSecs (od 0 do 45 sekund) - serwer odpowie, gdy rozmowa się zakończy albo upłynie limit czasu, zależnie od tego, co nastąpi pierwsze:

bash
curl "https://volai.cz/v1/calls/c_9d4e2b7f?waitSecs=30" \
  -H "Authorization: Bearer vk_YOUR_KEY"

Odbiór wyniku: webhook do n8n

W produkcyjnym workflow zwykle wygodniej nie czekać w trakcie przebiegu, tylko dostarczyć wynik osobno: proszę dodać w n8n węzeł Webhook (nowy workflow w zupełności wystarczy), skopiować jego Production URL i jednorazowo zarejestrować go w volai:

bash
curl -X PUT https://volai.cz/v1/webhook \
  -H "Authorization: Bearer vk_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-instance.app.n8n.cloud/webhook/volai-calls",
    "events": ["call.completed", "call.failed", "call.no_answer"]
  }'

Odpowiedź zawiera secret (w postaci whsec_...) - proszę go zapisać, będzie potrzebny do weryfikacji podpisu każdego przychodzącego żądania. volai wysyła nagłówek Volai-Signature w postaci t=<unix>,v1=<hex> - pełny kod weryfikacji w TypeScript i Pythonie znajduje się na stronie /docs/webhooky. Nie przenosi się on do węzła Code w n8n bez zmian: HMAC liczy się z SUROWEJ treści żądania, więc w węźle Webhook trzeba włączyć opcję Raw Body (bez niej n8n przekazuje już sparsowany JSON i podpis nigdy się nie zgodzi), a moduł crypto w węźle Code trzeba załadować przez require, nie import. Treści nie należy przetwarzać przed zweryfikowaniem podpisu.

To, co przychodzi po zakończeniu rozmowy:

json
{
  "event": "call.completed",
  "ts": 1756111640000,
  "data": {
    "id": "c_9d4e2b7f",
    "direction": "out",
    "from": "+420601234567",
    "to": "+420777123456",
    "status": "completed",
    "durationSecs": 90,
    "priceHal": 513,
    "answeredBy": "human",
    "endReason": "completed",
    "transcript": [
      { "role": "agent", "message": "Dzień dobry, dzwonię w sprawie zamówienia A-42." },
      { "role": "caller", "message": "Tak, już wiem, dziękuję za przypomnienie." }
    ],
    "summary": "Klient potwierdził odbiór zamówienia A-42.",
    "data": {
      "order_confirmed": true
    }
  }
}

Klucz data zagnieżdżony wewnątrz data jest opcjonalny i zawiera dokładnie te pola, które skonfigurowano agentowi w jego dataFields - powyższy przykład (order_confirmed) jest tylko ilustracją, w Państwa przypadku będzie inaczej. Kolejny węzeł n8n może wtedy rozgałęziać przebieg według tych pól, na przykład aktualizować rekord w CRM albo przełożyć rozmowę na inny termin.

Jak dzwonić bez agenta?

Gdy agent głosowy nie jest potrzebny, a chodzi tylko o bezpośrednie połączenie dwóch numerów (na przykład handlowca z klientem), proszę wysłać from zamiast agentId:

json
{
  "to": "+420777123456",
  "from": "+420601234567"
}

volai dzwoni najpierw na from, a gdy ktoś odbierze, wybiera to. Obie nogi połączenia są rozliczane osobno po 0.92 CZK za minutę (bez dopłaty za agenta). from to numer, na którym odbierają SAMI Państwo - zwykle komórka - i może to być dowolny numer czeski lub słowacki, nie musi należeć do konta volai. Prawdziwy wymóg jest inny: konto musi mieć co najmniej jeden własny numer volai, bo to przez niego połączenie jest rozliczane i to on wyświetla się obu stronom (bez niego volai zwróci bridge_needs_number). Tylko nie należy wpisywać do from numeru volai, który jest skierowany na agenta albo na SIP - sieć połączyłaby tę pierwszą nogę z agentem zamiast z Państwem i telefon nigdy by nie zadzwonił.

Ile kosztuje takie połączenie?

Połączenie wychodzące przez agenta jest rozliczane jako suma dwóch stawek: minuty połączenia wychodzącego (0.92 CZK) plus dopłaty za agenta NA TO (2.50 CZK), obie według cennika z 2 września 2026. Połączenie trwające 90 sekund kosztuje 5.13 CZK:

PozycjaStawkaRozmowa 90 s
Minuta połączenia wychodzącego0.92 CZK/min1.38 CZK
Dopłata za agenta2.50 CZK/min3.75 CZK
Razem5.13 CZK

Rozliczenie następuje co do sekundy, a nie za całe minuty - krótsza rozmowa kosztuje proporcjonalnie mniej. Pełny cennik (numery, agent, SMS) jest na stronie /cenik.

Bezpieczne ponawianie (Idempotency-Key)

n8n potrafi automatycznie ponowić węzeł po błędzie (Retry On Fail) - bez zabezpieczenia oznaczałoby to dwa prawdziwe telefony do tego samego klienta. Należy dodać nagłówek Idempotency-Key o wartości stałej w obrębie jednego przebiegu workflow (n8n udostępnia ją jako {{ $execution.id }}, można też użyć własnego numeru zamówienia) - przy tym samym kluczu w ciągu 24 godzin otrzymuje się z powrotem odpowiedź z pierwszej próby, a połączenie nie zostanie wykonane dwa razy:

bash
curl -X POST https://volai.cz/v1/calls \
  -H "Authorization: Bearer vk_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: reminder-order-A-42" \
  -d '{"to": "+420777123456", "agentId": "ag_kx91fa2b"}'

Co zrobić, gdy API zwróci błąd?

Tabela obejmuje warianty agentId i from - stany charakterystyczne dla rozmowy próbnej bez własnego numeru ( systemPrompt) są celowo pominięte, to inny przepływ.

StatusKodCo oznacza
400invalid_numberPole to nie zawiera prawidłowego numeru telefonu.
400validationBrakuje agentId/from albo podano oba; albo variables przekraczają limity, albo ringingTimeoutSecs jest poza zakresem 5 do 60.
400on_dncNumer znajduje się na Państwa liście zakazu dzwonienia (/v1/dnc).
400destination_auto_blockedTen cel został automatycznie zablokowany na 30 dni po powtarzających się niepowodzeniach.
400cannot_call_own_numberOchrona przed pętlą - nie można zadzwonić na własny numer volai.
404agent_not_foundagentId nie istnieje albo nie należy do Państwa konta.
404agent_no_numberAgent nie ma przypisanego numeru telefonu.
404bridge_needs_numberDla wariantu z from: konto musi mieć co najmniej jeden numer volai.
409destination_busyTrwa już inna rozmowa z tym samym numerem.
402insufficient_creditSaldo nie wystarcza na minimum potrzebne do rozpoczęcia rozmowy.
503capacity_busyWszystkie linie wychodzące są teraz zajęte, proszę spróbować ponownie za minutę.

Ogólny limit API

Poza powyższą tabelą obowiązuje jeden limit dla całego API: najwyżej 60 żądań na minutę na klucz API (HTTP 429, rate_limited). Warto o tym pamiętać w workflow, które wybiera dziesiątki numerów jeden po drugim - ten sam limit dotyczy POST /v1/messagesi każdego innego endpointu pod tym samym kluczem.

Co dalej

Pełny przegląd tego, co można robić z n8n (SMS, numery, lista zakazu dzwonienia), znajduje się w poradniku integracji z n8n. Pełna dokumentacja REST jest na stronie /docs/api, a kompletna lista zdarzeń webhooków wraz z weryfikacją podpisu na stronie /docs/webhooky.

Wypróbuj volai - pierwsze 50 kr na nasz koszt

Konto zakładają Państwo w minutę i od razu mają na nim saldo startowe na połączenia i numer - SMS-y wyślą Państwo po pierwszym doładowaniu.

Wypróbować za darmo