Skip to main content

Inbound webhooks

Dynamische agent-routing — Finn belt je server wanneer er een inkomend gesprek binnenkomt.

6 min read

Inkomende webhooks

Wanneer er een inkomende oproep binnenkomt, kan Finn in realtime aan jouw server vragen met welke agent er moet worden opgenomen, welke variabelen moeten worden geïnjecteerd en welke kennisbank moet worden geladen. Zo kun je dynamisch routeren — op telefoonnummer van de beller, accountniveau, tijdstip, A/B-cohort of welke logica je backend ook hanteert.

Is een vaste agent voldoende, sla dit dan over. Wil je dynamische routering per oproep, dan is dit de juiste pagina.


Hoe het werkt

caller dials → Finn answers ring → Finn POSTs to your URL → your server returns agent + vars → Finn streams the voice agent

De handshake voegt ongeveer 100–200 ms latentie toe. Je endpoint moet binnen 500 ms antwoorden, anders valt Finn terug op de standaardagent.


Configureren

1. Stel de URL voor de inkomende webhook in

Dashboard → Instellingen → Telefoonnummers → [nummer] → URL inkomende webhook.

Plak je HTTPS-endpoint. Sla op. Finn stuurt er eenmalig een verzoek met een {"ping": true}-body naartoe om de bereikbaarheid te controleren.

Via de API

curl https://api.hirefinn.ai/v1/phone-numbers/ph_1234 \
  -X PATCH \
  -H "Authorization: Bearer $FINN_API_KEY" \
  -d '{ "inbound_webhook_url": "https://your-app.com/finn/inbound" }'

2. Stel een fallback-agent in

Configureer een standaard-Finn voor dat nummer — die wordt gebruikt als je webhook een time-out geeft, een fout retourneert of een ongeldig antwoord teruggeeft.


Payload van het verzoek

Finn stuurt een POST naar je URL met:

{
  "session_id": "ws_2H4abc",
  "request_type": "inbound",
  "phone_number_id": "ph_1234",
  "channel": "pstn",
  "from": {
    "phone": "+919876543210",
    "country": "IN",
    "carrier": "Airtel"
  },
  "to": {
    "phone": "+918765432100",
    "country": "IN"
  },
  "received_at": "2026-05-22T14:30:00Z",
  "metadata": {}
}

Bij WhatsApp-oproepen gebruikt het from-blok WhatsApp-specifieke velden:

"from": {
  "wa_id": "919876543210",
  "display_name": "Priya M",
  "profile_pic_url": "https://..."
},
"channel": "whatsapp"

Antwoord

Retourneer binnen 500 ms een JSON-body die beschrijft hoe de oproep gerouteerd moet worden:

{
  "finn_id": "fn_def456",
  "language": "hi-IN",
  "variables": {
    "customer_name": "Priya M",
    "account_tier": "premium",
    "last_order_id": "ord_99831",
    "preferred_agent": "Aria"
  },
  "knowledge_base_ids": ["kb_general", "kb_premium_perks"],
  "metadata": {
    "campaign_tag": "premium-support-q2",
    "ab_cohort": "B"
  },
  "recording_enabled": true,
  "max_call_duration_seconds": 600
}

Veldreferentie

VeldVerplicht?Opmerkingen
finn_idJaWelke agent opneemt. Moet bij je organisatie horen.
languageNeeOverschrijft de standaardtaal van de agent. en-US, hi-IN, es-ES, enz.
variablesNeeVrije verzameling sleutel/waarde-paren. Beschikbaar in de prompt als {variable_name}.
knowledge_base_idsNeeOverschrijft welke kennisbanken voor deze oproep worden geladen.
metadataNeeWordt toegevoegd aan het oproepverslag en de post-call webhook. Gebruik dit voor je eigen analytics-tagging.
recording_enabledNeeOverschrijft de standaardinstelling van de agent.
max_call_duration_secondsNeeHarde limiet. Standaard is 1800 (30 min).

Oproep weigeren

{ "action": "reject", "reason": "blocked_caller" }

Finn beëindigt de oproep met een ingesteld uitgaand bericht ("Dit nummer is niet meer in gebruik"). Gebruik dit voor DNC-handhaving, geblokkeerde accounts of ophangen buiten openingstijden.

Direct doorverbinden

{ "action": "transfer", "to": "+918888888888", "reason": "vip_route" }

Sla de AI-agent volledig over — verbind direct door naar een medewerker. Handig voor VIP- of escalatieniveaus.


Implementatievoorbeeld (Node)

import express from "express";

const app = express();
app.use(express.json());

