Handling deliveries

When an event occurs that your app is subscribed to, Stok.ly will send a HTTP request to the URL you provided. This page describes how to handle that request.

Request Headers

Each delivery contains the following HTTP headers:

HeaderExampleDescription
Content-Typeapplication/jsonThe content type of the request body. This is always "application/json"
X-Stokly-Subscription-Id8e2e56ea-6ccd-489c-90a7-79249913a9f6The subscription ID responsible for this delivery
X-Stokly-Delivery-Id10b4df71-272a-48ad-8eaa-97ae4136d544A unique identifier (UUID) for this delivery. This will be the same across retries of the same delivery
X-Stokly-Timestamp2026-07-10T10:29:01.619ZThe time this delivery was attempted. This is used to create the signature
X-Stokly-Topicitem.createdThe topic that triggered the delivery
X-Stokly-Payload-Version1.0.0The payload version. This can be changed per subscription
X-Stokly-Signaturesha256=The signature of the request. This is used to validate the request body

Verifying the request

You should verify each webhook delivery to ensure it came from Stok.ly. This can be done by generating a signature from the subscription secret, the timestamp header & the request body.

Here is an example written in NodeJS using the node:crypto library:

const signatureHeader = request.headers["x-stokly-signature"];
const timestampHeader = request.headers["x-stokly-timestamp"];

if (!signatureHeader || !timestampHeader) {
  sendJson(response, 400, { 
    error: "Missing required headers (x-stokly-signature, x-stokly-timestamp)" 
  });
  return;
}

const secret = "_aWG2kSLAZjcxLaL13tIYfHh4JrwuZakJquV56xBcXg"
const rawBody = await readRequestBody(request);
const expected = crypto
  .createHmac("sha256", secret)
  .update(`${timestampHeader}.${rawBody}`)
  .digest("hex");

const received = signatureHeader.replace("sha256=", "");

const valid = crypto.timingSafeEqual(
  Buffer.from(expected, "hex"),
  Buffer.from(received, "hex")
);

if (!valid) {
  sendJson(response, 400, { error: "Invalid signature" });
  return;
}

// signature verified - start your own logic


// return a 200
sendJson(response, 202, {});

Sending a response

Stok.ly considers any 2xx response status as successful. Any other status codes will be treated as an error and will be retried according to our retry logic that is detailed below.

Your server must respond within 5 seconds, otherwise the request will time out.

Retries

Failed requests will be retried a maximum of 15 times with increasing delays

📘

Stok.ly will make 16 attempts in total, consisting of the initial attempt and then up to 15 retries

After each failed delivery attempt, the next request is delayed as follows

Retry NumberDelay
130 seconds
21 minute
35 minutes
415 minutes
530 minutes
61 hour
72 hours
83 hours
94 hours
105 hours
118 hours
1212 hours
1316 hours
1424 hours
1548 hours

If 3 consecutive deliveries reach the maximum number of retries, the webhook subscription will be automatically disabled.


Did this page help you?