Gå til indholdet

Sådan ringes der til en kunde fra et n8n-workflow

September 2, 2026 · 7 minutter læsetid

Fra n8n kan der ringes til en kunde uden en eneste linje kode: tilføj en HTTP Request-node til workflowet, indstil den til POST https://volai.cz/v1/calls med headeren Authorization: Bearer vk_... og body {to, agentId} - så ringer stemmeagenten op præcis i det øjeblik, workflowet har brug for det. Nedenfor er hele fremgangsmåden: hvordan data fra workflowet mappes ind i agentens prompt, hvordan opkaldsresultatet kommer tilbage til n8n, og hvordan der også kan ringes uden agent, ved direkte at forbinde to numre.

Det skal der bruges

  • En n8n-instans - cloud eller selvhostet, versionen er ligegyldig.
  • En konto hos volai og en API-nøgle fra portalen (Indstillinger -> API og MCP) på formen vk_.... Gem den i n8n som en credential af typen Header Auth (headeren Authorization, værdien Bearer vk_...) - så undgås det, at nøglen skal skrives ind i hver node for sig.
  • Et eget telefonnummer med en stemmeagent - den oprettes i portalen eller via /docs/agent. Uden agent kan der også ringes ved direkte at forbinde to numre (varianten from nedenfor).

1Tilføj en HTTP Request-node

Metode POST, URL https://volai.cz/v1/calls, autentificering via den gemte credential, Body Content Type JSON. I body hører to (kundens nummer) og agentId (agentens ID fra portalen) hjemme - begge er obligatoriske, og der skal angives PRÆCIS ÉN af agentId/from/systemPrompt, aldrig flere på én gang.

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

Værdier med ={{...}} er n8n-udtryk - $json læser felter fra den foregående node i workflowet (for eksempel en webhook for en ny ordre eller en række fra en tabel).

2Map data fra workflowet ind i agentens prompt

variables er et objekt med tekststrenge som nøgler og værdier, som agenten modtager som dynamiske variabler - i sin systemprompt henviser den til dem med notationen {{customer_name}}. Grænser: højst 20 nøgler, nøglen op til 64 tegn, værdien op til 512 tegn. Navnene attempt_id, caller_number og called_number er reserveret - dem udfylder volai selv, og værdier med samme navn fra workflowet bliver kasseret.

ringingTimeoutSecs (5 til 60 sekunder, som standard 25) bestemmer, hvor længe der ringes, før et ubesvaret opkald afsluttes med endReason: "no_answer". Længere ringetid øger chancen for, at kunden når at tage telefonen, men optager også den udgående kanal længere.

3Læs svaret og vent evt. på resultatet

Et vellykket svar kommer med det samme, men opkaldet er på det tidspunkt kun lige begyndt - status er initiated, ikke en slutstatus:

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

Hvis der i det samme workflow skal ventes på resultatet i stedet for på en separat webhook, tilføjes lige efter HTTP Request-noden en anden, der kalder GET /v1/calls/{id} med parameteren waitSecs (0 til 45 sekunder) - serveren svarer, når opkaldet er slut, eller når grænsen er nået, alt efter hvad der indtræffer først:

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

Resultatet tilbage: webhook til n8n

I produktionsworkflows er det mere praktisk ikke at vente på resultatet i samme kørsel, men at få det leveret separat: tilføj i n8n (gerne i et nyt workflow) en Webhook-node, kopiér dens Production URL og registrér den én gang hos 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"]
  }'

Svaret indeholder secret (på formen whsec_...) - gem den, for den skal bruges til at verificere signaturen på hver indkommende forespørgsel. volai sender headeren Volai-Signature på formen t=<unix>,v1=<hex> - den fulde verificeringskode i både TypeScript og Python findes på /docs/webhooky. I en Code-node i n8n kan den ikke overføres uændret: HMAC beregnes over forespørgslens RÅ body, så på Webhook-noden skal indstillingen Raw Body slås til (uden den afleverer n8n allerede parset JSON, og signaturen stemmer ikke), og modulet crypto skal importeres med require, ikke med import. Behandl ikke body, før signaturen er verificeret.

Et eksempel på, hvad der lander i webhooken, når opkaldet er slut:

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": "God dag, jeg ringer angående ordre A-42." },
      { "role": "caller", "message": "Ja, det ved jeg godt, tak for påmindelsen." }
    ],
    "summary": "Kunden bekræftede modtagelsen af ordre A-42.",
    "data": {
      "order_confirmed": true
    }
  }
}

Nøglen data inde i data er valgfri og indeholder præcis de felter, som agenten har fået sat op i sine dataFields - eksemplet ovenfor (order_confirmed) er kun en illustration, de egentlige felter bliver andre. Den næste node i n8n kan så ud fra disse felter afgøre, om for eksempel CRM-systemet skal opdateres, eller opkaldet skal planlægges igen.

