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

# Client Setup

## At a glance

| | |
| :- | :- |
| **Request** | `POST` to your URL, `Content-Type: application/json; charset=utf-8` |
| **Signature** | `X-Webhook-Signature: sha256=<HEX>`, HMAC-SHA256 of the raw body with your secret |

## 1. Get set up with Flinks

Send your Flinks contact the HTTPS URL that will receive the events. Provide one per environment (sandbox and production).

In return you receive a webhook secret, used to verify every event. Store it like a password, for example in a secrets manager or environment variable, never in source control. Keep sandbox and production secrets separate.

## 2. Verify the signature

Every request carries `X-Webhook-Signature: sha256=<HEX>`. Check it before trusting the body.

* Compute HMAC-SHA256 over the **raw request body bytes**, keyed with your secret (UTF-8). Do not parse and re-serialize the JSON first: the bytes must match exactly.
* The hex in the header is **uppercase**. Compare case-insensitively and in constant time.
* Respond `401` if it does not match.
* Optional replay protection: `when` is part of the signed body, so you can reject events older than a few minutes.

### Example: Node.js (Express)

```javascript theme={null}
import crypto from "node:crypto";
import express from "express";

const app = express();
const SECRET = process.env.FLINKS_WEBHOOK_SECRET;

app.post("/webhooks/flinks", express.raw({ type: "application/json" }), (req, res) => {
  const received = (req.get("X-Webhook-Signature") ?? "").toLowerCase();
  const expected = "sha256=" +
    crypto.createHmac("sha256", SECRET).update(req.body).digest("hex");

  const a = Buffer.from(received);
  const b = Buffer.from(expected);
  if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
    return res.sendStatus(401);
  }

  const event = JSON.parse(req.body.toString("utf8"));
  res.sendStatus(200); // ack first: there are no retries
  handleEvent(event);  // then process (queue, background job, ...)
});

app.listen(3000);
```
