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

# Recibir webhooks

> Notificaciones HTTP POST salientes cuando algo cambia en FlowEstate. Headers, verificación de firma, política de reintentos, prevención de bucles.

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](/es/api-reference/webhooks). Esta página cubre lo que tu receptor necesita hacer una vez creada la suscripción.

## Headers

Cada entrega incluye:

| Header                   | Descripción                                                                           |
| ------------------------ | ------------------------------------------------------------------------------------- |
| `Content-Type`           | Siempre `application/json`.                                                           |
| `User-Agent`             | `FlowEstate-Webhook/1.0`                                                              |
| `X-FlowEstate-Event`     | Tipo del evento, p.ej. `lead.created`.                                                |
| `X-FlowEstate-Delivery`  | UUID de este intento de entrega. Úsalo para procesamiento idempotente.                |
| `X-FlowEstate-Signature` | `sha256=<hex>` HMAC-SHA256 del **cuerpo crudo exacto** usando el secret del endpoint. |
| `X-FlowEstate-Source`    | Origen del evento: `ui`, `api` o `system`. Úsalo para evitar bucles — ver más abajo.  |

## Body

```json theme={null}
{
  "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](/es/enums#webhookeventtype).

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

<CodeGroup>
  ```js Node.js theme={null}
  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();
  });
  ```

  ```python Python theme={null}
  import hmac
  import hashlib

  def verify(raw_body: bytes, signature_header: str, secret: str) -> bool:
      expected = "sha256=" + hmac.new(
          secret.encode(),
          raw_body,
          hashlib.sha256,
      ).hexdigest()
      return hmac.compare_digest(expected, signature_header or "")
  ```

  ```php PHP theme={null}
  function flowestate_verify(string $rawBody, string $signatureHeader, string $secret): bool {
      $expected = "sha256=" . hash_hmac("sha256", $rawBody, $secret);
      return hash_equals($expected, $signatureHeader);
  }
  ```
</CodeGroup>

<Warning>**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.</Warning>

El secret viene en la respuesta de [`POST /webhooks/subscriptions`](/es/api-reference/webhooks#crear-una-suscripcion) 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**:

```js theme={null}
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:

| Intento | Delay antes del retry |
| ------- | --------------------- |
| 1       | — (inicial)           |
| 2       | 1 minuto              |
| 3       | 5 minutos             |
| 4       | 30 minutos            |
| 5       | 2 horas               |
| 6       | 12 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.

```js theme={null}
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).
