Chatsy logoChatsy logo
Precios
Iniciar sesiónEmpieza gratis
Volver al blog
Tutoriales

Crear integraciones personalizadas de chatbots con webhooks y APIs

Una guía para desarrolladores sobre conectar chatbots de IA con sistemas externos usando webhooks, integraciones API y patrones event-driven, con ejemplos TypeScript y buenas prácticas de producción.

Asad Ali
Fundador y CEO
30 de marzo de 2026
19 min de lectura
Compartir:

Un chatbot de IA que puede responder preguntas es útil. Un chatbot que puede responder preguntas, crear tickets de soporte, buscar pedidos, actualizar registros CRM y activar workflows es transformador. El puente entre un chatbot independiente y uno integrado son los webhooks y las APIs.

Esta guía recorre los fundamentos de las integraciones basadas en webhooks, patrones comunes para conectar chatbots con sistemas externos y un tutorial completo paso a paso para crear un webhook que cree tickets de soporte cuando el chatbot no pueda resolver un problema.

Resumen rápido:

  • Los webhooks son callbacks HTTP que envían eventos desde tu chatbot a sistemas externos en tiempo real. Protégelos con firmas HMAC y valida cada payload entrante.
  • Patrones comunes de integración incluyen sincronización CRM (registrar conversaciones), creación de tickets (escalar problemas no resueltos), búsqueda de pedidos (traer datos en tiempo real) y actualizaciones de base de conocimiento (mantener contenido fresco).
  • Crea integraciones con lógica de retry y exponential backoff desde el primer día. La entrega de webhooks falla más a menudo de lo que esperas, y los fallos silenciosos erosionan la confianza.
Nuestra metodología

Esta guía sintetiza detalles operativos de tres categorías de fuentes:

  • Patrones de código de producción de repos open-source (por ejemplo, LangChain, LlamaIndex, documentación de pgvector y ejemplos de HuggingFace)
  • Investigación académica publicada en arxiv y en actas de conferencias sobre recuperación y generación
  • Debates de profesionales en r/MachineLearning, r/LocalLLaMA y r/LangChain donde ingenieros reportan restricciones reales de producción alrededor de webhooks e integraciones de chatbots

Evitamos afirmaciones puramente de marketing y priorizamos ejemplos que se despliegan en bases de código reales. Cuando citamos cifras de latencia o precisión, la metodología, dataset o condiciones de prueba se indican junto a ellas. Revisado por última vez: abril de 2026.

Fundamentos de webhooks

Un webhook es una solicitud HTTP POST enviada de un sistema a otro cuando ocurre un evento. A diferencia de polling (preguntar repetidamente "¿hay algo nuevo?"), los webhooks empujan eventos cuando suceden.

┌──────────────┐     Ocurre evento    ┌──────────────────┐
│   Plataforma │ ──── HTTP POST ────▶ │  Tu endpoint     │
│   chatbot    │                      │   webhook        │
└──────────────┘                      └────────┬─────────┘
                                               │
                                      Procesar evento
                                               │
                                      ┌────────▼─────────┐
                                      │ Sistema externo  │
                                      │ (CRM, helpdesk,  │
                                      │ sistema pedidos) │
                                      └──────────────────┘

Patrón de solicitud y respuesta

La plataforma de chatbot envía una solicitud POST con un payload JSON que describe el evento. Tu endpoint lo procesa y devuelve un código de estado 2xx para confirmar recepción.

typescript
// Lo que la plataforma de chatbot envía a tu webhook { "event": "conversation.escalated", "timestamp": "2026-03-30T14:22:00Z", "data": { "conversationId": "conv_abc123", "customerId": "cust_456", "customerEmail": "user@example.com", "messages": [ { "role": "user", "content": "My order hasn't arrived and it's been 2 weeks" }, { "role": "assistant", "content": "I apologize for the delay. Let me look into this..." }, { "role": "user", "content": "I want to speak to someone" } ], "metadata": { "agentConfidence": 0.32, "escalationReason": "customer_requested", "detectedIntent": "shipping_complaint" } } }

