Saltar al contenido
Ver todas las notas
whatsappiaautomatizaciónarquitectura

Un chatbot de WhatsApp con la API oficial y un LLM detrás

Lo que nadie te cuenta antes de montar un bot de WhatsApp con IA: la ventana de 24 horas y su cobro por mensaje desde octubre de 2026, por qué el webhook no puede esperar al modelo, y cómo evitar que el asistente invente respuestas.

· 11 min de lectura

Casi todos los tutoriales de "chatbot de WhatsApp" empiezan conectando un número personal con una librería no oficial. Funciona en diez minutos. Y funciona hasta que Meta bloquea el número, que suele ser cuando el bot ya atiende clientes de verdad.

Esta nota es sobre el otro camino: la API oficial de WhatsApp Business, con un modelo de lenguaje detrás. Es más lento de arrancar y no se cae.

Lo que Meta exige antes de escribir una línea#

Esta es la parte que descoloca a todo el mundo, porque no es código:

  1. Una cuenta de Meta Business con el negocio verificado (documentos reales de la empresa).
  2. Un número de teléfono que no esté registrado en WhatsApp. Ni el personal, ni el que ya usa el equipo. Si lo está, hay que darlo de baja primero y esperar.
  3. Una app en Meta for Developers con el producto WhatsApp añadido.
  4. Plantillas de mensaje aprobadas para poder escribir tú primero.

Nada de eso se resuelve en una tarde. Cuento entre una y dos semanas solo de trámites, y lo pongo en el cronograma desde el principio: es la causa número uno de que un proyecto de estos "se retrase" cuando en realidad iba bien.

La ventana de 24 horas#

Este es el concepto que rompe más expectativas, y conviene explicarlo al cliente antes de firmar:

Puedes responder con texto libre solo dentro de las 24 horas siguientes al último mensaje que te envió la persona. Pasado ese plazo, solo puedes iniciar conversación con una plantilla aprobada por Meta.

Las consecuencias prácticas son grandes:

  • Un asistente que responde dudas: sin problema, siempre contesta dentro de la ventana.
  • Recordar una cita de mañana: necesita plantilla, y la plantilla se aprueba antes, con el texto fijo y variables acotadas.
  • "Que el bot escriba a los clientes que no compran hace un mes": plantilla, y además es marketing, con su categoría y su costo distinto.

Cuando alguien pide "un bot que le escriba a la gente", casi siempre está pidiendo plantillas sin saberlo. Aclararlo temprano evita una conversación incómoda a mitad del proyecto.

Lo que cambia en octubre de 2026: cada burbuja cuesta#

La ventana no desaparece: sigues pudiendo responder con texto libre dentro de esas 24 horas sin plantillas aprobadas. Lo que desaparece es que salga gratis.

Hasta octubre de 2026Desde octubre de 2026
Unidad de cobroLa conversaciónCada mensaje enviado
Texto libre en la ventanaIncluidoSe cobra por unidad
PrecioPor conversaciónFijo por mensaje, según el país del destinatario

Y aplica a todo lo que sale: lo escriba un humano, un bot o una IA de un tercero. No hay tarifa distinta por ser una respuesta automática.

Suena a nota de facturación y es una decisión de arquitectura, porque cambia qué es un buen diseño de conversación.

Lo que encarece de golpe#

Estos patrones eran gratis dentro de la ventana y a partir de octubre se pagan uno por uno:

  • Trocear una respuesta larga en varias burbujas "para que se lea mejor". Tres burbujas son ahora tres cobros.
  • Los acuses intermedios: "Ok, dame un momento…", "Déjame revisar…", "¡Listo!". Cada uno cuesta lo mismo que una respuesta útil.
  • Simular que el bot escribe mandando mensajes cortos seguidos.
  • Menús en varios pasos que preguntan una cosa por mensaje.

Un asistente que resuelve una consulta en cinco burbujas cuesta cinco veces lo que uno que la resuelve en una. Con volumen bajo da igual; con miles de conversaciones al mes, es la diferencia entre un gasto razonable y una factura que nadie previó.

Cómo diseño ahora#

Una respuesta, un mensaje. Bien estructurada, con saltos de línea y listas si hace falta, pero completa. Si el modelo tiende a trocear, el prompt del sistema se lo prohíbe explícitamente.

Cero relleno conversacional. Nada de "enseguida te ayudo" seguido de la ayuda: se manda la ayuda. La cortesía va dentro del mismo mensaje.

Preguntar una vez y bien. Si faltan tres datos para procesar un pedido, se piden los tres juntos, no uno por mensaje.

Botones y listas interactivas en lugar de encadenar preguntas: un solo mensaje puede ofrecer varias opciones y ahorra toda la ida y vuelta.

Es un caso raro y agradable: lo que abarata la factura es también mejor experiencia. Nadie quiere recibir seis notificaciones seguidas de un negocio.

El webhook: por qué no puedes llamar al modelo ahí#

Meta envía cada mensaje entrante a tu webhook y espera un 200 rápido. Si tardas, reintenta; si reintenta, procesas el mismo mensaje dos veces y el cliente recibe dos respuestas.

