Con una llave, tu CRM, tienda en línea, n8n, Zapier o un script de 20 líneas manda mensajes a tus clientes y recibe por webhook lo que te escriben. Sin plantillas, sin aprobaciones de mensajes, sin bloqueos.
ga_live_....Servidor: http://inference.chateacontuempresa.com
POST http://inference.chateacontuempresa.com/api/v1/messages con Authorization: Bearer ga_live_TU_LLAVE.
curl -X POST http://inference.chateacontuempresa.com/api/v1/messages \
-H "Authorization: Bearer ga_live_TU_LLAVE" \
-H "Content-Type: application/json" \
-d '{"to": "+50255551234", "text": "Hola Ana, tu pedido #120 va en camino 🛵"}'
const r = await fetch("http://inference.chateacontuempresa.com/api/v1/messages", {
method: "POST",
headers: { Authorization: "Bearer ga_live_TU_LLAVE", "Content-Type": "application/json" },
body: JSON.stringify({ to: "+50255551234", text: "Hola Ana, tu pedido #120 va en camino 🛵" }),
});
console.log(await r.json()); // { ok: true, id: "...", delivered: "online" | "queued" }
import requests
r = requests.post("http://inference.chateacontuempresa.com/api/v1/messages",
headers={"Authorization": "Bearer ga_live_TU_LLAVE"},
json={"to": "+50255551234", "text": "Hola Ana, tu pedido #120 va en camino 🛵"})
print(r.json())
$ch = curl_init("http://inference.chateacontuempresa.com/api/v1/messages");
curl_setopt_array($ch, [
CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["Authorization: Bearer ga_live_TU_LLAVE", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => json_encode(["to" => "+50255551234", "text" => "Hola Ana, tu pedido #120 va en camino 🛵"]),
]);
echo curl_exec($ch);
| Campo | Tipo | Descripción |
|---|---|---|
to | string | Número con código de país (+502...). Si escribes 8 dígitos se asume Guatemala. |
text | string | Texto del mensaje (hasta 4000 caracteres). |
type | "text" | "image" | Opcional. Si mandas media se asume imagen. |
media | string base64 | Imagen en base64 (JPEG o PNG, máx. 4 MB). |
| Código | Significado |
|---|---|
| 200 | { ok, id, to, delivered: "online" | "queued" }. "queued" = el cliente está sin conexión; lo recibe (con push) en cuanto abra. |
| 401 / 403 | Llave inválida, no aprobada, cuenta suspendida o cliente que bloqueó al negocio. |
| 404 | Ese número no usa Said Chat. |
| 409 | El cliente instaló la app pero todavía no la abrió (no tiene llave de cifrado). |
| 429 | Más de 60 mensajes por minuto. |
Cada vez que un cliente le escribe a tu negocio, Said Chat hace POST a tu URL con este JSON:
{
"event": "message",
"id": "5f1c...",
"at": 1757350000000,
"from": { "id": "06ac...", "phone": "+50255551234", "name": "Ana" },
"type": "text", // text | image | audio | location | order | appointment
"text": "Hola, ¿tienen envío a zona 10?",
"media": "...base64...", // solo en image / audio
"data": { ... } // en order / appointment: items, total, when, note
}
Responde 200 en menos de 10 segundos. Si no, reintentamos 3 veces. Firma: la cabecera X-SaidChat-Signature: sha256=HMAC_SHA256(secreto, cuerpo). Verifícala así:
// Node.js (Express)
import crypto from "node:crypto";
app.post("/saidchat", express.raw({ type: "*/*" }), (req, res) => {
const sig = "sha256=" + crypto.createHmac("sha256", process.env.SAIDCHAT_SECRET).update(req.body).digest("hex");
if (sig !== req.get("x-saidchat-signature")) return res.sendStatus(401);
const event = JSON.parse(req.body);
console.log(event.from.phone, event.text);
res.sendStatus(200);
});
# Python (Flask)
import hmac, hashlib
@app.post("/saidchat")
def hook():
sig = "sha256=" + hmac.new(SECRET.encode(), request.get_data(), hashlib.sha256).hexdigest()
if not hmac.compare_digest(sig, request.headers.get("X-SaidChat-Signature", "")): abort(401)
event = request.get_json(); print(event["from"]["phone"], event["text"]); return "", 200
Prueba tu webhook con POST /api/v1/webhook/test (con tu llave) o con el botón Probar webhook del panel.
| Método y ruta | Para qué |
|---|---|
GET /api/v1/me | Datos del negocio, webhook configurado, contadores y si está en línea. |
POST /api/v1/webhook/test | Manda un evento de prueba a tu webhook. |
Si ya tienes tus productos en tu sistema, publica un JSON y pega la URL en el panel (Productos → Sincronizar desde URL). Se actualiza cada 6 horas o cuando toques "Sincronizar ahora". Formato aceptado: una lista, o un objeto con products, items o catalog.
[
{ "id": "SKU-1", "name": "Capuchino", "price": 25, "description": "Con leche entera o de avena", "category": "Cafe", "image": "https://tu-sitio.com/img/capuchino.jpg" },
{ "id": "SKU-2", "name": "Croissant", "price": 22, "category": "Panaderia" }
]
Las imágenes pueden ser URL (https) o base64. En Excel/CSV, las columnas son nombre, precio, descripcion, categoria, imagen.
POST /api/v1/messages con "tu pedido va en camino".type: "appointment", crea el evento en tu agenda y confirma con un mensaje.
Said Chat API para negocios
Conecta tu sistema (CRM, tienda, n8n, Zapier, un PHP) para mandar mensajes a tus clientes y recibir lo que te escriben. Sin plantillas que aprobar, sin ventanas de 24 horas, sin bloqueos por “spam”.
1. Consigue tu llave
- En la app: Ajustes → Cuenta → Mi negocio → Conectar mi sistema → Solicitar.
- Said Chat revisa y aprueba tu negocio. En la app aparece tu
API key y tu secreto. - Ahí mismo pones la URL de tu webhook (adónde te mandamos los mensajes que recibes).
2. Mandar un mensaje
curl -X POST http://TU-SERVIDOR:3000/api/v1/messages \
-H "Authorization: Bearer ga_live_TU_LLAVE" \
-H "Content-Type: application/json" \
-d '{"to": "+50255551234", "text": "Hola Juan, tu pedido #123 ya salio"}'
Respuesta: {"ok":true,"id":"...","to":{...},"delivered":"online"|"queued"}. Si el cliente no usa Said Chat: 404 not_found. Si te bloqueo: 403 blocked.
Imagen: {"to":"+502...","type":"image","media":"<base64 del jpg>","text":"Tu factura"}
3. Recibir mensajes (webhook)
Cada mensaje que un cliente te escribe llega como POST a tu URL:
{
"event": "message",
"id": "uuid",
"at": 1757300000000,
"from": { "id": "uuid", "phone": "+50255551234", "name": "Juan" },
"type": "text",
"text": "Quiero 2 pizzas"
}
Cabecera X-SaidChat-Signature: sha256=HMAC_SHA256(secreto, cuerpo). Responde 200 en menos de 10 s; si no, reintentamos 3 veces.
Probar tu webhook: POST /api/v1/webhook/test con tu llave.
4. Ejemplos
Node.js (Express)
app.post("/guatapp", express.json(), async (req, res) => {
const { from, text } = req.body; // lo que escribio el cliente
res.sendStatus(200);
if (/precio/i.test(text)) {
await fetch("http://TU-SERVIDOR:3000/api/v1/messages", {
method: "POST",
headers: { Authorization: "Bearer ga_live_TU_LLAVE", "Content-Type": "application/json" },
body: JSON.stringify({ to: from.phone, text: "La pizza grande cuesta Q80. Te mando una?" }),
});
}
});
PHP
$data = json_decode(file_get_contents("php://input"), true);
$ch = curl_init("http://TU-SERVIDOR:3000/api/v1/messages");
curl_setopt_array($ch, [CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ["Authorization: Bearer ga_live_TU_LLAVE", "Content-Type: application/json"],
CURLOPT_POSTFIELDS => json_encode(["to" => $data["from"]["phone"], "text" => "Gracias, ya lo vimos"])]);
curl_exec($ch);
n8n / Zapier / Make: nodo Webhook (recibe) → tu logica → nodo HTTP Request (POST a /api/v1/messages con la cabecera Authorization).
5. Limites y reglas
Regla Valor Mensajes por minuto por llave 60 Tamano de imagen 3 MB en base64 Quien puede recibir Cualquier usuario de Said Chat que no te haya bloqueado Privacidad Los chats con un negocio conectado tambien se cifran para el sistema del negocio; el cliente ve un aviso en el chat.
6. Errores
Codigo Significado 401 invalid_key Llave mal escrita o revocada 403 not_approved Said Chat aun no aprobo tu negocio 404 not_found Ese numero no usa Said Chat 409 no_key El cliente instalo pero no ha abierto la app 429 rate_limited Pasaste de 60 por minuto