Noti para empresas
De cero a tu primer aviso en 5 minutos.
Esta guía es para vos o para quien programe tu sistema. Los ejemplos se copian y pegan: están probados contra la API de verdad. Elegí Curl, Node o Python arriba de cada ejemplo.
¿Vas a integrarlo con tu agente de IA? Usá la guía «Dáselo a tu agente»: un prompt listo para pegar en Claude Code, Cursor o Codex, más un cliente de un solo archivo en Python y en Node. Esta página es la referencia para humanos.
Qué es Noti en 30 segundos
- La gente se suscribe a tu empresa escaneando tu QR o escribiendo tu código en la app Noti.
- Vos les mandás avisos: aparecen en la pantalla bloqueada del teléfono.
- Se pueden dar de baja cuando quieran, y vos nunca te enterás. Tampoco sabés cuántos te siguen hoy. No es un descuido: es lo que hace que decir que sí sea fácil.
- La app sólo recibe. Nadie te responde por Noti.
- Los remitentes son empresas verificadas: nadie recibe avisos de un desconocido.
Antes de empezar: una dirección y un token
Todos los ejemplos usan dos variables. Cambiás la dirección de Noti en un solo lugar:
export NOTI=https://noti-api-production.up.railway.app # la dirección de Noti
export NOTI_TOKEN=ntf_TU_TOKEN # tu token (lo conseguís en el paso 4)
Cuando Noti tenga su dominio propio, sólo cambia la primera línea. En Node (18 o más nuevo) se lee con process.env.NOTI y en Python con os.environ["NOTI"]: los ejemplos ya lo hacen.
Paso 1 · Creá tu empresa
- Entrá a
/panely iniciá sesión con tu mail. - Tocá «Creá tu empresa», poné el nombre y listo.
Tu empresa queda en revisión hasta que la aprobemos. Verificamos a cada empresa a mano porque es la promesa de Noti: quien te llega a la pantalla bloqueada existe y la gente lo reconoce, nunca es un desconocido.
Mientras esté en revisión no podés mandar avisos y tu QR todavía no funciona: si alguien lo escanea, la app no encuentra tu empresa. Cuando la aprobemos todo se enciende solo.
Paso 2 · Tu QR y tu código
Tu empresa tiene un código corto (por ejemplo mercadoverde) y un QR que lleva ese código. Hacen lo mismo:
- El QR: la persona abre Noti, toca + y le apunta la cámara.
- El código: la persona abre Noti, toca + y lo escribe.
Dónde está tu QR. En el panel, pestaña «Mis QR»: descargarlo, imprimirlo, copiar el link. Cada QR tiene además una página pública lista para imprimir o mandar por mail: https://noti-api-production.up.railway.app/e/TU_CODIGO (muestra el QR grande y el código). Ponelo en el mostrador, en la vidriera, en el ticket o en tu Instagram.
Un QR por sucursal o por tema
Podés tener todos los QR que quieras (sucursales, clientes VIP, un evento). Cada uno tiene su código y sirve para saber por cuál QR se sumó la gente y para avisarle sólo a los de ese QR (paso 3).
curl -X POST "$NOTI/company/groups" \
-H "Authorization: Bearer $NOTI_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "Sucursal Palermo", "code": "palermo"}'
const r = await fetch(`${process.env.NOTI}/company/groups`, {
method: "POST",
headers: { Authorization: `Bearer ${process.env.NOTI_TOKEN}`, "Content-Type": "application/json" },
body: JSON.stringify({ name: "Sucursal Palermo", code: "palermo" }),
});
console.log(r.status, await r.json());
import os, requests # pip install requests
r = requests.post(
f"{os.environ['NOTI']}/company/groups",
headers={"Authorization": f"Bearer {os.environ['NOTI_TOKEN']}"},
json={"name": "Sucursal Palermo", "code": "palermo"},
)
print(r.status_code, r.json())
Respuesta (201):
{
"id": "2e335cd4-2212-4b31-bf0b-6b223d6c72bc",
"code": "palermo",
"name": "Sucursal Palermo",
"signups": 0,
"archived": false,
"qr_text": "noti://s/palermo",
"page_url": "https://noti-api-production.up.railway.app/e/palermo"
}
name: hasta 80 caracteres, obligatorio.code: opcional. Sin él, Noti inventa uno corto (6k7rps). Con él: de 3 a 24 caracteres, minúsculas, números y guiones. Si ya está en uso,409; si tiene algo que no va,400.signups: cuántas personas se sumaron alguna vez por este QR. Sólo sube, nunca baja.page_url: la página para imprimir.qr_textes lo que lleva el QR por dentro.
Para ver tus QR: GET /company/groups (con ?archived=true incluye los archivados). Para renombrar o archivar: PATCH /company/groups/{id} con {"name": "…"} o {"archived": true}. Archivar apaga ese QR, pero no saca a nadie: los que ya se habían suscripto siguen recibiendo tus avisos.
Paso 3 · Mandá un aviso
Una sola llamada, POST /send, y le llega a todos tus suscriptos sin que sepas quiénes son. Hace falta tu token (paso 4).
curl -X POST "$NOTI/send" \
-H "Authorization: Bearer $NOTI_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"title": "Llegó la verdura",
"body": "Hoy hay 2x1 en hojas verdes hasta las 20.",
"emoji": "🥬",
"cta": { "label": "Ver el local", "url": "https://mercadoverde.com" }
}'
const r = await fetch(`${process.env.NOTI}/send`, {
method: "POST",
headers: { Authorization: `Bearer ${process.env.NOTI_TOKEN}`, "Content-Type": "application/json" },
body: JSON.stringify({
title: "Llegó la verdura",
body: "Hoy hay 2x1 en hojas verdes hasta las 20.",
emoji: "🥬",
cta: { label: "Ver el local", url: "https://mercadoverde.com" },
}),
});
console.log(r.status, await r.json());
import os, requests # pip install requests
r = requests.post(
f"{os.environ['NOTI']}/send",
headers={"Authorization": f"Bearer {os.environ['NOTI_TOKEN']}"},
json={
"title": "Llegó la verdura",
"body": "Hoy hay 2x1 en hojas verdes hasta las 20.",
"emoji": "🥬",
"cta": {"label": "Ver el local", "url": "https://mercadoverde.com"},
},
)
print(r.status_code, r.json())
Respuesta, siempre la misma:
202 Accepted
{ "id": "f6e51d18-66be-485b-859a-db756f4185db" }
A quién le llega
Además de title y body, ponés como mucho uno de estos destinos (sin ninguno, es a todos):
| Destino | Qué es | Le llega a |
|---|---|---|
| ninguno | difusión | todos los suscriptos a tu empresa |
"group": "palermo" | el código de uno de tus QR | sólo a los que se sumaron por ese QR |
"ref": "cliente-1042" | el id con que vos conocés a la persona | sólo a esa persona (paso 5) |
Poner dos a la vez da 400.
# Sólo a los de la sucursal Palermo
curl -X POST "$NOTI/send" \
-H "Authorization: Bearer $NOTI_TOKEN" \
-H "Content-Type: application/json" \
-d '{"title": "Palermo abre hasta las 22", "body": "Hoy cerramos más tarde.", "group": "palermo"}'
await fetch(`${process.env.NOTI}/send`, {
method: "POST",
headers: { Authorization: `Bearer ${process.env.NOTI_TOKEN}`, "Content-Type": "application/json" },
body: JSON.stringify({ title: "Palermo abre hasta las 22", body: "Hoy cerramos más tarde.", group: "palermo" }),
});
import os, requests # pip install requests
requests.post(
f"{os.environ['NOTI']}/send",
headers={"Authorization": f"Bearer {os.environ['NOTI_TOKEN']}"},
json={"title": "Palermo abre hasta las 22", "body": "Hoy cerramos más tarde.", "group": "palermo"},
)
POST /company/send hace exactamente lo mismo que POST /send: el panel usa ese, y tu sistema puede usar el que prefiera, con el mismo token y el mismo cuerpo.
Todos los campos
| Campo | ¿Obligatorio? | Qué es y límite |
|---|---|---|
title | sí | Lo que se lee en la pantalla bloqueada. Hasta 100 caracteres. |
body | sí | El texto del aviso. Hasta 1000 caracteres; el push muestra los primeros 140 y el resto se lee al abrir la app. |
emoji | no | Un solo emoji, al lado del título (🥬 sí; 🥬🍅 no; una letra no). |
cta | no | Un botón con un link. label y url van juntos. |
cta.label | con cta | El texto del botón. Hasta 40 caracteres. |
cta.url | con cta | A dónde lleva. Hasta 2048 caracteres. Empieza con http://, https://, mailto:, tel:, sms: o whatsapp:. |
group | no | Código de uno de tus QR. Hasta 24 caracteres. |
ref | no | Tu id de la persona. Hasta 128 caracteres. |
Existe también un campo to (la dirección de una persona de Noti, un @handle): es para personas que se mandan avisos entre sí. Con una empresa no lo necesitás: usá group o ref. Se puede poner uno solo de to, group y ref.
Un botón de WhatsApp
Para que te escriban por WhatsApp, usá un link https://wa.me/ con el número con código de país y sólo dígitos (sin +, espacios ni ceros; en Argentina, 549 + código de área sin 0 + número) y, si querés, el mensaje ya armado en ?text= (codificado como URL). Es un https, así que abre WhatsApp en cualquier teléfono y pasa la validación del botón:
"cta": {
"label": "Escribinos por WhatsApp",
"url": "https://wa.me/5491155550123?text=Hola%2C%20quiero%20hacer%20un%20pedido"
}
Con el MCP son boton_label y boton_url (https://wa.me/549…?text=…). El constructor de botones del panel arma este link por vos.
El botón no viaja en el push: se ve al abrir el aviso en la app, y debajo del botón la app muestra a dónde lleva de verdad (leído del link, no del texto que escribiste). No se puede mandar una imagen ni un archivo: un aviso es título, texto, un emoji y, si querés, un botón.
Qué quiere decir ese 202 (y qué no)
El 202 no quiere decir «entregado». Es un «recibí tu pedido» y nada más. La respuesta es idéntica (mismo código, mismo formato, y tarda lo mismo) si le llegó a mil personas, a una o a ninguna; si nadie se suscribió todavía; si alguien silenció tus avisos (le quedan guardados, sin sonar) o te cortó (no le llega nada); si te pasaste de un límite; o si el ref o el group no existen.
Es a propósito y no se va a cambiar: si la respuesta contara quiénes recibieron, podrías deducir quién te cortó, y darse de baja dejaría de ser gratis para la gente. Esa promesa es lo que hace fácil escanear tu QR. Si necesitás saber cuánta gente te sigue o a quién le llegó, Noti no es la herramienta; lo que sí podés ver es si la gente abrió tus avisos.
Cómo probar que funciona: instalá Noti en tu teléfono, escaneá tu propio QR y mandate un aviso. Si suena en tu pantalla bloqueada, está todo bien.
Límites
| Límite | Valor |
|---|---|
| Llamadas de envío por minuto, por token | 60 |
Difusiones por hora, por empresa (las de «todos» o «un QR»; los avisos a una persona con ref no cuentan) | 20 |
| Personas por difusión | hasta 5.000 |
| Avisos a la misma persona | uno cada 10 segundos; si mandás otro antes, ese se descarta para ella |
Pasarte de un límite no da error: contesta el mismo 202 de siempre, pero el aviso no sale.
Errores posibles
| Código | Cuándo | Cuerpo |
|---|---|---|
202 | el pedido está bien armado | {"id": "…"} |
400 | un campo no cumple las reglas: título vacío, emoji doble, botón a medias, link javascript:, dos destinos a la vez… | {"detail": [ … qué campo y por qué ]} |
401 | falta el token, está mal escrito o fue revocado | {"detail": "No autenticado"} |
Un 400 por un emoji doble:
{"detail":[{"type":"value_error","loc":["body","emoji"],"msg":"Value error, el emoji tiene que ser exactamente uno","input":"ab","ctx":{"error":{}}}]}
No hay 429 en el envío: los límites se contestan con el 202 de siempre.
Paso 4 · El token de API
El token es la llave con la que tu sistema manda avisos. Va en el encabezado Authorization: Bearer ntf_….
- En el panel (
/panel), pestaña «Conectar», tocá Crear. - Copialo ahí mismo: se muestra una sola vez. Si lo perdés, creás otro y revocás el viejo.
- Guardalo como variable de entorno o en el gestor de secretos de tu sistema. Nunca en el código ni en una página pública.
export NOTI_TOKEN=ntf_TU_TOKEN
curl "$NOTI/company/me" -H "Authorization: Bearer $NOTI_TOKEN" # ¿anda?
Para revocar uno (si se filtró o ya no se usa), tocá Revocar en la misma pestaña: corta en el acto. Podés tener hasta 10 tokens activos (los revocados no cuentan).
Crear y revocar tokens (POST /company/tokens, DELETE /company/tokens/{id}) lo hace una persona de tu equipo desde el panel. Un token no puede crear otros tokens: si se filtra uno, se corta el daño con revocarlo.
Paso 5 · Avisarle a UNA persona (integrarlo en tu sistema)
Para avisarle a un cliente concreto («tu pedido salió», «tu turno es mañana»), Noti necesita saber quién es en tu sistema sin saber quién es en Noti. Son dos pasos.
1 · Vincular. Le pedís a Noti un pase para esa persona con el id con el que vos la conocés (ref). Es una página con un QR y un botón «Abrir en Noti»: se la mostrás en tu web, tu app o tu mail.
curl -X POST "$NOTI/company/connect-links" \
-H "Authorization: Bearer $NOTI_TOKEN" \
-H "Content-Type: application/json" \
-d '{"ref": "cliente-1042"}'
const r = await fetch(`${process.env.NOTI}/company/connect-links`, {
method: "POST",
headers: { Authorization: `Bearer ${process.env.NOTI_TOKEN}`, "Content-Type": "application/json" },
body: JSON.stringify({ ref: "cliente-1042" }),
});
console.log(r.status, await r.json());
import os, requests # pip install requests
r = requests.post(
f"{os.environ['NOTI']}/company/connect-links",
headers={"Authorization": f"Bearer {os.environ['NOTI_TOKEN']}"},
json={"ref": "cliente-1042"},
)
print(r.status_code, r.json())
{
"url": "https://noti-api-production.up.railway.app/c/AmNY4Yiy-euO3DwAzprqHeXv",
"qr_text": "noti://c/AmNY4Yiy-euO3DwAzprqHeXv",
"expires_at": "2026-10-31T20:08:27.725748Z"
}
ref: obligatorio, hasta 128 caracteres. Es tu id (usuario, nº de cliente, lo que uses).group: opcional. El código de uno de tus QR: además de vincularla, la suma a ese grupo.url: la página/c/<pase>. En un teléfono tiene el botón que abre Noti; en una computadora muestra el QR para escanear con la cámara de Noti.qr_text: lo que lleva el QR por dentro (noti://c/<pase>), si preferís dibujarlo vos.- El pase sirve una sola vez y vence a los 7 días. Uno vencido o usado se ve igual que uno que no existe: «Este enlace ya no sirve». Generá otro.
2 · Avisar. Cuando la persona abrió el pase en su Noti, le mandás con el mismo ref:
curl -X POST "$NOTI/send" \
-H "Authorization: Bearer $NOTI_TOKEN" \
-H "Content-Type: application/json" \
-d '{"ref": "cliente-1042", "title": "Tu pedido salió", "body": "Llega hoy antes de las 18.", "emoji": "📦"}'
await fetch(`${process.env.NOTI}/send`, {
method: "POST",
headers: { Authorization: `Bearer ${process.env.NOTI_TOKEN}`, "Content-Type": "application/json" },
body: JSON.stringify({ ref: "cliente-1042", title: "Tu pedido salió", body: "Llega hoy antes de las 18.", emoji: "📦" }),
});
import os, requests # pip install requests
requests.post(
f"{os.environ['NOTI']}/send",
headers={"Authorization": f"Bearer {os.environ['NOTI_TOKEN']}"},
json={"ref": "cliente-1042", "title": "Tu pedido salió", "body": "Llega hoy antes de las 18.", "emoji": "📦"},
)
Si esa persona todavía no abrió el pase, o se dio de baja, el 202 es el mismo y el aviso no sale: Noti no te lo cuenta. Si una persona cambia de teléfono, generale un pase nuevo con el mismo ref: pasa al dispositivo nuevo.
O invitala por mail (ideal para tu equipo)
Si tenés su mail, no hace falta mostrarle nada: Noti le manda un mail. La persona instala Noti, entra con ese mail (le llega un código, sin contraseña) y acepta la invitación en la app. Desde ahí le mandás con ref, que por omisión es su mail.
curl -X POST "$NOTI/company/invitations" \
-H "Authorization: Bearer $NOTI_TOKEN" \
-H "Content-Type: application/json" \
-d '{"email": "ana@tuempresa.com"}'
email: obligatorio.ref: opcional (si falta, el mail en minúsculas).group: opcional, el código de un QR al que además sumarla.- La respuesta es siempre la misma (202). Noti no te dice si el mail llegó, si la persona ya usaba Noti, si pidió no recibir invitaciones ni si aceptó.
- La invitación vive 30 días. Invitar de nuevo a la misma persona la renueva; el mail le llega como mucho una vez por día. Hasta 200 invitaciones por día por empresa.
- Desde Claude: la tool
invitar_por_mail(mail, ref?, qr?). Desde el panel: pestaña «Vincular».
Un ejemplo corto de integración
const NOTI = process.env.NOTI;
const headers = {
Authorization: `Bearer ${process.env.NOTI_TOKEN}`,
"Content-Type": "application/json",
};
// 1) Cuando un cliente quiere recibir avisos: mostrale este link (o su QR).
async function pasePara(clienteId) {
const r = await fetch(`${NOTI}/company/connect-links`, {
method: "POST",
headers,
body: JSON.stringify({ ref: String(clienteId) }),
});
if (!r.ok) throw new Error(`Noti contestó ${r.status}`);
return r.json(); // { url, qr_text, expires_at }
}
// 2) Cuando pasa algo en tu sistema: avisale.
async function avisarA(clienteId, title, body) {
const r = await fetch(`${NOTI}/send`, {
method: "POST",
headers,
body: JSON.stringify({ ref: String(clienteId), title, body }),
});
if (!r.ok) throw new Error(`Noti contestó ${r.status}`); // sólo 400/401: el 202 no dice más
}
const pase = await pasePara(1042);
console.log("Mostrale al cliente:", pase.url);
await avisarA(1042, "Tu pedido salió", "Llega hoy antes de las 18.");
import os
import requests # pip install requests
NOTI = os.environ["NOTI"]
HEADERS = {"Authorization": f"Bearer {os.environ['NOTI_TOKEN']}"}
def pase_para(cliente_id):
"""Mostrale al cliente `url` (o el QR de `qr_text`). Sirve una vez, 7 días."""
r = requests.post(f"{NOTI}/company/connect-links", headers=HEADERS, json={"ref": str(cliente_id)})
r.raise_for_status()
return r.json() # {"url": ..., "qr_text": ..., "expires_at": ...}
def avisar_a(cliente_id, title, body):
r = requests.post(f"{NOTI}/send", headers=HEADERS, json={"ref": str(cliente_id), "title": title, "body": body})
r.raise_for_status() # sólo 400/401: el 202 no dice si llegó
pase = pase_para(1042)
print("Mostrale al cliente:", pase["url"])
avisar_a(1042, "Tu pedido salió", "Llega hoy antes de las 18.")
avisar() { # avisar "Título" "Texto"
curl -sS -X POST "$NOTI/send" \
-H "Authorization: Bearer $NOTI_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"title\": \"$1\", \"body\": \"$2\"}"
}
avisar "Backup terminado" "Todo en orden"
En «Curl» está la versión para un script (cron, deploy, lo que sea): manda a todos tus suscriptos; agregale "ref": "…" o "group": "…" al JSON para acotarlo, y evitá comillas dobles dentro del título o el texto.
Recetas cortas
Las tres cosas que casi toda empresa quiere. Los clientes de un solo archivo
(noti.py, noti.ts) ya traen reintentos y un modo de pruebas; el prompt para tu agente está en
/empresas/integrar.
Aviso de pedido a UNA persona (por ref)
Primero la persona vincula su Noti con el botón «Conectar mi Noti» (un pase de un solo uso, paso 5). Después, cada vez que cambia el estado del pedido:
from noti import Noti
noti = Noti.from_env() # NOTI_ENABLED / NOTI_URL / NOTI_TOKEN; apagado = no hace nada
pase = noti.vincular_persona("usuario-42") # mostrale pase.url (link con QR)
envio = noti.send(ref="usuario-42", title="📦 Pedido #7", body="Salió y llega hoy.",
emoji="📦", cta_label="Ver el pedido", cta_url="https://tu-sistema.com/pedidos/7",
key="pedido-7-salio")
# envio.accepted = «Noti lo aceptó», NO «le llegó»
import { Noti } from "./noti.ts";
const noti = Noti.fromEnv();
const pase = await noti.vincularPersona("usuario-42"); // mostrale pase.url
await noti.send({ ref: "usuario-42", title: "Pedido #7", body: "Salió y llega hoy.", emoji: "📦",
ctaLabel: "Ver el pedido", ctaUrl: "https://tu-sistema.com/pedidos/7", key: "pedido-7-salio" });
curl -X POST "$NOTI/send" -H "Authorization: Bearer $NOTI_TOKEN" -H "Content-Type: application/json" \
-d '{"ref": "usuario-42", "title": "Pedido #7", "body": "Salió y llega hoy.", "emoji": "📦",
"cta": {"label": "Ver el pedido", "url": "https://tu-sistema.com/pedidos/7"}}'
Tips: mandá cuando el pedido cambia de estado, no por cada cambio; no pongas datos personales en el texto (se lee en la pantalla bloqueada); y guardá una marca «ya avisé de este pedido» en tu base para no repetir si el evento se reintenta.
Difusión a todos
noti.send(title="Hoy abrimos hasta las 22", body="Pasá por el local.", emoji="🕙")
await noti.send({ title: "Hoy abrimos hasta las 22", body: "Pasá por el local.", emoji: "🕙" });
curl -X POST "$NOTI/send" -H "Authorization: Bearer $NOTI_TOKEN" -H "Content-Type: application/json" \
-d '{"title": "Hoy abrimos hasta las 22", "body": "Pasá por el local.", "emoji": "🕙"}'
Tope: 20 difusiones por hora. Un envío a todos no se deshace: confirmá el texto antes.
Un QR por sucursal
# 1) crear el QR de la sucursal
curl -X POST "$NOTI/company/groups" -H "Authorization: Bearer $NOTI_TOKEN" -H "Content-Type: application/json" \
-d '{"name": "Sucursal Palermo", "code": "palermo"}'
# 2) imprimir la página $NOTI/e/palermo en el local
# 3) avisar sólo a los que se sumaron por ahí
curl -X POST "$NOTI/send" -H "Authorization: Bearer $NOTI_TOKEN" -H "Content-Type: application/json" \
-d '{"group": "palermo", "title": "Palermo abre hasta las 22", "body": "Hoy cerramos más tarde."}'
noti.send(group="palermo", title="Palermo abre hasta las 22", body="Hoy cerramos más tarde.")
await noti.send({ group: "palermo", title: "Palermo abre hasta las 22", body: "Hoy cerramos más tarde." });
Saber si un aviso se vio
Cada POST /send te devuelve un id: es el del aviso. Con GET /company/avisos (la lista) y GET /company/avisos/{id} (uno) preguntás qué hizo la gente, sin que Noti te diga nunca quién silenció o cortó. En el panel es la pestaña «Tus avisos»; en el MCP, listar_avisos y ver_aviso.
{
"id": "6f1c…", "kind": "ref", "title": "Tu pedido salió", "ref": "cliente-42",
"has_cta": true,
"seen_on": "2026-10-26", // a UNA persona: el día en que lo abrió; null = sin señal
"seen_count": null, // a muchos: cuántos lo abrieron, sólo desde el mínimo
"click_count": null, // a muchos: cuántos tocaron el botón, mismo mínimo
"min_audience": 30
}
- Sólo señales positivas. A una persona (
ref):seen_on, el día (sin hora: con la hora se vería «al instante» contra «días después» y se podría adivinar quién silenció). A muchos:seen_countyclick_count, sólo desde 30 personas (el mismo mínimo de la salud de tu audiencia); por debajo,null. Sólo suben. - Nunca hay «entregado», «falló» ni un denominador («40 de 100»): cualquier cosa derivada de si el push llegó o de a cuántas personas se mandó cambiaría cuando alguien silencia o corta, y podrías deducir quién fue.
nullno es «no lo vio». Es lo mismo si no lo abrió, lo silenció, te cortó, nunca se vinculó o el envío se descartó por un límite. No hay forma de distinguirlos, a propósito.- Cortar no cambia nada de lo que ves, salvo que el número deja de crecer (igual que si nadie más lo abre). Que el aviso expire en la app o que alguien borre su cuenta tampoco lo baja: el contador es tuyo.
- Una persona suma una vez cuando abre el aviso (no cuando llega el push) y otra si toca el botón. La app sigue sin mandarle nada a nadie.
Salud de tu audiencia
GET /company/stats (con tu token, o en la pestaña «Tu audiencia» del panel) te dice cómo viene tu audiencia sin decirte nunca quién se fue. Siempre trae lo que ya sabías: signups_total (cuántas personas se sumaron en total; sólo sube) y avisos_enviados_30d (los envíos que hiciste vos). Además, por empresa y por QR, un bloque salud:
{
"signups_total": 240,
"avisos_enviados_30d": 18,
"salud": {
"disponible": true,
"calidad": "buena",
"rechazo_banda": "5–10%",
"alcance_banda": "70–80%",
"tendencia": "estable",
"foto_del": "2026-10-25",
"consejos": ["Vas bien. …"]
}
}
- Sólo bandas, nunca números.
calidadesexcelente(menos de 5 % de la audiencia silenció o cortó),buena(5–10 %),atencion(10–20 %) oriesgo(20 % o más).alcance_bandaes, aproximadamente, a cuánta de tu audiencia le suenan tus avisos.tendencia(mejora,estable,empeora, osin_datoshasta tener unas semanas de historia) compara con el mes anterior. - Con pocos suscriptos no hay nada. Hasta que tu audiencia llega a 30 personas,
saludes{"disponible": false, "motivo": …}: con tan pocos, cualquier número delataría a una persona. Cada QR necesita llegar a ese mínimo por su cuenta. - Es una foto por día. Se saca la primera vez que preguntás cada día y después se contesta siempre la misma: volver a preguntar, mandar un aviso o que alguien te corte no la cambia hasta mañana. A propósito: así no se puede comparar un «antes» y un «después» de tu aviso para adivinar quién se fue.
- Nunca hay desglose por persona ni cuántos silenciaron, bloquearon o siguen activos, ni cuándo. En el MCP es la tool
ver_audiencia(sólo lectura).
Con Claude
Si usás Claude, podés mandar avisos hablando: conectás el MCP de Noti con tu token y Claude hace las llamadas por vos. En Claude Code se instala con un comando:
claude mcp add --transport http noti https://noti-api-production.up.railway.app/mcp \
--header "Authorization: Bearer ntf_TU_TOKEN"
El token (ntf_…) lo sacás en el panel, en Tokens; se muestra una sola vez.
Claude Desktop (necesita Node instalado): abrí el archivo de configuración (~/Library/Application Support/Claude/claude_desktop_config.json en Mac, %APPDATA%\Claude\claude_desktop_config.json en Windows), agregá esto y reiniciá Claude:
{
"mcpServers": {
"noti": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://noti-api-production.up.railway.app/mcp",
"--header", "Authorization:${NOTI_AUTH}"],
"env": { "NOTI_AUTH": "Bearer ntf_TU_TOKEN" }
}
}
}
Cursor, Windsurf, VS Code y otros clientes MCP: agregá un servidor remoto («Streamable HTTP») con la URL https://noti-api-production.up.railway.app/mcp y el header Authorization: Bearer ntf_TU_TOKEN. claude.ai en el navegador y la app del celular: todavía no, piden otro tipo de login; por ahora usá Claude Code o Claude Desktop. Tres pedidos que funcionan:
- «Avisale a todos que hoy abrimos hasta las 22.»
- «Creame un QR para la sucursal de Belgrano y decime la página para imprimirlo.»
- «Mandale a Juan (cliente 1042) que su pedido salió.» Funciona si Juan ya vinculó su Noti; si no, pedile a Claude un pase para él y mandáselo.
Claude no te va a decir «entregado»: por lo mismo que arriba, no hay forma de saberlo.
Referencia de endpoints
Todos con Authorization: Bearer ntf_… (el token de tu empresa), salvo los de panel, que son para una persona de tu equipo con sesión iniciada. La definición completa, en formato OpenAPI: /empresas/openapi.json.
| Método y ruta | Auth | Qué hace |
|---|---|---|
POST /send | token | Manda un aviso (a todos, a un QR con group o a una persona con ref). Siempre 202 {"id"}. |
POST /company/send | token o panel | Igual que /send. |
GET /company/me | token o panel | Tu empresa: nombre, código, texto del QR y página. |
GET /company/groups | token o panel | Tus QR (?archived=true incluye los archivados). |
GET /company/avisos | token o panel | Tus avisos y si se vieron: sólo señales positivas (seen_on, seen_count, click_count). |
GET /company/avisos/{id} | token o panel | Un aviso (el id del 202) y si se vio. |
GET /company/stats | token o panel | Salud de tu audiencia: altas en total, avisos enviados y calidad en bandas. |
GET /company/logo | token o panel | El logo de tus avisos: {logo_url, pending_review}. |
PUT /company/logo | token o panel | Sube tu logo (el cuerpo es la imagen PNG o JPEG, hasta 1 MB). Una persona de Noti lo revisa antes de usarlo. |
DELETE /company/logo | token o panel | Saca tu logo. |
POST /company/groups | token o panel | Crea un QR: {name, code?}. |
PATCH /company/groups/{id} | token o panel | Renombra o archiva un QR: {name?, archived?}. |
POST /company/connect-links | token o panel | Pase para vincular a UNA persona: {ref, group?} → {url, qr_text, expires_at}. |
POST /company/invitations | token o panel | Invitar por mail a UNA persona: {email, ref?, group?} → 202 {ref, message} (siempre igual). |
GET /company/tokens | panel | Lista tus tokens (nunca el valor). |
POST /company/tokens | panel | Crea un token; se ve en claro una sola vez. |
DELETE /company/tokens/{id} | panel | Revoca un token. |
GET /company/companies | panel | Las empresas a las que entra tu mail. |
GET /e/{código} | pública | Página con el QR de una empresa o de un QR, para imprimir. |
GET /c/{pase} | pública | Página de vinculación de una persona. |
GET /empresas/openapi.json | pública | Esta API en formato OpenAPI, para generar clientes. |
Preguntas frecuentes
¿Puedo ver cuánta gente tengo?
Vas a ver cuántas personas se sumaron en total (signups_total, y signups en cada QR; sólo sube, nunca baja) y, cuando tu audiencia llega a 30 personas, una calidad aproximada en bandas (Salud de tu audiencia). Nunca vas a ver quiénes se fueron, ni cuántos silenciaron, bloquearon o siguen activos, ni cuándo: por eso no hay números exactos y la foto cambia una vez por día. Si querés saber por dónde entra la gente, usá un QR por grupo.
¿Por qué no sé quién se dio de baja?
Porque si lo supieras, darse de baja saldría caro («¿se va a enojar?») y la gente dudaría antes de suscribirse. Con baja silenciosa, escanear tu QR no cuesta nada. La persona corta o silencia desde su app y vos no recibís ningún aviso, ningún número cambia y la respuesta del envío no se vuelve distinta.
¿Cuántos avisos puedo mandar?
Hasta 60 llamadas por minuto por token y 20 difusiones por hora (las de «todos» o «un QR»; los avisos a una persona con ref no cuentan para ese tope). A la misma persona le llega uno cada 10 segundos como mucho, y una difusión llega a un máximo de 5.000 personas. Detalle en el paso 3.
¿Se puede mandar una imagen?
No. Un aviso es un título, un texto, un emoji y, opcionalmente, un botón con un link. Si querés mostrar una imagen, ponela en una página y mandá el link en el botón.
¿Qué pasa si alguien no instaló la app?
No puede suscribirse: la suscripción se hace desde la app, escaneando tu QR o escribiendo tu código. La página /e/<tu código> le explica que tiene que abrir Noti. Y si alguien se suscribió y después desinstaló, no te enterás: tu envío da el mismo 202.
¿Cuánto duran los avisos en la app?
La bandeja se vacía sola: un aviso se borra 24 horas después de que la persona lo leyó, o a los 7 días si nunca lo abrió.
¿Puedo mandar por número de teléfono o por mail?
No: Noti no conoce teléfonos ni mails de la gente. Se llega a una persona por tu ref, después de que ella abrió el pase que le diste (paso 5).
¿Pueden contestarme?
No, la app sólo recibe. Si querés que contesten, poné en el botón un link a tu WhatsApp (whatsapp:), un mail (mailto:) o una web.
Perdí mi token, o se filtró
Revocalo en el panel (corta en el acto) y creá otro.
Mandé y no llegó
Con el 202 no hay forma de saberlo (es a propósito) y que un aviso no muestre señal tampoco prueba nada, pero revisá en este orden: (1) tu empresa ya está aprobada; (2) alguien se suscribió, empezando por vos con tu teléfono; (3) no te pasaste de un límite (mandaste varios seguidos); (4) el group o el ref están bien escritos y esa persona abrió su pase; (5) esa persona no te silenció (le llega sin sonar) ni te cortó.
¿Algo no anda o falta? Escribinos a axel@letschatty.com.