Skip to main content

Webhook in entrata

Instradamento dinamico dell'agente — Finn chiama il tuo server quando arriva una chiamata in entrata.

6 min read

Webhook in entrata

Quando arriva una chiamata in entrata, Finn può chiedere in tempo reale al tuo server con quale agente rispondere, quali variabili inserire e quale knowledge base caricare. Questo permette di instradare le chiamate in modo dinamico: per numero del chiamante, livello dell'account, fascia oraria, coorte A/B o qualsiasi logica gestita dal tuo backend.

Se ti basta un agente fisso, salta questa pagina. Se invece vuoi un instradamento dinamico per singola chiamata, questa è la pagina giusta.


Come funziona

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

L'handshake aggiunge circa 100–200 ms di latenza. Il tuo endpoint deve rispondere in meno di 500 ms, altrimenti Finn ricade sull'agente predefinito.


Configurazione

1. Imposta l'URL del webhook in entrata

Dashboard → Impostazioni → Numeri di telefono → [numero] → URL webhook in entrata.

Incolla il tuo endpoint HTTPS e salva. Finn lo contatta una volta con un body {"ping": true} per verificarne la raggiungibilità.

Tramite 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. Imposta un agente di fallback

Configura un Finn predefinito per quel numero: viene usato se il webhook va in timeout, restituisce un errore o una risposta non valida.


Payload della richiesta

Finn invia una POST al tuo URL con:

{
  "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": {}
}

Per le chiamate WhatsApp, il blocco from usa campi specifici di WhatsApp:

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

Risposta

Restituisci entro 500 ms un body JSON che descrive come instradare la chiamata:

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

Riferimento dei campi

CampoObbligatorio?Note
finn_idQuale agente risponde. Deve appartenere alla tua organizzazione.
languageNoSovrascrive la lingua predefinita dell'agente. en-US, hi-IN, es-ES, ecc.
variablesNoInsieme libero di coppie chiave/valore. Disponibile nel prompt come {variable_name}.
knowledge_base_idsNoSovrascrive quali knowledge base vengono caricate per questa chiamata.
metadataNoAllegato al record della chiamata e al webhook post-chiamata. Usalo per il tagging delle tue analisi.
recording_enabledNoSovrascrive il valore predefinito dell'agente.
max_call_duration_secondsNoLimite massimo. Il valore predefinito è 1800 (30 min).

Rifiuta la chiamata

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

Finn chiude la chiamata con un messaggio in uscita configurato ("Questo numero non è più attivo"). Da usare per l'applicazione delle liste DNC, gli account bloccati o le chiusure fuori orario.

Trasferisci subito

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

Salta del tutto l'agente AI: instrada direttamente a un operatore. Utile per i livelli VIP / di escalation.


Esempio di implementazione (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);

Verifica della firma

Stesso schema dei webhook post-chiamata: HMAC-SHA256 sul corpo raw, 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");

Vedi Webhook post-chiamata per l'implementazione completa della verifica.


Requisiti di prestazione

Questo passaggio è sul percorso critico: il chiamante sente il segnale di libero mentre attende la tua risposta.

MetricaObiettivo
Tempo di risposta< 500 ms (p99)
Timeout1000 ms
Fallback in caso di timeoutAgente predefinito configurato sul numero di telefono
Fallback in caso di 5xxAgente predefinito
Fallback in caso di JSON non validoAgente predefinito + errore registrato

Consigli per restare sotto i 500 ms

  • Metti in cache le ricerche CRM per numero di telefono per 5 minuti
  • Usa un database colocato: webhook da us-east e DB in us-west significa 80 ms per tratta
  • Pre-calcola le decisioni di instradamento: salva la risposta sulla scheda del cliente, non deciderla al volo
  • Salta l'arricchimento non essenziale nel passaggio inbound: sposta quel lavoro sul webhook post-chiamata

Pre-screening del chiamante

La maggior parte delle applicazioni usa il webhook inbound per uno di questi schemi:

SchemaCaso d'uso
Ricerca + personalizzazioneRecupera il record cliente, passa nome e livello come variabili
Applicazione DNCRifiuta le chiamate da numeri bloccati
Instradamento VIPSalta l'agente, trasferisci direttamente a un operatore
Rilevamento della linguaScegli hi-IN per l'India, en-US per i chiamanti dagli Stati Uniti
Test A/BInstrada il 50% delle chiamate a una nuova versione dell'agente
Instradamento fuori orarioAgente diverso (o segreteria) fuori dall'orario lavorativo
Monitoraggio delle campagneEtichetta le chiamate da specifici numeri di tracciamento con un ID campagna
SaaS multi-tenantInstrada al tenant il cui numero è stato composto

Test

Usa la CLI di Finn per simulare chiamate in entrata senza uno squillo PSTN reale:

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

L'output mostra la risposta dell'endpoint, la latenza e l'agente e le variabili risolte. Compatibile con CI.


Problemi comuni

SintomoSoluzione
Le chiamate ricadono sempre sull'agente predefinitoL'endpoint supera i 500 ms. Controlla i tempi nel log dei webhook di Finn.
Il chiamante sente 2-3 s di silenzio prima che l'agente parliL'endpoint è lento ma entro il timeout. Ottimizzalo a meno di 200 ms.
Le variabili non compaiono nel parlato dell'agenteDiscrepanza tra il segnaposto del prompt {customer_name} e la chiave del webhook customerName. Usa snake_case su entrambi i lati.
Ha risposto l'agente sbagliatoL'endpoint ha restituito un finn_id che appartiene a un'altra organizzazione. Verifica la proprietà.
action: reject non rispettatoVerifica che reason sia una stringa e che il JSON di risposta sia valido. Risposte non valide → fallback.

Correlati

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.