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

# HMAC-SHA256 signature

> Generate and verify X-Jelou-Signature for the Custom Channel

The `X-Jelou-Signature` header with format `sha256=<HMAC_HEX>` is used in **both directions**. The `signingKey` is required when you enable the channel.

* **Inbound (your app → Jelou):** you must generate the signature when calling `POST /v1/custom-channel/:botId` and `POST /v1/custom-channel/:botId/status`.
* **Outbound (Jelou → your webhook):** verify the signature on every delivery to your `webhookUrl`, especially useful to ensure the message is legitimate and unmodified.

Signing is **independent** of `credentials.auth` (api\_key / bearer / basic).

***

## Generate a signature on inbound requests

Sign the exact **raw body** you send (the same HTTP body bytes), not a differently re-serialized object. Applies to send interaction and check status.

```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: "Hello" },
});

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

<Warning>
  Use the same string/buffer you send in the request. Re-serializing JSON with a different key order or whitespace breaks verification.
</Warning>

***

## Verify signatures on events received at your webhook

Jelou signs the stringified JSON of the outbound payload (`JSON.stringify` of the full envelope).

```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);
}
```

Read the body as a raw string (before parsing) and compare it to the `X-Jelou-Signature` header.
