Saltar al contenido principal

Documentation Index

Fetch the complete documentation index at: https://developers.flowestate.app/llms.txt

Use this file to discover all available pages before exploring further.

FlowEstate envía webhooks cuando los leads, proyectos o unidades cambian. Los suscriptores registran una URL destino y un tipo de evento; FlowEstate distribuye requests POST firmados con cuerpo JSON. Para gestionar suscripciones programáticamente (crear, listar, eliminar), ver la Referencia API de webhooks. Esta página cubre lo que tu receptor necesita hacer una vez creada la suscripción.

Headers

Cada entrega incluye:
HeaderDescripción
Content-TypeSiempre application/json.
User-AgentFlowEstate-Webhook/1.0
X-FlowEstate-EventTipo del evento, p.ej. lead.created.
X-FlowEstate-DeliveryUUID de este intento de entrega. Úsalo para procesamiento idempotente.
X-FlowEstate-Signaturesha256=<hex> HMAC-SHA256 del cuerpo crudo exacto usando el secret del endpoint.
X-FlowEstate-SourceOrigen del evento: ui, api o system. Úsalo para evitar bucles — ver más abajo.

Body

{
  "id": "evt_<uuid>",
  "type": "lead.created",
  "createdAt": "2026-04-29T18:04:15.000Z",
  "organization": {
    "id": "org_...",
    "name": "Acme Realty",
    "slug": "acme-realty"
  },
  "data": { /* payload específico del evento */ }
}
data depende del evento:
  • Eventos lead.* llevan data.lead.
  • Eventos project.* llevan data.project.
  • Eventos unit.* llevan data.unit y data.project.
  • lead.note_added lleva data.leadId y data.note.
  • lead.communication_logged lleva data.leadId y data.communication.
Para la lista completa de eventos ver Enums.

Verificar la firma

Verifica siempre la firma antes de confiar en un payload. Cualquiera que conozca tu URL puede pegarle; solo FlowEstate tiene el secret.
import crypto from "node:crypto";

function verify(rawBody, signatureHeader, secret) {
  const expected = "sha256=" + crypto
    .createHmac("sha256", secret)
    .update(rawBody)
    .digest("hex");
  const a = Buffer.from(expected);
  const b = Buffer.from(signatureHeader);
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

// Express
app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => {
  if (!verify(req.body, req.get("X-FlowEstate-Signature"), process.env.FLOWESTATE_SECRET)) {
    return res.status(401).end();
  }
  const event = JSON.parse(req.body.toString("utf8"));
  // ... maneja el evento ...
  res.status(200).end();
});
Firma los bytes crudos del cuerpo. Re-serializar el JSON parseado va a producir una firma diferente porque el orden de las claves, los espacios y el formato de números pueden diferir.
El secret viene en la respuesta de POST /webhooks/subscriptions y se muestra solo una vez. Si lo pierdes, elimina la suscripción y crea una nueva.

Acuse de recibo

Devuelve cualquier status 2xx para acusar recibo. FlowEstate no lee el cuerpo de la respuesta — un 200 vacío está bien. Si tu handler hace trabajo pesado (llama otros APIs, escribe a una BD lenta), acusa primero, procesa después:
app.post("/webhook", (req, res) => {
  if (!verify(...)) return res.status(401).end();
  res.status(200).end();          // ACK primero
  enqueueForBackgroundProcessing(req.body);  // procesa fuera de banda
});
El timeout de entrega es de unos pocos segundos — handlers lentos provocan reintentos innecesarios.

Política de reintentos

Las entregas fallidas (error de red, timeout, o HTTP ≥ 400) se reintentan con este backoff:
IntentoDelay antes del retry
1— (inicial)
21 minuto
35 minutos
430 minutos
52 horas
612 horas
Después de cinco intentos fallidos la entrega se marca como failed y no se reintenta más. El header X-FlowEstate-Delivery se mantiene constante a través de los reintentos — úsalo como tu clave de idempotencia al procesar.

Prevención de bucles

Cuando escribes a FlowEstate vía este API REST, el webhook resultante se distribuye con X-FlowEstate-Source: api. Si tu receptor también escribe de vuelta a FlowEstate, descarta los eventos con source api para evitar un bucle infinito.
if (req.headers["x-flowestate-source"] === "api") {
  return res.status(200).end();  // ack e ignora
}
Los demás valores de source:
  • ui — cambio hecho por un usuario haciendo clic en el panel de FlowEstate.
  • system — cambio hecho por un worker interno (asignaciones, automatizaciones).
  • api — cambio hecho a través de este API REST (tus propias escrituras, plataformas partner).