Tu endpoint debe responder rápido (menos de 5 segundos para la mayoría de plataformas). Si el procesamiento tarda más, acepta el webhook de inmediato y procesa de forma asíncrona:

typescript
// Bien: confirmar de inmediato, procesar async app.post("/webhooks/chatbot", async (req, res) => { // Validar firma primero (ver siguiente sección) if (!verifySignature(req)) { return res.status(401).json({ error: "Invalid signature" }); } // Confirmar recepción res.status(200).json({ received: true }); // Procesar de forma asíncrona processWebhookEvent(req.body).catch((err) => { console.error("Webhook processing failed:", err); }); });

Proteger webhooks con firmas HMAC

Cualquiera que descubra tu URL de webhook puede enviar eventos falsos. Las firmas HMAC lo evitan. La plataforma de chatbot firma cada payload con un secreto compartido, y tu endpoint verifica la firma antes de procesar.

typescript
import crypto from "crypto"; import type { Request } from "express"; const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET!; function verifySignature(req: Request): boolean { const signature = req.headers["x-webhook-signature"] as string; if (!signature) return false; const expectedSignature = crypto .createHmac("sha256", WEBHOOK_SECRET) .update(JSON.stringify(req.body)) .digest("hex"); // Comparación timing-safe para prevenir ataques de timing return crypto.timingSafeEqual( Buffer.from(signature, "hex"), Buffer.from(expectedSignature, "hex") ); }

Usa siempre timingSafeEqual en lugar de === para comparar firmas. Una comparación normal de strings filtra información sobre cuántos caracteres coinciden, algo que atacantes pueden explotar.

Idempotencia

Las plataformas de webhooks reintentan ante fallos, lo que significa que tu endpoint puede recibir el mismo evento varias veces. Diseña tu handler para ser idempotente: procesar el mismo evento dos veces debería producir el mismo resultado que procesarlo una vez.

typescript
import { Redis } from "ioredis"; const redis = new Redis(process.env.REDIS_URL); async function processWebhookEvent(event: WebhookEvent): Promise<void> { const eventId = event.data.conversationId + ":" + event.timestamp; // Revisar si ya procesamos este evento const alreadyProcessed = await redis.get(`webhook:processed:${eventId}`); if (alreadyProcessed) { console.log(`Skipping duplicate event: ${eventId}`); return; } // Procesar el evento await handleEvent(event); // Marcar como procesado con TTL de 24 h await redis.set(`webhook:processed:${eventId}`, "1", "EX", 86400); }

Patrones comunes de integración

1. Sincronización CRM

Registra cada conversación del chatbot en tu CRM para que ventas y soporte tengan contexto completo.

typescript
interface CRMContact { email: string; properties: Record<string, string>; } async function syncConversationToCRM(event: ConversationEndedEvent): Promise<void> { const { customerEmail, messages, metadata } = event.data; // Crear o actualizar el contacto en tu CRM const contact = await crmClient.contacts.createOrUpdate({ email: customerEmail, properties: { last_chatbot_interaction: new Date().toISOString(), chatbot_resolution_status: metadata.resolved ? "resolved" : "unresolved", chatbot_detected_intent: metadata.detectedIntent, }, }); // Registrar la conversación como actividad/nota await crmClient.activities.create({ contactId: contact.id, type: "chatbot_conversation", body: summarizeConversation(messages), metadata: { conversationId: event.data.conversationId, messageCount: messages.length, duration: metadata.durationSeconds, }, }); }

2. Creación de tickets

Cuando el chatbot no puede resolver un problema, crea automáticamente un ticket de soporte con contexto completo.

typescript
async function createSupportTicket(event: EscalationEvent): Promise<string> { const { customerId, customerEmail, messages, metadata } = event.data; const ticket = await helpdeskClient.tickets.create({ subject: `Chatbot escalation: ${metadata.detectedIntent}`, requester: { email: customerEmail }, priority: determinePriority(metadata), tags: ["chatbot-escalation", metadata.detectedIntent], description: formatTicketDescription(messages, metadata), customFields: { chatbot_conversation_id: event.data.conversationId, escalation_reason: metadata.escalationReason, ai_confidence_score: metadata.agentConfidence, }, }); return ticket.id; } function determinePriority(metadata: EscalationMetadata): "low" | "normal" | "high" | "urgent" { if (metadata.escalationReason === "customer_frustrated") return "high"; if (metadata.agentConfidence < 0.2) return "high"; if (metadata.detectedIntent.includes("billing")) return "normal"; return "normal"; } function formatTicketDescription( messages: Message[], metadata: EscalationMetadata ): string { const transcript = messages .map((m) => `**${m.role === "user" ? "Customer" : "AI"}:** ${m.content}`) .join("\n\n"); return `## Chatbot Escalation **Reason:** ${metadata.escalationReason} **AI Confidence:** ${(metadata.agentConfidence * 100).toFixed(0)}% **Detected Intent:** ${metadata.detectedIntent} ## Conversation Transcript ${transcript}`; }

3. Búsqueda de pedidos

El chatbot llama a tu API para obtener datos de pedido en tiempo real durante la conversación.

typescript
// Esta es una llamada API saliente desde el chatbot, no un webhook // Registrada como una "herramienta" que el chatbot puede invocar async function lookupOrder(orderId: string, customerEmail: string): Promise<OrderInfo> { const response = await fetch(`${ORDER_API_URL}/orders/${orderId}`, { headers: { Authorization: `Bearer ${ORDER_API_TOKEN}`, "Content-Type": "application/json", }, }); if (!response.ok) { throw new Error(`Order lookup failed: ${response.status}`); } const order = await response.json(); // Verificar que el pedido pertenece a este cliente if (order.customerEmail !== customerEmail) { throw new Error("Order does not belong to this customer"); } return { orderId: order.id, status: order.status, trackingNumber: order.trackingNumber, estimatedDelivery: order.estimatedDelivery, items: order.items.map((i: OrderItem) => ({ name: i.productName, quantity: i.quantity, })), }; }

4. Actualizaciones de base de conocimiento

Cuando cambia tu documentación, activa un webhook para reindexar el contenido actualizado en la base de conocimiento de tu chatbot.

typescript
// Webhook desde tu CMS cuando se publica o actualiza un artículo de ayuda app.post("/webhooks/cms-update", async (req, res) => { if (!verifySignature(req)) { return res.status(401).json({ error: "Invalid signature" }); } res.status(200).json({ received: true }); const { articleId, action, content, title, url } = req.body; switch (action) { case "published": case "updated": await chatbotKB.upsertDocument({ externalId: articleId, title, content, sourceUrl: url, lastUpdated: new Date().toISOString(), }); break; case "unpublished": await chatbotKB.deleteDocument({ externalId: articleId }); break; } });

Tutorial paso a paso: webhook de ticket de escalado

Construyamos un endpoint webhook completo que crea un ticket de soporte en un sistema externo cuando el chatbot escala una conversación. Usaremos Express, TypeScript y una API genérica de helpdesk.

Configuración del proyecto

bash
mkdir chatbot-webhook && cd chatbot-webhook npm init -y npm install express dotenv npm install -D typescript @types/express @types/node tsx npx tsc --init

El servidor webhook completo

typescript
// src/server.ts import express from "express"; import crypto from "crypto"; import dotenv from "dotenv"; dotenv.config(); const app = express(); app.use(express.json()); // --- Configuración --- const PORT = process.env.PORT || 3000; const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET!; const HELPDESK_API_URL = process.env.HELPDESK_API_URL!; const HELPDESK_API_KEY = process.env.HELPDESK_API_KEY!; // --- Tipos --- interface WebhookPayload { event: string; timestamp: string; data: { conversationId: string; customerId: string; customerEmail: string; customerName?: string; messages: Array<{ role: "user" | "assistant"; content: string }>; metadata: { agentConfidence: number; escalationReason: string; detectedIntent: string; sessionDurationSeconds: number; }; }; } interface TicketResponse { id: string; url: string; status: string; } // --- Verificación de firma --- function verifyWebhookSignature( payload: string, signature: string | undefined ): boolean { if (!signature) return false; const expected = crypto .createHmac("sha256", WEBHOOK_SECRET) .update(payload) .digest("hex"); try { return crypto.timingSafeEqual( Buffer.from(signature, "hex"), Buffer.from(expected, "hex") ); } catch { return false; } } // --- Cliente API de helpdesk --- async function createTicket(payload: WebhookPayload): Promise<TicketResponse> { const { data } = payload; const ticketBody = { subject: `[Chatbot Escalation] ${data.metadata.detectedIntent}`, requester: { email: data.customerEmail, name: data.customerName || "Customer", }, priority: data.metadata.agentConfidence < 0.3 ? "high" : "normal", tags: ["chatbot-escalation", data.metadata.detectedIntent], description: buildTicketDescription(data), custom_fields: { conversation_id: data.conversationId, escalation_reason: data.metadata.escalationReason, ai_confidence: data.metadata.agentConfidence, session_duration: data.metadata.sessionDurationSeconds, }, }; const response = await fetch(`${HELPDESK_API_URL}/tickets`, { method: "POST", headers: { Authorization: `Bearer ${HELPDESK_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify(ticketBody), }); if (!response.ok) { const errorBody = await response.text(); throw new Error( `Helpdesk API error: ${response.status} - ${errorBody}` ); } return response.json() as Promise<TicketResponse>; } function buildTicketDescription( data: WebhookPayload["data"] ): string { const transcript = data.messages .map((m) => { const speaker = m.role === "user" ? "Customer" : "AI Agent"; return `**${speaker}:** ${m.content}`; }) .join("\n\n"); return `## Escalation Details | Field | Value | |-------|-------| | Conversation ID | ${data.conversationId} | | Escalation Reason | ${data.metadata.escalationReason} | | AI Confidence | ${(data.metadata.agentConfidence * 100).toFixed(0)}% | | Detected Intent | ${data.metadata.detectedIntent} | | Session Duration | ${data.metadata.sessionDurationSeconds}s | ## Conversation Transcript ${transcript}`; } // --- Endpoint webhook --- app.post("/webhooks/escalation", (req, res) => { // Paso 1: verificar firma const rawBody = JSON.stringify(req.body); const signature = req.headers["x-webhook-signature"] as string; if (!verifyWebhookSignature(rawBody, signature)) { console.warn("Invalid webhook signature received"); return res.status(401).json({ error: "Invalid signature" }); } const payload = req.body as WebhookPayload; // Paso 2: validar tipo de evento if (payload.event !== "conversation.escalated") { return res.status(200).json({ skipped: true, reason: "Unhandled event type" }); } // Paso 3: confirmar de inmediato res.status(200).json({ received: true, conversationId: payload.data.conversationId }); // Paso 4: procesar de forma asíncrona createTicket(payload) .then((ticket) => { console.log( `Ticket created: ${ticket.id} for conversation ${payload.data.conversationId}` ); }) .catch((err) => { console.error( `Failed to create ticket for ${payload.data.conversationId}:`, err ); // En producción: enviar a una dead-letter queue para retry }); }); // Health check app.get("/health", (_, res) => { res.status(200).json({ status: "ok" }); }); app.listen(PORT, () => { console.log(`Webhook server listening on port ${PORT}`); });

Variables de entorno

bash
# .env PORT=3000 WEBHOOK_SECRET=your-shared-secret-from-chatbot-platform HELPDESK_API_URL=https://api.yourhelpdesk.com/v2 HELPDESK_API_KEY=your-helpdesk-api-key

Ejecutarlo

bash
npx tsx src/server.ts

Patrones de autenticación

Distintos sistemas externos requieren distintos mecanismos de autenticación. Estos son los tres patrones más comunes:

API Keys

El enfoque más simple. Incluye una key estática en el header de la solicitud.

typescript
const response = await fetch(apiUrl, { headers: { "X-API-Key": process.env.EXTERNAL_API_KEY!, "Content-Type": "application/json", }, body: JSON.stringify(payload), });

Pros: simple, sin lógica de refresh de token. Contras: rotar la key requiere redeploy. Sin scoping ni expiración.

OAuth 2.0 Client Credentials

Para comunicación machine-to-machine donde tu servidor se autentica como sí mismo (no en nombre de un usuario).

typescript
class OAuth2Client { private accessToken: string | null = null; private tokenExpiry: number = 0; constructor( private clientId: string, private clientSecret: string, private tokenUrl: string ) {} async getAccessToken(): Promise<string> { // Devolver token cacheado si sigue siendo válido if (this.accessToken && Date.now() < this.tokenExpiry - 60_000) { return this.accessToken; } const response = await fetch(this.tokenUrl, { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: new URLSearchParams({ grant_type: "client_credentials", client_id: this.clientId, client_secret: this.clientSecret, scope: "tickets:write contacts:read", }), }); if (!response.ok) { throw new Error(`Token request failed: ${response.status}`); } const data = await response.json(); this.accessToken = data.access_token; this.tokenExpiry = Date.now() + data.expires_in * 1000; return this.accessToken; } async authenticatedFetch(url: string, options: RequestInit = {}): Promise<Response> { const token = await this.getAccessToken(); return fetch(url, { ...options, headers: { ...options.headers, Authorization: `Bearer ${token}`, }, }); } }

JWT (JSON Web Tokens)

Genera tokens de vida corta firmados con tu clave privada. El sistema externo verifica la firma con tu clave pública.

typescript
import jwt from "jsonwebtoken"; function generateServiceToken(): string { return jwt.sign( { iss: "chatbot-webhook-service", sub: "webhook-integration", aud: "helpdesk-api", iat: Math.floor(Date.now() / 1000), exp: Math.floor(Date.now() / 1000) + 300, // Expira en 5 minutos }, process.env.JWT_PRIVATE_KEY!, { algorithm: "RS256" } ); }

Manejo de errores y lógica de retry

La entrega de webhooks a APIs externas falla regularmente: timeouts de red, rate limits, errores transitorios de servidor. Construye lógica de retry desde el inicio.

Exponential backoff con jitter

typescript
interface RetryConfig { maxRetries: number; baseDelayMs: number; maxDelayMs: number; } async function withRetry<T>( fn: () => Promise<T>, config: RetryConfig = { maxRetries: 3, baseDelayMs: 1000, maxDelayMs: 30000 } ): Promise<T> { let lastError: Error | null = null; for (let attempt = 0; attempt <= config.maxRetries; attempt++) { try { return await fn(); } catch (err) { lastError = err as Error; if (attempt === config.maxRetries) break; // No reintentar errores de cliente (4xx excepto 429) if (err instanceof ApiError && err.status >= 400 && err.status < 500 && err.status !== 429) { throw err; } // Exponential backoff con jitter const exponentialDelay = config.baseDelayMs * Math.pow(2, attempt); const jitter = Math.random() * config.baseDelayMs; const delay = Math.min(exponentialDelay + jitter, config.maxDelayMs); console.warn( `Attempt ${attempt + 1} failed, retrying in ${Math.round(delay)}ms:`, (err as Error).message ); await sleep(delay); } } throw lastError; } function sleep(ms: number): Promise<void> { return new Promise((resolve) => setTimeout(resolve, ms)); } // Uso const ticket = await withRetry(() => createTicket(payload), { maxRetries: 3, baseDelayMs: 1000, maxDelayMs: 30000, });

Dead-letter queue

Para eventos que fallan después de todos los retries, envíalos a una dead-letter queue para revisión manual o procesamiento posterior:

typescript
async function processWebhookWithDLQ(payload: WebhookPayload): Promise<void> { try { await withRetry(() => createTicket(payload)); } catch (err) { console.error("All retries exhausted. Pushing to DLQ:", err); await deadLetterQueue.push({ payload, error: (err as Error).message, failedAt: new Date().toISOString(), retryCount: 3, }); // Alertar al equipo await alerting.notify({ channel: "webhook-failures", message: `Webhook processing failed for conversation ${payload.data.conversationId}`, severity: "warning", }); } }

Probar webhooks localmente

ngrok

Expón tu servidor local a internet para que la plataforma de chatbot pueda alcanzarlo:

bash
# Terminal 1: ejecutar tu servidor npx tsx src/server.ts # Terminal 2: exponer puerto 3000 ngrok http 3000

ngrok te da una URL pública como https://a1b2c3d4.ngrok.io. Regístrala como tu URL de webhook en la configuración de la plataforma de chatbot, añadiendo tu path: https://a1b2c3d4.ngrok.io/webhooks/escalation.

Prueba manual con curl

Envía un evento de prueba a tu endpoint local:

bash
# Generar una firma válida SECRET="your-shared-secret" PAYLOAD='{"event":"conversation.escalated","timestamp":"2026-03-30T14:22:00Z","data":{"conversationId":"conv_test123","customerId":"cust_456","customerEmail":"test@example.com","messages":[{"role":"user","content":"I need help"},{"role":"assistant","content":"Let me connect you with a human agent."}],"metadata":{"agentConfidence":0.25,"escalationReason":"low_confidence","detectedIntent":"general_inquiry","sessionDurationSeconds":45}}}' SIGNATURE=$(echo -n "$PAYLOAD" | openssl dgst -sha256 -hmac "$SECRET" | awk '{print $2}') curl -X POST http://localhost:3000/webhooks/escalation \ -H "Content-Type: application/json" \ -H "x-webhook-signature: $SIGNATURE" \ -d "$PAYLOAD"

webhook.site

Para inspeccionar qué está enviando realmente tu plataforma de chatbot, usa webhook.site. Te da una URL temporal que registra todas las solicitudes entrantes: headers, body, timing. Útil para depurar problemas de formato de payload antes de escribir código.

Monitoring y debugging en producción

Logging estructurado

Registra cada evento webhook con contexto suficiente para depurar fallos:

typescript
import pino from "pino"; const logger = pino({ level: "info" }); app.post("/webhooks/escalation", (req, res) => { const conversationId = req.body?.data?.conversationId || "unknown"; const childLogger = logger.child({ conversationId, event: req.body?.event }); if (!verifyWebhookSignature(JSON.stringify(req.body), req.headers["x-webhook-signature"] as string)) { childLogger.warn("Invalid webhook signature"); return res.status(401).json({ error: "Invalid signature" }); } childLogger.info("Webhook received"); res.status(200).json({ received: true }); createTicket(req.body as WebhookPayload) .then((ticket) => { childLogger.info({ ticketId: ticket.id }, "Ticket created successfully"); }) .catch((err) => { childLogger.error({ err }, "Ticket creation failed"); }); });

Métricas clave a rastrear

MétricaQué te diceUmbral de alerta
Tasa de recepción de webhooksVolumen de eventos desde la plataforma de chatbotCaída repentina (problema de plataforma) o pico (abuso)
Tasa de fallo de validación de firmaPosibles problemas de seguridad o mala configuración>1% de solicitudes
Tasa de éxito de procesamientoQué tan fiable manejas eventos<95%
Latencia de API externaQué tan rápido responde helpdesk/CRMP95 > 5 s
Tasa de retryQué tan a menudo falla el primer intento>10% indica problemas upstream
Profundidad de DLQEventos que fallaron todos los retries>0 requiere investigación

Endpoint de health check

Expón un health check que verifique conectividad con dependencias externas:

typescript
app.get("/health", async (_, res) => { const checks: Record<string, "ok" | "error"> = {}; try { await fetch(`${HELPDESK_API_URL}/health`, { headers: { Authorization: `Bearer ${HELPDESK_API_KEY}` }, signal: AbortSignal.timeout(3000), }); checks.helpdesk = "ok"; } catch { checks.helpdesk = "error"; } try { await redis.ping(); checks.redis = "ok"; } catch { checks.redis = "error"; } const allHealthy = Object.values(checks).every((v) => v === "ok"); res.status(allHealthy ? 200 : 503).json({ status: allHealthy ? "ok" : "degraded", checks }); });

Ideas clave

  1. Los webhooks empujan eventos en tiempo real desde tu chatbot a sistemas externos. Protégelos siempre con firmas HMAC y valida antes de procesar.
  2. Confirma rápido, procesa async. Devuelve 200 de inmediato y maneja el trabajo en segundo plano para evitar timeouts.
  3. Construye lógica de retry desde el primer día. Exponential backoff con jitter y una dead-letter queue para retries agotados te salva de pérdida silenciosa de datos.
  4. La idempotencia no es opcional. Las plataformas de webhooks reintentan, y tu handler debe producir el mismo resultado al procesar el mismo evento dos veces.
  5. Monitorea todo. Rastrea tasa de recepción, éxito de procesamiento, latencia de API externa y profundidad de DLQ. Los fallos silenciosos se acumulan rápido.

Cuándo los webhooks son la elección de integración equivocada

  • Flujos síncronos request-response donde el llamador necesita la respuesta en el mismo turno HTTP, no un callback
  • Streams de eventos de alta frecuencia donde el fan-out de webhooks sobrecarga servicios downstream y una queue encaja mejor
  • Redes no confiables donde los receptores no pueden validar firmas y la protección contra replay se vuelve frágil
  • Workflows que necesitan procesamiento ordenado y exactly-once, ya que la mayoría de entregas webhook son at-least-once y fuera de orden
  • Integraciones hacia sistemas detrás de firewalls estrictos donde exponer un endpoint entrante no es viable
  • Equipos sin observabilidad para retries, dead-letter queues y fallos de firma, donde los webhooks pierden eventos en silencio

Preguntas frecuentes

¿Cuál es la diferencia entre un webhook y una API?

Una API es un conjunto de endpoints que llamas cuando necesitas datos (modelo pull). Un webhook es un callback HTTP que un sistema remoto te envía cuando ocurre un evento (modelo push). En integraciones de chatbots, usas APIs para obtener datos (por ejemplo, buscar un pedido) y webhooks para reaccionar a eventos (por ejemplo, crear un ticket cuando una conversación se escala). La mayoría de integraciones usan ambos: los webhooks disparan el workflow y las llamadas API dentro de ese workflow interactúan con sistemas externos.

¿Cómo protejo mi endpoint webhook?

Usa firmas HMAC. La plataforma de chatbot firma cada payload con un secreto compartido usando HMAC-SHA256. Tu endpoint recalcula la firma desde el cuerpo crudo de la solicitud y el mismo secreto, luego las compara con una función timing-safe. Rechaza cualquier solicitud donde la firma no coincida. Además, usa HTTPS para cifrado de transporte, restringe tu endpoint a rangos IP esperados si tu plataforma los publica y valida el esquema del payload antes de procesar.

¿Qué pasa si mi endpoint webhook está caído?

La mayoría de plataformas de chatbots implementan retries automáticos con exponential backoff. Si tu endpoint devuelve un código no 2xx o hace timeout, la plataforma reintenta, normalmente 3-5 veces durante varias horas. Los eventos que agotan todos los retries suelen registrarse en el dashboard de entregas webhook de la plataforma. De tu lado, diseña para idempotencia para que cuando el endpoint se recupere y lleguen retries, el procesamiento duplicado no cree tickets o registros duplicados.

¿Cómo pruebo webhooks durante desarrollo local?

Usa ngrok para exponer tu servidor local a internet. Ejecuta tu servidor en localhost, luego ejecuta ngrok http 3000 para obtener una URL pública. Registra esa URL como tu endpoint webhook en la plataforma de chatbot. Para pruebas manuales, usa curl con una firma HMAC precomputada para enviar payloads de prueba directamente. Para inspeccionar los payloads crudos que envía tu plataforma, usa webhook.site para capturar y mostrar solicitudes entrantes.

¿Cómo manejo eventos webhook que requieren procesamiento lento?

Acepta el webhook de inmediato con una respuesta 200 y luego procesa el evento de forma asíncrona. Para casos simples, usa una llamada async después de responder. Para sistemas de producción, envía el evento a una queue de mensajes (como SQS, Redis Streams o BullMQ) y procésalo con un worker separado. Esto desacopla recepción de procesamiento, evita timeouts y te permite escalar workers de forma independiente según profundidad de queue.


Artículos relacionados

  • La guía completa para crear chatbots de IA en 2026
  • Cómo añadir live chat a tu sitio web
  • Crear para escala: manejar millones de documentos
  • Expansión de consultas con IA: hacer agentes 10 veces más inteligentes
#webhooks#API#integration#tutorial#developer#chatbot
AnteriorChatbot para firmas contables y fiscales: soporte a clientes y automatización
Más reciente Reseña de Botpress 2026: probamos el creador de chatbots open-source
Relacionado

Artículos relacionados

Tutoriales

Cómo añadir un chatbot a tu sitio web (WordPress, Shopify, Wix y más)

Guía paso a paso para añadir un chatbot de IA a cualquier sitio web. Cubre WordPress, Shopify, Wix, Squarespace y sitios personalizados en menos de 10 minutos.

Tutoriales

Cómo añadir live chat a tu sitio web en 2026 (paso a paso)

Una guía completa para añadir live chat a tu sitio web. Desde elegir una plataforma hasta instalar el widget, personalizarlo, entrenar la IA y salir en vivo, con instrucciones específicas para Shopify, WordPress, Webflow y sitios personalizados.

Tutoriales

Cómo crear una base de conocimiento que realmente reduzca tickets

Una guía práctica para construir una base de conocimiento que desvíe tickets de soporte. Desde planificar contenido y escribir artículos hasta organizar categorías, SEO para contenido de ayuda y medir impacto, con plantillas y consejos de recuperación para IA.

¿Listo para probar Chatsy?

Crea tu propio agente de soporte al cliente con IA en minutos, sin código.

Comenzar prueba gratis

¿Listo para transformar tu
soporte al cliente?

Implementa agentes de soporte de IA que resuelven problemas, actúan y encantan a tus clientes.

Empieza gratisNo se requiere tarjeta de crédito
Chatsy logoChatsy logo

Plataforma de soporte al cliente con IA, chat en vivo, transferencia humana, base de conocimiento y tickets.

Producto

  • Funciones
  • Precios
  • Integraciones

Soluciones

  • Ecommerce
  • SaaS
  • Salud
  • Servicios financieros

Recursos

  • Blog
  • Estadísticas
  • Comparar
  • Alternativas
  • Plantillas
  • Glosario
  • Calculadora de ROI
  • Feed RSS

Empresa

  • Acerca de
  • Contacto
  • Política de privacidad
  • Términos de servicio

© 2026 Chatsy. Todos los derechos reservados.

Idioma
EnglishEspañol

10685-B Hazelhurst Dr. # 21148, Houston, TX 77043, USA