> ## Documentation Index
> Fetch the complete documentation index at: https://docs.jelou.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Firma HMAC-SHA256

> Generación y verificación de X-Jelou-Signature en el Canal Personalizado

El header `X-Jelou-Signature` con formato `sha256=<HMAC_HEX>` se usa en **ambas direcciones**. La `signingKey` es obligatoria al activar el canal.

* **Entrante (tu app → Jelou):** debes generar la firma al llamar `POST /v1/custom-channel/:botId` y `POST /v1/custom-channel/:botId/status`.
* **Saliente (Jelou → tu webhook):**  verifica la firma de cada entrega a tu `webhookUrl`, especialmente útil para garantizar un mensaje legítimo y sin modificaciones.

La firma es **independiente** de `credentials.auth` (api\_key / bearer / basic).

***

## Generar firma en peticiones entrantes

Firma el **raw body** exacto que envías (los mismos bytes del body HTTP), no un objeto re-serializado distinto. Aplica a enviar interacción y a consultar estado.

```javascript theme={null}
const crypto = require("crypto");

function signBody(rawBody, signingKey) {
  const digest = crypto
    .createHmac("sha256", signingKey)
    .update(rawBody)
    .digest("hex");

  return `sha256=${digest}`;
}

const body = JSON.stringify({
  referenceId: "user-123",
  message: { type: "TEXT", text: "Hola" },
});

const signature = signBody(body, process.env.SIGNING_KEY);
// Header: X-Jelou-Signature: sha256=...
```

<Warning>
  Usa el mismo string/buffer que envías en el request. Re-serializar el JSON con distinto orden de claves o espacios rompe la verificación.
</Warning>

***

## Verificar firma de eventos recibidos en tu webhook

Jelou firma el JSON stringificado del payload saliente (`JSON.stringify` del envelope completo).

```javascript theme={null}
const crypto = require("crypto");

function verifySignature(rawBody, signatureHeader, signingKey) {
  const expected = `sha256=${crypto
    .createHmac("sha256", signingKey)
    .update(rawBody)
    .digest("hex")}`;

  if (!signatureHeader) {
    return false;
  }

  const provided = Buffer.from(signatureHeader, "utf8");
  const expectedBuf = Buffer.from(expected, "utf8");

  if (provided.length !== expectedBuf.length) {
    return false;
  }

  return crypto.timingSafeEqual(provided, expectedBuf);
}
```

Lee el body como raw string (antes de parsearlo) y compáralo con el header `X-Jelou-Signature`.
