Saltar al contenido
AdSmartBotAyuda

Avisos a otros sistemas

Cuando pasa algo en la cuenta (llega un mensaje, se agenda una cita, se le agrega una etiqueta a un cliente), AdSmartBot le avisa al instante a la dirección que elijas: tu sistema, Zapier, Make o n8n.

Suscribirse

Hay dos formas, y las dos dan lo mismo:

  • En el panel: Configuración → Integraciones → API y avisos → Avisos a otros sistemas → Agregar aviso. Eliges la dirección, qué avisar y, si quieres, un filtro («solo la etiqueta VIP»).
  • Por la API: POST https://panel.adsmartbot.com/api/v1/webhooks con una clave que tenga «Gestionar los avisos a otros sistemas» (webhooks:manage). Así se suscriben solas las integraciones como Zapier.

La respuesta trae una sola vez la clave para comprobar la firma (whsec_…): guárdala. Cada clave de acceso solo ve y cambia los avisos que ella creó; los del panel y los de otras claves no existen para ella.

Como mucho 25 avisos por cuenta y 5 hacia un mismo dominio. Zapier, Make, n8n y Pabbly solo cuentan para el máximo de la cuenta.

Lo que recibe tu sistema

Un POST con JSON. Todos los avisos tienen la misma forma: type dice qué pasó, data trae los datos completos de ese momento (el contacto entero, con sus etiquetas y campos), actor quién lo hizo y cause qué sistema empezó la cadena, si fue uno de afuera.

  • Responde con cualquier 2xx en menos de 10 segundos. Procesa despacio después, si hace falta.
  • El mismo aviso puede llegar dos veces (si tu respuesta se perdió): reconócelo por su id, que es el mismo en cada intento.
  • El orden no está garantizado: usa created_at del aviso y updated_at de los objetos.
Ejemplo: contact.tag_added
{
  "id": "3f6c2a0e-5b7d-4c1e-9a2f-7d1b0c9e8a41",
  "object": "event",
  "type": "contact.tag_added",
  "api_version": "v1",
  "created_at": "2026-09-26T19:40:00.123Z",
  "account": {
    "id": "9b1d7c20-3e4f-4a5b-8c6d-0e1f2a3b4c5d",
    "name": "Peluquería Ana"
  },
  "actor": {
    "type": "team_member",
    "id": "b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e",
    "name": "Laura"
  },
  "cause": {
    "origin": {
      "type": "api_key",
      "id": "c3d4e5f6-a7b8-4c9d-8e0f-1a2b3c4d5e6f",
      "name": "Tienda"
    },
    "hops": 0
  },
  "data": {
    "contact": {
      "id": "a4f2c0de-8b1e-4c51-9a7a-2f1d9c0b77e1",
      "object": "contact",
      "channel": "whatsapp",
      "first_name": "Ana",
      "last_name": "Gómez",
      "full_name": "Ana Gómez",
      "phone": "+573001234567",
      "email": "ana@correo.com",
      "email_verified": false,
      "whatsapp": {
        "wa_id": "573001234567",
        "bsuid": null,
        "username": null
      },
      "profile_pic_url": null,
      "locale": null,
      "timezone": "America/Bogota",
      "origin": "direct_message",
      "tags": [
        {
          "id": "b8c9d0e1-f2a3-4b4c-9d5e-6f7a8b9c0d1e",
          "name": "VIP"
        }
      ],
      "fields": {
        "Ciudad": "Cali",
        "Talla": "M"
      },
      "broadcasts_subscribed": true,
      "broadcasts_opt_out": null,
      "whatsapp_opt_in": null,
      "email_unsubscribed": false,
      "blocked": false,
      "interactions_count": 12,
      "has_written": true,
      "last_inbound_at": "2026-09-26T19:39:58.000Z",
      "last_outbound_at": "2026-09-26T19:35:10.000Z",
      "whatsapp_windows": [
        {
          "channel_id": "c7e1b2a3-4d5e-4f60-8a71-b2c3d4e5f607",
          "open": true,
          "closes_at": "2026-09-27T19:39:58.000Z"
        }
      ],
      "created_at": "2026-09-01T10:00:00.000Z",
      "updated_at": "2026-09-26T19:40:00.120Z"
    },
    "tag": {
      "id": "b8c9d0e1-f2a3-4b4c-9d5e-6f7a8b9c0d1e",
      "name": "VIP"
    }
  }
}

Comprobar la firma

Cada aviso llega firmado con el formato abierto Standard Webhooks, con tres cabeceras:

Cabecera Qué es
webhook-id El id del aviso: el mismo en cada reintento
webhook-timestamp La hora del intento, en segundos Unix
webhook-signature v1, y la firma: HMAC-SHA256 de {id}.{timestamp}.{cuerpo} con tu clave, en base64

