Skip to main content

Webhooks entrantes

Enrutamiento dinámico de agentes: Finn llama a tu servidor cuando llega una llamada entrante.

6 min read

Inbound Webhooks

Cuando llega una llamada de entrada, Finn puede preguntar ** su servidor** en tiempo real con qué agente responder, qué variables inyectar, y qué base de conocimiento para cargar. Esto le permite recorrer dinámicamente —por número de llamada, nivel de cuenta, hora del día, cohorte A/B, o cualquier lógica que su backend se preocupe.

Si un agente fijo es suficiente, omita esto. Si quieres dinamic per-call routing, esta es la página.

-..

Cómo funciona

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

El apretón de manos añade ~100–200ms de latencia. Su punto final debe responder en menos de 500ms o Finn vuelve al agente predeterminado.

-..

Configuración

1. Establecer la URL de Webhook inbound

Dashboard → Ajustes → Números de teléfono → [número] → Inbound Webhook URL.

Pruebe su punto final HTTPS. Guardar. Finn pings it once with a {"ping": true} body to verify reachability.

Via 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. Establecer un agente de descomposición

Configure un Finn predeterminado para ese número — utilizado si su webhook se da cuenta, errores o devuelve una respuesta inválida.

-..

Solicitud de carga útil

Finn POST a su 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": {}
}

Para las llamadas WhatsApp, el bloque from utiliza campos específicos de WhatsApp:

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

-..

Respuesta

Devuelve un cuerpo JSON dentro de 500 ms describiendo cómo hacer la llamada:

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

Referencia sobre el terreno

Silencioso Campo Silencioso requerido Silencio.. Silencio. Que agente responde. Debe pertenecer a su org TEN language TENIDO No TENIDO El lenguaje predeterminado del agente Override. en-US, hi-IN, es-ES, etc TEN variables TENIDO No TENIDA Bolso de clave/valor de forma gratuita. Disponible en el interior del aviso como {variable_name} TEN knowledge_base_ids TENIDO No ANTERIENTE Sobrevivir qué KBs están cargados para esta llamada TEN metadata TENIDO No TENIDO Acoplado al registro de llamadas y webhook post-call. Use para su propio etiquetado de analítica. Silencio TEN recording_enabled TENIDO No TENIDO Sobrevivir el agente predeterminado Silencio max_call_duration_seconds Silencio No Silencio Tapa dura. Por defecto es 1800 (30 min)

Rechazar la llamada

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

Finn termina la llamada con un mensaje de salida configurado ("Este número ya no está en servicio"). Use for DNC enforcement, blocked accounts, or after-hours hangups.

Transferencia inmediatamente

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

Skip the AI agent entirely — route directly to a human. Útil para nivel VIP / escalada.

-..

Ejemplo de aplicación (Nodo)

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);

-..

Verificación de la firma

El mismo esquema que los webhooks post-call — HMAC-SHA256 sobre el cuerpo crudo, cabecera 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");

Véase Post-call Webhooks para la plena implementación de la verificación.

-..

Requisitos de ejecución

Esto está en la ruta hot — el callador está escuchando tono de anillo mientras espera su respuesta.

TENIDO TERRITORIO TERRITORIO Silencio.. tiempo de respuesta permanente ** 500ms** (p99) TENIDO TERRENO TENIDO 1000ms Silencio Fallback en el timeout Silencio Agente predeterminado configurado en el número de teléfono ← Fallback en 5xx Silencio agente predeterminado ← Fallback en inválido JSON Silencioso agente predeterminado + error conectado

Consejos para golpear

  • Cache CRM Lookups por teléfono durante 5 minutos
  • Use una base de datos colocada — webhook from us-east, DB in us-west = 80ms each way
  • Pre-compute routing decisions — guardar la respuesta en el registro del cliente, no decida en vivo
  • **Enriquecimiento no esencial en el hop inbound: empujar ese trabajo al webhook post-call

-..

Caller pre-screening

La mayoría de las aplicaciones utilizan el Webhook inbound para uno de estos patrones:

TEN TERRITOR SON SON SON ANTE Silencio.. Silencio Lookup + personalize Silencio Pull customer record, pass name + tier as variables ← Rechazar llamadas de números bloqueados Silencio VIP routing** Silencio Skip agent, transfer straight to human desk ← Silencio Detección de idiomas ← Pick hi-IN for India, en-US for US callers Silencio TEN A/B testing Silencio Route 50% de las llamadas a una nueva versión de agente Silencio ** Después de las horas de enrutamiento** Silencio Diferente agente (o buzón de voz) fuera de las horas de negocios Silencio Campaign tracking Tag calls from specific tracking numbers with a campaign ID tención Silencio Multi-tenant SaaS Silencio Ruta al inquilino cuyo número fue marcado

-..

Pruebas

Utilice el Finn CLI para simular llamadas inbound sin un anillo PSTN real:

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

La salida muestra su respuesta de punto final, latencia y el agente resuelto + variables. Es amigable.

-..

Compras comunes

Silencioso Silencioso Silencio.. ← Las llamadas siempre se devuelven al agente predeterminado Silencio Su punto final es > 500ms. Chequee el registro webhook de Finn para el tiempo. Silencio TEN Caller escucha 2-3s de silencio antes de que el agente hable TEN Su punto final es lento pero no tiene tiempo. Optimizar a los 200m Silencio Variables no apareciendo en el discurso del agente Silencio Mismatch entre el marcador de lugar rápido {customer_name} y webhook key customerName. Use serpiente case ambos lados Silencio Agente equivocado respondió TENIDO Su punto final devolvió un finn_id que pertenece a otra org. Verificar la propiedad TEN action: reject no honrado Silencio Check reason es una cadena, respuesta JSON es válida. Respuestas inválidas → retroceso

-..

Relacionados

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.