Skip to content
leoco/dev
← Volver a proyectos
Backend (autor principal)·2026·Repo privado

Botinfy Gateway

Una App de Meta tiene una única URL de webhook. Si esa App tiene varios números de WhatsApp conectados —uno por cliente— Meta manda los eventos de todos al mismo endpoint, mezclados y sin forma de suscribir cada número a un destino distinto. El gateway se para en el medio: recibe todo, mira de qué número o cuenta viene cada evento y lo reenvía al CRM de ese cliente. En el camino de vuelta permite que cada CRM responda por su propio número, sin entregarle nunca el token global de la App. Escrito íntegramente con la librería estándar de Go: sin una sola dependencia externa.

Disponible bajo NDA · puedo hacer un walkthrough en una llamada.
STACK TÉCNICO
GoMeta Graph APIEmbedded SignupHMACDockerGitHub Actions
DESAFÍOS

Aislamiento entre clientes

Los identificadores de WhatsApp, Messenger e Instagram son espacios independientes en Meta: un id de página puede coincidir numéricamente con el phone_number_id de otro cliente sin tener relación. Con un mapa único, ese evento se entregaría al CRM equivocado. Solución: un mapa de identificadores por plataforma, y sesgo conservador — ante cualquier duda gana el destino por defecto, porque mandar al default es recuperable y mandar al CRM ajeno filtra datos.

La firma se rompe al reserializar

La firma de Meta es un HMAC-SHA256 sobre los bytes exactos del body. Reserializar el JSON cambia el orden de las claves o los espacios, y la firma deja de validar en el destino. Solución: el payload se reenvía byte a byte, sin transformarlo, con la firma original intacta.

Un 200 es una promesa

Meta no reintenta un evento que ya recibió 200: desde ese momento el evento existe únicamente dentro del gateway. Solución: todo camino termina en el destino o en la cola de fallos en disco, y el único caso en que se responde 500 es cuando no puede garantizarse ninguna de las dos cosas.

SOLUCIONES

Reenvío asíncrono con cola de fallos

La respuesta a Meta no espera al destino: el reenvío corre en su propia goroutine con contexto de vida independiente. Si falla, el evento cae en una cola persistente en disco (.jsonl) que se drena sola cada intervalo y también a pedido. El apagado por SIGTERM drena en orden —servidor HTTP, reenvíos en vuelo, cola— para que un redeploy no pierda eventos.

Credenciales por cliente, no un token global

El token de la App permite emitir desde cualquier número conectado: dárselo a un cliente es darle la capacidad de suplantar a otro. Por eso el token no sale del gateway. Cada cliente recibe una API key propia, y esa key determina el número; el cuerpo del pedido no puede cambiarlo. Quien pide emitir desde un número ajeno recibe 403 y no se emite nada.

Splitting y re-firma de payloads mixtos

Meta puede meter en un solo POST entradas de clientes distintos. Antes eso mandaba el payload completo al destino por defecto, que recibía conversaciones ajenas mientras los otros CRM no recibían nada. Ahora se parte: cada destino recibe un cuerpo con solo sus entradas y el X-Hub-Signature-256 se recalcula sobre ese cuerpo. Un contador en /stats registra cuántas veces ocurrió.

Operación sin reinicios

La tabla de clientes se recarga en caliente bajo un candado compartido con los cambios de la API de administración, así que dar de alta un cliente no requiere redesplegar. Se suma métricas en memoria con endpoint /stats, health check, middleware de request-ID y checks de despliegue en GitHub Actions.

Alta de clientes en autoservicio

Conectar un WhatsApp nuevo ya no pasa por el servidor: el gateway emite un link firmado, de un solo uso y con vencimiento, que abre Embedded Signup de Meta. Al cerrar el popup canjea el código —que vive 30 segundos—, suscribe la App a la WABA del cliente y lo escribe en la tabla. Soporta números en coexistencia con la app de WhatsApp Business, guarda un token por WABA en vez de usar el global y, si el cliente tiene un portafolio de Meta fijado, rechaza con 409 una WABA que pertenezca a otro.

Tres canales de salida, cada uno con su permiso

Además de WhatsApp, cada CRM emite por Messenger e Instagram con el token de su propia página o cuenta, y administra sus plantillas de WhatsApp (listar, crear, borrar) con su API key. La comprobación de qué puede emitir cada cliente se hace por canal: como los espacios de ids de Meta son independientes, con una sola lista un cliente podría emitir desde la página de otro nombrándola como si fuera su número.

¿Te interesa este proyecto o tienes una idea similar?