Y un LLM tarda. Entre recuperar contexto y generar, se van varios segundos.

Así que el webhook no responde: encola.

@Controller('webhooks/whatsapp')
export class WhatsappWebhookController {
  constructor(
    @InjectQueue('whatsapp') private readonly queue: Queue,
    private readonly signature: SignatureVerifier,
  ) {}

  @Post()
  @HttpCode(200)
  async receive(
    @Headers('x-hub-signature-256') signature: string,
    @Body() payload: WhatsappWebhookPayload,
    @Req() req: RawBodyRequest<Request>,
  ): Promise<void> {
    // 1. Verificar que viene de Meta, con el cuerpo CRUDO
    if (!this.signature.isValid(req.rawBody, signature)) {
      throw new UnauthorizedException();
    }

    // 2. Encolar y devolver 200 de inmediato
    for (const message of extractMessages(payload)) {
      await this.queue.add('incoming', message, {
        // El mismo mensaje reintentado por Meta no se procesa dos veces
        jobId: message.id,
      });
    }
  }
}

Dos detalles que cuestan un día si se pasan por alto:

La firma se valida contra el cuerpo crudo. Si tu framework ya parseó el JSON y lo vuelves a serializar, el hash no coincide: JSON.stringify no garantiza el mismo orden ni el mismo espaciado. En NestJS hay que habilitar rawBody explícitamente.

El jobId es el identificador del mensaje. Meta reintenta, y la cola descarta el duplicado sola. Sin eso, un pico de latencia se traduce en respuestas repetidas.

Cómo evito que el asistente invente#

Un asistente que se inventa un precio o una política delante de un cliente hace más daño que no tener asistente. La confianza se pierde en un mensaje.

Tres decisiones que aplico siempre:

1. Responde desde documentos, no desde su memoria. El modelo no "sabe" del negocio: recibe fragmentos recuperados de los documentos reales del cliente —catálogo, políticas, horarios— y responde solo con eso.

async function answer(question: string, businessId: string): Promise<Answer> {
  const context = await retriever.search(question, { businessId, limit: 5 });

  // Sin contexto no se improvisa: se deriva
  if (context.length === 0) {
    return { type: 'handoff', reason: 'sin_contexto' };
  }

  const reply = await llm.complete({
    system: SYSTEM_PROMPT,   // "responde SOLO con el contexto dado"
    context,
    question,
  });

  return reply.confident ? { type: 'answer', text: reply.text } : { type: 'handoff' };
}

2. "No sé" es una respuesta válida y deseable. El prompt del sistema dice explícitamente que si el contexto no contiene la respuesta, debe decirlo y ofrecer pasar con una persona. Cuesta trabajo aceptarlo —queda menos impresionante en la demo— y es lo que hace que el asistente sea usable en producción.

3. Nada de acciones irreversibles sin confirmación. Un agente puede crear un pedido o agendar una cita, pero el paso final se confirma con el usuario en el mismo chat. Un modelo que interpreta mal "cancela eso" no debería poder cancelar nada por su cuenta.

El escalado a una persona no es opcional#

Todo asistente necesita una salida hacia un humano, y esa salida necesita estado. Cuando una conversación se deriva, el bot deja de responder en ese hilo hasta que el agente la devuelve. Si no, el cliente acaba hablando con los dos a la vez.

SituaciónQué hace el bot
Sin contexto para responderDeriva y avisa
El usuario pide hablar con alguienDeriva de inmediato
Reclamo o cancelaciónDeriva sin intentar resolver
Conversación derivada activaSilencio total hasta que la devuelvan

Lo que costó más de lo esperado#

Los trámites, no el código. La verificación del negocio y la aprobación de plantillas ocuparon más calendario que construir el bot entero.

Los audios. La gente manda notas de voz a los negocios constantemente. Si no las contemplas, el bot queda mudo justo con los clientes que más escriben. Transcribir y tratarlas como texto no es difícil, pero hay que decidirlo antes.

El costo variable, y que se mueve. Se paga a Meta por el tráfico y al proveedor del modelo por tokens, y las reglas cambian: el paso a cobro por mensaje de octubre de 2026 encarece justo el patrón que muchos bots usan sin pensarlo. Conviene revisar la política de precios antes de cada propuesta y dejarlo dicho en el contrato — un supuesto de facturación que envejece mal es una discusión incómoda dentro de seis meses.

Cuándo no montaría uno#

Si el volumen es bajo, una persona responde mejor, más barato y con más criterio. La automatización empieza a compensar cuando hay repetición real: las mismas veinte preguntas todos los días, o mensajes fuera del horario que hoy se pierden.

Y si los datos del negocio están desordenados —catálogos desactualizados, políticas que nadie escribió—, el asistente va a responder mal porque la fuente está mal. Ordenar eso es el primer trabajo, y muchas veces resuelve la mitad del problema sin necesidad de bot.

¿Tienes un sistema con este tipo de problemas?

Cuéntame en qué estás y te digo cómo lo abordaría yo.

Hablemos de tu sistema