Comprueba la firma con el cuerpo tal como llegó (antes de leerlo como JSON) y rechaza lo que tenga más de 5 minutos: así nadie puede hacerse pasar por nosotros ni repetir un aviso viejo. Al cambiar la clave, durante 24 horas llegan dos firmas separadas por un espacio: basta con que cuadre una. Hay librerías oficiales para JavaScript, Python, PHP, Go, Ruby, Java y C#.

JavaScript (librería oficial)
// npm install standardwebhooks
import { Webhook } from "standardwebhooks";

const wh = new Webhook(process.env.CLAVE_DE_FIRMA); // whsec_…

// Con el cuerpo TAL COMO LLEGÓ (texto), antes de leerlo como JSON:
const aviso = wh.verify(cuerpoCrudo, {
  "webhook-id": req.headers["webhook-id"],
  "webhook-timestamp": req.headers["webhook-timestamp"],
  "webhook-signature": req.headers["webhook-signature"],
});
// Si la firma no cuadra o tiene más de 5 minutos, `verify` lanza un error: responde 400.
Python (librería oficial)
# pip install standardwebhooks
from standardwebhooks.webhooks import Webhook

wh = Webhook(os.environ["CLAVE_DE_FIRMA"])  # whsec_…
aviso = wh.verify(cuerpo_crudo, dict(request.headers))
JavaScript, sin librerías
import { createHmac, timingSafeEqual } from "node:crypto";

function firmaValida(cuerpoCrudo, cabeceras, clave) {
  const id = cabeceras["webhook-id"];
  const hora = cabeceras["webhook-timestamp"];
  // Más de 5 minutos: se descarta (así nadie puede repetir un aviso viejo).
  if (Math.abs(Date.now() / 1000 - Number(hora)) > 300) return false;
  const secreto = Buffer.from(clave.replace(/^whsec_/, ""), "base64");
  const esperada = createHmac("sha256", secreto).update(`${id}.${hora}.${cuerpoCrudo}`).digest();
  // Pueden venir dos firmas (al cambiar la clave): basta con que cuadre una.
  return cabeceras["webhook-signature"].split(" ").some((parte) => {
    const [version, firma] = parte.split(",");
    const recibida = Buffer.from(firma ?? "", "base64");
    return version === "v1" && recibida.length === esperada.length && timingSafeEqual(recibida, esperada);
  });
}

Reintentos, pausa y registro

  • Si tu sistema no responde con un 2xx (o tarda más de 10 segundos), se reintenta a 1 minuto, 5 minutos, 30 minutos, 2 horas, 6 horas y 24 horas: 7 intentos en unas 33 horas. Con 429, se respeta tu Retry-After si es más largo.
  • 410 Gone apaga el aviso al instante: es la forma estándar de decir «no quiero más avisos».
  • Las redirecciones no se siguen: usa la dirección final.
  • Pausa automática: si todo falla durante 3 días seguidos (y van al menos 20 entregas), el aviso queda en pausa y el dueño de la cuenta recibe un aviso. Lo que pase mientras tanto se guarda 7 días y sale en orden, a 10 por segundo, al volver a encenderlo.
  • Registro de entregas: 30 días, con el código que respondió tu sistema y un pedazo de su respuesta. «Reenviar» vuelve a enviar exactamente el mismo aviso. Por la API: GET /webhooks/{id}/deliveries.

Sin bucles entre sistemas

Un Zap que agrega una etiqueta cada vez que le llega «Se le agrega una etiqueta» se llamaría a sí mismo sin fin. Para evitarlo:

  • Poner el mismo valor no avisa: si un campo ya tenía ese valor, no sale contact.field_changed.
  • skip_own_events (encendido por defecto en los avisos que crea una clave): no recibes lo que provocó tu propia clave, aunque en medio haya pasado una regla o un flujo. cause.origin dice quién empezó la cadena.
  • Un freno por si acaso: más de 20 avisos del mismo tipo para el mismo contacto en un minuto, provocados por otro sistema, se detienen 10 minutos y el dueño recibe un aviso.

Cada aviso pide que la clave que lo creó pueda leerlo (un aviso nunca da lo que la clave no puede leer), y se revisa otra vez en cada entrega. GET https://panel.adsmartbot.com/api/v1/event-types da este mismo catálogo, con un ejemplo de cada uno.

Contacto

Conversaciones

Secuencias

CRM

Citas

Flujos

Llamadas

Correo

Masivos

WhatsApp

Prueba

«Se acerca una cita», «Lleva tiempo sin contestar» y «Fecha de un campo» no salen como aviso directo: cada uno depende de cuántas horas antes o después elijas. Se hacen con una regla (Flujos → Reglas) cuya acción sea «Enviar a un webhook».