Hvordan ringes der uden agent?

Hvis der ikke er brug for en stemmeagent, og to numre blot skal forbindes (for eksempel en sælger med en kunde), sendes feltet from i stedet for agentId:

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

volai ringer først til from, og så snart nogen tager den, ringes der til to. Begge ben af opkaldet afregnes hver for sig til 0.92 CZK pr. minut (uden tillæg for agent). from er det nummer, hvor opkaldet tages af den, der ringer ud - typisk en mobil - og det kan være et hvilket som helst tjekkisk eller slovakisk nummer, gerne uden for kontoen hos volai. Betingelsen er en anden: kontoen skal have mindst ét EGET nummer hos volai, for det er fra det, opkaldet afregnes, og det er det nummer, der vises på skærmen hos begge parter (uden det returnerer volai fejlen bridge_needs_number). Pas blot på, at det ikke er et volai-nummer, der er dirigeret til en agent eller til SIP - nettet ville forbinde det første ben med det og ikke med den, der ringer ud, og opkaldet ville aldrig nå frem.

Hvad koster sådan et opkald?

Et udgående opkald via agent afregnes som summen af to takster: den udgående minut (0.92 CZK) og et tillæg for agenten oveni (2.50 CZK), begge fra prislisten gældende pr. 2. 9. 2026. Et opkald på halvanden minut kommer dermed til at koste 5.13 CZK:

PostTakst90 s opkald
Udgående minut0.92 CZK/min1.38 CZK
Tillæg for agent2.50 CZK/min3.75 CZK
I alt5.13 CZK

Der afregnes pr. sekund, ikke pr. påbegyndt minut - et kortere opkald koster forholdsmæssigt mindre. Den nøjagtige prisliste for alle poster (numre, agent, SMS) findes på /cenik.

Sikker gentagelse ved retry (Idempotency-Key)

n8n kan automatisk gentage en node ved fejl (Retry On Fail) - uden sikring ville det ved et telefonopkald betyde to rigtige opkald til den samme kunde. Tilføj headeren Idempotency-Key med en stabil værdi for én kørsel af workflowet (n8n tilbyder den som {{ $execution.id }}, eller der kan bruges et eget ordre-ID) - med den samme nøgle kommer svaret fra det første forsøg tilbage inden for 24 timer, og opkaldet oprettes ikke en anden gang:

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"}'

Hvad skal der gøres, når API'et returnerer en fejl?

Denne tabel dækker varianten med agentId og from - tilstande, der er specifikke for et prøveopkald uden nummer (systemPrompt), er bevidst ikke med her, de løses på anden vis.

StatusKodeHvad det betyder
400invalid_numberFeltet to er ikke et gyldigt telefonnummer.
400validationDer mangler en af agentId/from, eller der er flere end én; eventuelt ligger variables uden for grænsen, eller ringingTimeoutSecs uden for intervallet 5 til 60.
400on_dncNummeret står på listen over numre, der ikke må ringes til (/v1/dnc).
400destination_auto_blockedDestinationen er efter gentagne fejl automatisk blokeret i 30 dage.
400cannot_call_own_numberBeskyttelse mod løkker - der kan ikke ringes til et eget nummer hos volai.
404agent_not_foundagentId findes ikke eller tilhører ikke kontoen.
404agent_no_numberAgenten har ikke noget tildelt telefonnummer.
404bridge_needs_numberVed varianten from: kontoen skal have mindst ét eget nummer hos volai.
409destination_busyDer kører allerede et andet opkald til det samme nummer.
402insufficient_creditSaldoen dækker ikke minimum for at starte opkaldet.
503capacity_busyAlle udgående linjer er lige nu optaget, prøv igen om et minut.

Generel grænse i API'et

Ud over tabellen ovenfor gælder der endnu en fælles grænse for hele API'et: højst 60 forespørgsler pr. minut pr. API-nøgle (HTTP 429, rate_limited). Den bør der tages højde for i workflows, der ringer til mange numre i hurtig rækkefølge - den samme grænse gælder for POST /v1/messages og de øvrige endpoints under den samme nøgle.

Videre herfra

En komplet oversigt over, hvad der kan kaldes fra n8n (SMS, numre, DNC), findes i n8n-integrationen. Den fulde REST-reference findes på /docs/api, den fulde beskrivelse af webhook-hændelser og signaturverificering på /docs/webhooky.

Prøv volai - de første 50 kr er på os

Du opretter en konto på et minut og får straks en startsaldo til opkald og nummer - SMS kan sendes efter den første opfyldning.

Prøv gratis