FOMDocs

Webhooks

Get a signed message on your server when an order, stock or delivery changes.

A webhook is an HTTPS address on your server. When something happens in your shop, FOM sends it a POST with the details, so your tools don't have to keep asking.

Add an endpoint

In Settings → Developers, under Webhooks, click Add endpoint. Enter your URL (it must start with https:// and be reachable from the internet), and tick the events to send. Copy the signing secret shown next: it starts with whsec_ and is shown only once. A shop can have up to 10 endpoints.

Click Send test to send a ping event and see your server's answer.

Events

EventSent whendata
order.createdAn order is created, in FOM or through the API{ "order": … }
order.status_changedAn order's status changes{ "order": … }
stock.lowA variant's stock falls to its low-stock level, or to 0 when none is set{ "variant_id": "…", "product": … }
delivery.updatedA delivery is created or changes{ "delivery": … }
pingYou click Send test{ "message": "…" }

order, product and delivery have the same fields as in the API: see the order, product and delivery objects. They show the record as it was when the event happened.

POST /fom-webhooks HTTP/1.1
Content-Type: application/json
User-Agent: FOM-Webhooks/1.0
FOM-Event: order.created
FOM-Delivery: cmw9u2k0a0001…
FOM-Signature: t=1795680000,v1=5f2b8c…

{
  "id": "cmw9u1z4e0000…",
  "type": "order.created",
  "created_at": "2026-11-25T08:00:00.000Z",
  "shop_id": "…",
  "data": { "order": { "id": "…", "order_no": "ORD-0319", "status": "new", … } }
}

Answer quickly

  • Reply with any 2xx status within 10 seconds. Do slow work afterwards, for example in a queue.
  • Anything else, a timeout or a redirect counts as a failure.
  • Events for one endpoint arrive in the order they happened; the next one waits until the earlier one succeeds or gives up.
  • The same event can arrive more than once. Use its id to skip ones you've handled.

Retries

A failed send is tried again after 1 minute, 5 minutes, 30 minutes, 2 hours, 6 hours and 24 hours, then marked Failed. Click Deliveries on an endpoint to see each send, your server's answer, and Resend any of them. Sends are kept for 30 days.

After 50 failures in a row FOM turns the endpoint off and tells the Owner. Fix your server, then click Turn on.

Check the signature

Check every message before trusting it, so nobody else can send fake events to your URL. FOM-Signature has a time t and a signature v1: the HMAC-SHA256 of t, a dot and the raw request body, made with your signing secret, in hex.

  1. Read the raw body before any JSON parsing.
  2. Compute HMAC-SHA256(secret, t + "." + body) and compare it with v1 in constant time.
  3. Refuse messages whose t is more than 5 minutes from now, to stop replays.
import { createHmac, timingSafeEqual } from "node:crypto"

export function verifyFomSignature(rawBody, header, secret) {
  const parts = Object.fromEntries(
    header.split(",").map((part) => part.trim().split("="))
  )
  const t = Number(parts.t)
  if (!parts.v1 || Math.abs(Date.now() / 1000 - t) > 300) return false
  const expected = createHmac("sha256", secret)
    .update(`${t}.${rawBody}`)
    .digest()
  const given = Buffer.from(parts.v1, "hex")
  return given.length === expected.length && timingSafeEqual(given, expected)
}

// Express: keep the raw body.
app.post("/fom-webhooks", express.raw({ type: "application/json" }), (req, res) => {
  if (!verifyFomSignature(req.body, req.get("FOM-Signature") ?? "", process.env.FOM_WEBHOOK_SECRET)) {
    return res.sendStatus(400)
  }
  const event = JSON.parse(req.body)
  res.sendStatus(200)
})

The TypeScript SDK has this built in as constructWebhookEvent.

Change the secret

Click New secret on an endpoint to make a new signing secret, for example if the old one leaked. The old one stops at once, so update your server straight away.

On this page