Inbound webhooks
Dynamische agent-routing — Finn belt je server wanneer er een inkomend gesprek binnenkomt.
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
| Veld | Verplicht? | Opmerkingen |
|---|---|---|
finn_id | Ja | Welke agent opneemt. Moet bij je organisatie horen. |
language | Nee | Overschrijft de standaardtaal van de agent. en-US, hi-IN, es-ES, enz. |
variables | Nee | Vrije verzameling sleutel/waarde-paren. Beschikbaar in de prompt als {variable_name}. |
knowledge_base_ids | Nee | Overschrijft welke kennisbanken voor deze oproep worden geladen. |
metadata | Nee | Wordt toegevoegd aan het oproepverslag en de post-call webhook. Gebruik dit voor je eigen analytics-tagging. |
recording_enabled | Nee | Overschrijft de standaardinstelling van de agent. |
max_call_duration_seconds | Nee | Harde 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.
| Metriek | Doel |
|---|---|
| Responstijd | < 500 ms (p99) |
| Time-out | 1000 ms |
| Terugval bij time-out | Standaardagent ingesteld op het telefoonnummer |
| Terugval bij 5xx | Standaardagent |
| Terugval bij ongeldige JSON | Standaardagent + 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:
| Patroon | Use case |
|---|---|
| Opzoeken + personaliseren | Klantrecord ophalen, naam + tier als variabelen doorgeven |
| DNC-handhaving | Oproepen van geblokkeerde nummers weigeren |
| VIP-routering | Agent overslaan, direct doorverbinden naar een medewerker |
| Taaldetectie | hi-IN kiezen voor India, en-US voor bellers uit de VS |
| A/B-testen | 50% van de oproepen naar een nieuwe agentversie routeren |
| Routering buiten kantooruren | Andere agent (of voicemail) buiten kantooruren |
| Campagnetracking | Oproepen van specifieke trackingnummers taggen met een campagne-ID |
| Multi-tenant SaaS | Routeren 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
| Symptoom | Oplossing |
|---|---|
| Oproepen vallen altijd terug op de standaardagent | Je endpoint doet er > 500 ms over. Controleer de timing in het webhook-log van Finn. |
| Beller hoort 2-3 s stilte voordat de agent spreekt | Je endpoint is traag, maar blijft binnen de timeout. Optimaliseer naar < 200 ms. |
| Variabelen verschijnen niet in de spraak van de agent | Verschil tussen promptplaceholder {customer_name} en webhooksleutel customerName. Gebruik aan beide kanten snake_case. |
| Verkeerde agent nam op | Je endpoint gaf een finn_id terug die bij een andere organisatie hoort. Controleer het eigenaarschap. |
action: reject wordt niet gerespecteerd | Controleer 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.