
Por qué construimos nuestro propio motor de avisos
El 1 de octubre Meta empieza a cobrar los mensajes de servicio. Cómo modelamos el costo y por qué terminamos escribiendo el canal nosotros.
Hay decisiones de arquitectura que se toman leyendo un changelog de precios.
El 1 de octubre de 2026 Meta empieza a cobrar los service messages de WhatsApp: cada respuesta libre del bot dentro de la ventana de 24 horas, que hasta hoy no cuesta nada. En Argentina la tarifa esperada es de USD 0,0260 por mensaje entregado, y —esto es lo importante— sin escalones por volumen. El precio del mensaje número un millón es el mismo que el del primero.
Este post es cómo modelamos ese cambio y qué construimos como respuesta. No es un post sobre irse de WhatsApp: nadie en eventos se fue de WhatsApp. Es sobre dejar de depender de un solo canal para algo que el producto hace miles de veces por día.
Lo que cuesta avisar
El envío de más volumen de Circli es el recordatorio de charla: sale al padrón del evento, por cada charla marcada, quince minutos antes. Es también el que más impacto tiene, porque es el que llena las salas.
Con la tarifa nueva, un evento chico —de los que hacemos seguido, unos 190 registrados con una decena de mensajes cada uno— cuesta alrededor de USD 49 en avisos. Un evento grande, de diez mil personas, cae entre USD 2.500 y 9.900 según cuánta gente active los avisos.
Para poner esos números en escala: los mismos avisos por SMS costarían cerca de USD 195; por email, menos de USD 2; por Web Push, cero.
Pero el número que decidió la arquitectura no es ninguno de esos. Es éste: el costo no es existencial, la falta de tope sí. Cuarenta y nueve dólares por un evento no cambian el negocio. Lo que sí lo cambia es no tener forma de saber, cuando un organizador marca doce charlas de un evento de diez mil personas, que acaba de firmar una factura de cuatro cifras — y enterarse a fin de mes.
Por qué escribirlo en vez de comprarlo
La respuesta obvia era contratar un proveedor de push. Hay varios, y varios son gratis.
Elegimos escribirlo, y la razón cabe en una frase: el costo por mensaje de Web Push es cero en cualquier proveedor; lo que cambia es quién guarda las suscripciones y quién puede cortarnos. Habíamos llegado hasta acá justamente por depender de una plataforma que un día cambió su precio. Reemplazar esa dependencia por otra, en la capa que existía para no tenerla, no arreglaba nada.
Web Push es un estándar abierto, y eso quiere decir que se puede implementar entero. Firmamos cada envío con nuestras propias claves (VAPID, RFC 8292) y ciframos el contenido del aviso de punta a punta (RFC 8291), de modo que el servicio del navegador entrega un blob que no puede leer. No hay intermediario que facture, y tampoco hay intermediario que sepa qué dice el aviso.
No es un canal paralelo: es una escalera
Acá está la parte que más nos costó y de la que más aprendimos.
Un aviso se anuncia una sola vez, por el mejor canal disponible. Si la persona activó los avisos del navegador, va por ahí. Si no tiene ningún dispositivo suscripto, baja a WhatsApp.
La tentación es escribir eso con un booleano: ¿le llegó el push? Si no, mandá WhatsApp. Y ahí está el bug, porque "no le llegó" tapa dos situaciones que no se parecen en nada:
- Ya se le avisó. El recordatorio salió hace un rato y el candado dice que esta persona, para esta charla, ya está anunciada.
- Se cayó el envío. El servicio del navegador tuvo un corte, o el dispositivo dejó de existir.
Con un booleano las dos se leen igual, y el resultado es que la misma charla se anuncia dos veces por dos canales — el error que ya nos había pasado antes con los avisos de mensajes directos.
Por eso el envío devuelve tres estados, no dos: salió, suprimido y sin suscripción. Y sólo "sin suscripción" baja a WhatsApp. La línea entre los dos últimos no es "¿me tocaba avisarle?" sino "¿pudo haber llegado algo?". Un corte de cinco minutos del servicio de push no puede costarle a nadie el recordatorio de una charla que empieza en quince minutos: eso cae por la escalera y sale igual.

Prender el canal, entonces, nunca deja a nadie sin aviso: sólo mueve de canal a quien lo pidió. Apagarlo tampoco: todos vuelven por donde venían.
El candado vive en la base, no en la memoria
Un detalle que parece de plomería y no lo es.
La primera versión guardaba en memoria a quién ya se le había avisado. Funcionaba perfecto en el laboratorio y no funcionaba nunca en producción: cada corrida del cron es un contenedor nuevo y frío, así que ese registro arrancaba vacío siempre. La deduplicación, sencillamente, no existía — y no fallaba nada, que es lo peor que puede hacer un bug así.
Hoy el candado es una fila en Postgres, con una restricción de unicidad por evento, tipo de aviso, clave de deduplicación y persona. Se reclama antes de enviar, no después. Si dos corridas se pisan, una sola gana la fila y la otra ve que ya está anunciado.
Dos compuertas, una por canal
El recordatorio ahora sale por dos caminos y sólo uno pasa por Meta. Eso obliga a que el corte de emergencia sea doble.
Con una sola compuerta, un problema de reputación del número de WhatsApp —que se apaga solo, y está bien que se apague solo— dejaría sin avisos también al canal que no toca Meta. Es decir: el modo de falla que Web Push vino a evitar sería el que lo apaga. Cada canal tiene su propia puerta, y se cierran por separado.
Lo que el navegador ya sabía hacer
Lo probamos en un iPhone de verdad, con iOS 26. La web agregada a la pantalla de inicio recibe avisos con la app cerrada y la pantalla bloqueada — agregada tanto desde Safari como desde Chrome, y sin ningún manifest. En Android no hace falta ni eso: funciona en una pestaña común.
Vale ser preciso con lo que esto es y lo que no. No hay una app de Circli, y no la estamos construyendo. No hay nada que bajar, no hay tienda de aplicaciones, no hay actualización pendiente. Lo único que hay es una dirección web y un permiso que la persona da o no da. El trabajador que corre en segundo plano no cachea nada ni intercepta una sola navegación: dibuja avisos y abre enlaces. Eso es todo lo que hace, a propósito, porque todo lo demás que podría hacer es superficie que se rompe.
Lo que todavía no está
El primer entregable que nos falta no es un canal nuevo: es un tope de gasto para el camino que el 1 de octubre deja de ser gratis, por evento y por persona, con el mismo reclamo-antes-de-enviar que ya usan las plantillas. Mientras eso no exista, el único límite real es la ventana de tiempo, y una ventana no es un presupuesto.
Es, otra vez, la misma idea: el problema nunca fue el precio. Fue no tener el número a la vista antes de gastarlo.
¿Organizás eventos y te importa que los avisos lleguen sin que nadie instale nada? Conocé Circli Events o escribinos desde eventos.circli.app.