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

# HMAC Signature Verification

> How to verify the authenticity of MarIA webhooks using HMAC-SHA256

For each webhook request:

1. Read the **RAW body** exactly as sent by the server (watch out for middlewares that parse/modify it).
2. Compute `HMAC_SHA256(secret, rawBody)`.
3. Convert to **lowercase hex** and compose `sha256=<hex>`.
4. Compare in **constant time** with the value in the `X-Integration-Signature` header.

<Warning>
  Reject the request if the time skew of the `Date` header (if sent) exceeds 5 minutes.
</Warning>

### Node.js (Express)

```js theme={null}
import crypto from 'node:crypto';

export function verifySignature({ secret, rawBody, header }) {
  const digest = crypto
    .createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex');
  const expected = `sha256=${digest}`;
  const provided = String(header || '');
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(provided)
  );
}
```

### Python (FastAPI / Starlette)

```py theme={null}
import hmac, hashlib

def verify_signature(secret: str, raw_body: bytes, header: str) -> bool:
    digest = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
    expected = f"sha256={digest}"
    try:
        return hmac.compare_digest(expected, header or "")
    except Exception:
        return False
```

## Retries and idempotency

If your endpoint responds with `>=500` or does not respond in time, MarIA will retry the delivery.

## Common errors

<Warning>
  Reading an already-parsed `req.body` loses the RAW bytes. Use middleware to preserve the buffer before any JSON parser.
</Warning>
