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.

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.gualda.ai   # 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

  1. Entrá a /panel y iniciá sesión con tu mail.
  2. 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.gualda.ai/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.gualda.ai/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_text es 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):

DestinoQué esLe llega a
ningunodifusióntodos los suscriptos a tu empresa
"group": "palermo"el código de uno de tus QRsólo a los que se sumaron por ese QR
"ref": "cliente-1042"el id con que vos conocés a la personasó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
titlesíLo que se lee en la pantalla bloqueada. Hasta 100 caracteres.
bodysíEl texto del aviso. Hasta 1000 caracteres; el push muestra los primeros 140 y el resto se lee al abrir la app.
emojinoUn solo emoji, al lado del título (🥬 sí; 🥬🍅 no; una letra no).
ctanoUn botón con un link. label y url van juntos.
cta.labelcon ctaEl texto del botón. Hasta 40 caracteres.
cta.urlcon ctaA dónde lleva. Hasta 2048 caracteres. Empieza con http://, https://, mailto:, tel:, sms: o whatsapp:.
groupnoCódigo de uno de tus QR. Hasta 24 caracteres.
refnoTu 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.

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.

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ímiteValor
Llamadas de envío por minuto, por token60
Difusiones por hora, por empresa (las de «todos» o «un QR»; los avisos a una persona con ref no cuentan)20
Personas por difusiónhasta 5.000
Avisos a la misma personauno 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ódigoCuándoCuerpo
202el pedido está bien armado{"id": "…"}
400un 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é ]}
401falta 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_….

  1. En el panel (/panel), pestaña «Conectar», tocá Crear.
  2. Copialo ahí mismo: se muestra una sola vez. Si lo perdés, creás otro y revocás el viejo.
  3. 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.gualda.ai/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.

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.

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.gualda.ai/mcp \
  --header "Authorization: Bearer ntf_TU_TOKEN"

Para Claude Desktop u otra compu, escribinos y te pasamos los pasos. Tres pedidos que funcionan:

  1. «Avisale a todos que hoy abrimos hasta las 22.»
  2. «Creame un QR para la sucursal de Belgrano y decime la página para imprimirlo.»
  3. «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 rutaAuthQué hace
POST /sendtokenManda un aviso (a todos, a un QR con group o a una persona con ref). Siempre 202 {"id"}.
POST /company/sendtoken o panelIgual que /send.
GET /company/metoken o panelTu empresa: nombre, código, texto del QR y página.
GET /company/groupstoken o panelTus QR (?archived=true incluye los archivados).
POST /company/groupstoken o panelCrea un QR: {name, code?}.
PATCH /company/groups/{id}token o panelRenombra o archiva un QR: {name?, archived?}.
POST /company/connect-linkstoken o panelPase para vincular a UNA persona: {ref, group?} → {url, qr_text, expires_at}.
GET /company/tokenspanelLista tus tokens (nunca el valor).
POST /company/tokenspanelCrea un token; se ve en claro una sola vez.
DELETE /company/tokens/{id}panelRevoca un token.
GET /company/companiespanelLas empresas a las que entra tu mail.
GET /e/{código}públicaPágina con el QR de una empresa o de un QR, para imprimir.
GET /c/{pase}públicaPágina de vinculación de una persona.
GET /empresas/openapi.jsonpúblicaEsta API en formato OpenAPI, para generar clientes.

Preguntas frecuentes

¿Puedo ver cuánta gente tengo?

No, y es a propósito. Lo más cerca que hay es signups en cada QR por grupo (GET /company/groups o la pestaña «Mis QR»): cuántas personas se sumaron alguna vez por ese QR. Sólo sube; no baja aunque alguien se vaya, así que no te dice cuántos te siguen hoy. Tu QR general no tiene contador: 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), 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.