Register a subscription

The secret is generated server-side and returned exactly once. Capture it immediately — Index has no way to show it again.

Your subscriptions

Read-only — to change which events a subscription receives, delete it and register a new one.

Loading…
Verify the signature

Every outgoing delivery carries the headers below. Compute HMAC-SHA256(secret, raw_body) and compare with X-INDEX-Signature using a constant-time check — never ==, which leaks the secret over time.

# Flask receiver — verify X-INDEX-Signature before trusting the payload.
import hmac, hashlib
from flask import Flask, request, abort

app = Flask(__name__)
SECRET = "<your subscription secret>".encode()

@app.route("/webhook", methods=["POST"])
def receive():
    raw  = request.get_data()                       # bytes, BEFORE any parsing
    sent = request.headers.get("X-INDEX-Signature", "")
    want = "sha256=" + hmac.new(SECRET, raw, hashlib.sha256).hexdigest()
    if not hmac.compare_digest(sent, want):
        abort(401)
    payload = request.get_json()
    # …handle payload[event]…
    return "", 204
// Express receiver — express.raw() preserves the body for HMAC.
const express = require("express");
const crypto  = require("crypto");
const SECRET  = "<your subscription secret>";

const app = express();
app.post(
  "/webhook",
  express.raw({ type: "application/json" }),
  (req, res) => {
    const sent = req.headers["x-index-signature"] || "";
    const want = "sha256=" +
      crypto.createHmac("sha256", SECRET).update(req.body).digest("hex");
    const a = Buffer.from(sent), b = Buffer.from(want);
    if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
      return res.sendStatus(401);
    }
    const payload = JSON.parse(req.body.toString("utf8"));
    // …handle payload.event…
    res.sendStatus(204);
  }
);
# Reproduce a delivery against a local receiver. Hash MUST be over the
# exact bytes you POST — re-serializing the JSON will change the digest.
BODY='{"event":"entry.created","entry_id":"e_demo","test":true}'
SECRET='<your subscription secret>'
SIG="sha256=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$SECRET" | awk '{print $2}')"

curl -X POST http://localhost:3000/webhook \
  -H "Content-Type: application/json" \
  -H "X-INDEX-Event: entry.created" \
  -H "X-INDEX-Delivery: 00000000-0000-0000-0000-000000000000" \
  -H "X-INDEX-Signature: $SIG" \
  --data-binary "$BODY"