Webhooks

Developer docs: all pages

Start

Help and docs

Changes

01Subscribe

In Supplier Admin or Retailer Admin, open Developers and add the HTTPS endpoint and events. Save the whsec_ signing secret when it appears; it is shown once. The same page lists deliveries and sends a test.

02Events

EventForWhen
stock.updatedRetailersA stock row changed; stockId and updatedAt.
stock.updated.v2RetailersThe same change, with tier, provenance and observation time.
stock.confirmedRetailersA person confirmed stock without changing it.
lead.createdBrandsA shopper reserved a product.
import.completedBrandsAn import reached a terminal outcome.

Payloads carry identifiers and times, not personal data. Read details through the API. Stock updates deliver the latest state; confirmations are not collapsed.

03Check the signature

Read X-Stockisto-Event, X-Stockisto-Delivery-Id and X-Stockisto-Signature. The signature header is t=<unixSeconds>,v1=<hexHmac>. Verify HMAC-SHA256 over the timestamp, a full stop and the raw request body. Never reserialize parsed JSON.

Accept any matching v1 during rotation, ignore unknown tags, and reject timestamps more than 5 minutes away. Pass the unmodified body as a Buffer or UTF-8 string to this Node.js function. Return 2xx only after accepting the event; deduplicate by delivery id.

X-Stockisto-Event: lead.created
X-Stockisto-Delivery-Id: DELIVERY_ID
X-Stockisto-Signature: t=1791374400,v1=SIGNATURE
Node.js
const crypto = require("node:crypto");

function verify(header, rawBody, secret) {
    const pairs = String(header)
        .split(",")
        .map((p) => p.trim().split("="));
    const timestamps = pairs.filter(([k]) => k === "t");
    if (timestamps.length !== 1) return false;
    const t = timestamps[0][1];
    if (!/^\d+$/.test(t) || Math.abs(Date.now() / 1000 - Number(t)) > 300)
        return false;
    const want = crypto
        .createHmac("sha256", secret)
        .update(t + ".")
        .update(rawBody)
        .digest();
    return pairs.some(
        ([k, v]) =>
            k === "v1" &&
            /^[0-9a-f]{64}$/.test(v) &&
            crypto.timingSafeEqual(Buffer.from(v, "hex"), want),
    );
}

04Retries

A delivery gets up to 5 attempts, with retries after 1, 5, 30 and 120 minutes. Inspect the delivery log in Developers. A blocked endpoint can fail without further attempts.

05Rotate the secret

  1. Choose Rotate and save the new secret and the old secret’s retirement time.
  2. Both secrets sign each delivery for 7 days. Deploy the new secret during that overlap.
  3. After 7 days, only the new secret signs. A new signature scheme adds a tag such as v2; the old scheme stays for at least 90 days.