app.post("/finn/inbound", async (req, res) => {
  const { from, to, channel } = req.body;

  // 1. Look up caller in CRM (must be fast — DB index by phone)
  const phone = from.phone ?? `+${from.wa_id}`;
  const customer = await crm.findByPhone(phone);

  // 2. Block known DNC
  if (customer?.dnc) {
    return res.json({ action: "reject", reason: "dnc" });
  }

  // 3. VIP → straight to human
  if (customer?.tier === "vip") {
    return res.json({
      action: "transfer",
      to: process.env.VIP_DESK_NUMBER!,
      reason: "vip_route",
    });
  }

  // 4. Localized agent based on caller country
  const language = from.country === "IN" ? "hi-IN" : "en-US";

  // 5. Pick agent + load context
  return res.json({
    finn_id: customer?.tier === "premium"
      ? "fn_premium_aria"
      : "fn_standard_aria",
    language,
    variables: {
      customer_name: customer?.name ?? "there",
      account_tier: customer?.tier ?? "standard",
      last_order_id: customer?.last_order_id ?? "",
    },
    knowledge_base_ids:
      customer?.tier === "premium" ? ["kb_general", "kb_premium"] : ["kb_general"],
    metadata: { ab_cohort: customer?.ab_cohort ?? "A" },
  });
});

app.listen(3000);

Handtekeningverificatie

Zelfde methode als webhooks na de oproep — HMAC-SHA256 over de ruwe body, header X-Finn-Signature.

const sig = req.headers["x-finn-signature"] as string;
const ok = verifyFinnSignature(rawBody, sig, process.env.FINN_INBOUND_SECRET!);
if (!ok) return res.status(401).send("bad signature");

Zie Webhooks na de oproep voor de volledige verificatie-implementatie.


Prestatie-eisen

Dit ligt op het kritieke pad — de beller hoort de beltoon terwijl er op je antwoord wordt gewacht.

MetriekDoel
Responstijd< 500 ms (p99)
Time-out1000 ms
Terugval bij time-outStandaardagent ingesteld op het telefoonnummer
Terugval bij 5xxStandaardagent
Terugval bij ongeldige JSONStandaardagent + fout gelogd

Tips om onder 500 ms te blijven

  • Cache CRM-zoekopdrachten per telefoonnummer gedurende 5 minuten
  • Gebruik een database in dezelfde regio — webhook vanuit us-east, DB in us-west = 80 ms per richting
  • Bereken routeringsbeslissingen vooraf — sla het antwoord op in het klantrecord, beslis niet live
  • Sla niet-essentiële verrijking over bij de inkomende stap — verplaats dat werk naar de webhook na de oproep

Voorscreening van bellers

De meeste apps gebruiken de inkomende webhook voor een van deze patronen:

PatroonUse case
Opzoeken + personaliserenKlantrecord ophalen, naam + tier als variabelen doorgeven
DNC-handhavingOproepen van geblokkeerde nummers weigeren
VIP-routeringAgent overslaan, direct doorverbinden naar een medewerker
Taaldetectiehi-IN kiezen voor India, en-US voor bellers uit de VS
A/B-testen50% van de oproepen naar een nieuwe agentversie routeren
Routering buiten kantoorurenAndere agent (of voicemail) buiten kantooruren
CampagnetrackingOproepen van specifieke trackingnummers taggen met een campagne-ID
Multi-tenant SaaSRouteren naar de tenant van wie het nummer is gebeld

Testen

Gebruik de Finn CLI om inkomende oproepen te simuleren zonder echte PSTN-oproep:

finn inbound simulate \
  --phone-number-id ph_1234 \
  --from +919876543210 \
  --webhook-url https://your-app.com/finn/inbound

De uitvoer toont je endpoint-respons, latency en de bepaalde agent + variabelen. Geschikt voor CI.


Veelvoorkomende valkuilen

SymptoomOplossing
Oproepen vallen altijd terug op de standaardagentJe endpoint doet er > 500 ms over. Controleer de timing in het webhook-log van Finn.
Beller hoort 2-3 s stilte voordat de agent spreektJe endpoint is traag, maar blijft binnen de timeout. Optimaliseer naar < 200 ms.
Variabelen verschijnen niet in de spraak van de agentVerschil tussen promptplaceholder {customer_name} en webhooksleutel customerName. Gebruik aan beide kanten snake_case.
Verkeerde agent nam opJe endpoint gaf een finn_id terug die bij een andere organisatie hoort. Controleer het eigenaarschap.
action: reject wordt niet gerespecteerdControleer of reason een string is en of de JSON-respons geldig is. Ongeldige responses → fallback.

Gerelateerd

Was this page helpful?

Still stuck or have feedback?

Email [email protected] or use the chat bubble in the bottom-right corner — it's a Finn that knows the Academy cold.