StoreLyne API docs 2026-11

Concepts

Webhooks

Subscribe to events and verify the signature.

A webhook sends a POST to your URL when something happens in the store, for example order.paid. Webhooks belong to an app, and each topic needs the matching read scope (order topics need read_orders). Managing webhooks needs write_webhooks.

Subscribe

curl https://api.storelyne.com/admin/2026-11/webhooks \
  -H "Authorization: Bearer pat_live_..." \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com/hooks","topics":["order.created","order.paid"]}'

The response has the signing secret (whsec_...). It is shown once. URLs must use https and a public address.

What we send

{ "id": "evt_...", "type": "order.paid", "api_version": "2026-11",
  "created_at": "2026-11-04T10:00:00.000Z", "store_id": "sto_...", "data": { "id": "ord_...", ... } }

Headers: StoreLyne-Signature, StoreLyne-Event-Id and StoreLyne-Topic. Fetch the full resource from the API when you need more than data. Delivery is at least once: use StoreLyne-Event-Id to ignore repeats.

Verify the signature

StoreLyne-Signature: t=1793830000,v1=5f2a.... Compute HMAC-SHA256 of t + "." + raw body with your secret, compare it with v1 in constant time, and reject timestamps older than 5 minutes.

// Node
const crypto = require('node:crypto');
function verify(secret, rawBody, header) {
  const p = Object.fromEntries(header.split(',').map((x) => x.split('=')));
  if (Math.abs(Date.now() / 1000 - Number(p.t)) > 300) return false;
  const want = crypto.createHmac('sha256', secret).update(p.t + '.' + rawBody).digest('hex');
  return want.length === p.v1.length && crypto.timingSafeEqual(Buffer.from(want), Buffer.from(p.v1));
}
# Python
import hashlib, hmac, time
def verify(secret: str, raw_body: bytes, header: str) -> bool:
    p = dict(x.split("=", 1) for x in header.split(","))
    if abs(time.time() - int(p["t"])) > 300:
        return False
    want = hmac.new(secret.encode(), p["t"].encode() + b"." + raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(want, p["v1"])

Retries

Answer with any 2xx within 10 seconds. Anything else is tried again: 15 tries over about 3 days, with growing gaps. If the last try fails, the webhook is paused with the reason; fix the endpoint, resume it, and resend from the delivery log. POST /webhooks/{id}/test sends a webhook.test event at once.