OrderDen is pre-release software. There is no paid plan yet, no support model, and no guarantee your data will be preserved. Read this before you put your business in it or tell us what broke.

Docs / Reference

Webhooks & automation

Get told the moment something happens instead of asking: signed deliveries with retries, the polling filter for anything without a public URL, idempotency keys, Zapier and the MCP server.

The API lets a program read and change your workspace. Webhooks are the other direction: OrderDen tells your program the moment something happens, so nothing has to sit there asking "anything yet?" every thirty seconds.

Set them up in Company Settings → API keys → Webhooks.

Adding an endpoint

Give it an HTTPS URL that can be reached from the internet, say what it is for, and pick your events — or leave Everything ticked, which keeps working as new event kinds are added.

You get a signing secret, shown once. Copy it there and then: it is not stored anywhere you can read it back, which is the whole point of a signature. Lose it and you rotate it, which mints a new one and stops the old one immediately.

Send test queues an obviously fake delivery so you can watch one arrive before you wire anything real to it. The payload says "test": true and names nothing that exists.

The events

Event When it fires
quote.sent A quote goes out to a client
quote.accepted A client accepts, in the portal or by hand
quote.declined A client declines
order.created An order is raised — by hand, from a quote, or imported
order.confirmed An order leaves draft and stock is reserved
order.shipped An order ships in full (partial shipments don't fire this)
order.completed An order finishes — shipped and settled, or closed short
invoice.sent An invoice is issued
invoice.paid An invoice reaches a zero balance, however it got there
payment.received A payment is recorded, online or by hand
payment.refunded A refund is issued
client.created A client is added
client.updated A client changes — previous carries the fields as they were
item.updated A catalog item changes, with the same previous block
stock.low An item's available quantity falls to its reorder point
production.completed A run finishes and its output goes into stock

stock.low fires on the crossing, not on every sale while it stays low — otherwise a busy afternoon would place one purchase order per unit sold.

What arrives

A POST with a JSON body:

{
  "id": "evt_01J8…",
  "type": "invoice.paid",
  "createdAt": "2026-09-07T12:00:00.000Z",
  "data": { "id": "inv_…", "number": "INV-000142", "total": "1250.00", "balance": "0.00" }
}

data is the same shape GET /api/v1/invoices/{id} returns, so a receiver that already reads the API needs no second parser.

Headers on every delivery:

Header What it is
X-OrderDen-Signature t=<unix seconds>,v1=<hmac-sha256>
X-OrderDen-Event The event name
X-OrderDen-Event-Id The event's id — the same across every endpoint it went to
X-OrderDen-Delivery This attempt's own id

Checking the signature

Compute HMAC-SHA256(secret, "<t>.<raw body>") and compare it to v1. Use the raw body, before any JSON parsing — re-serialising changes the bytes and the signature will not match.

import { createHmac, timingSafeEqual } from "node:crypto";

function verify(secret, rawBody, header) {
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=", 2)));
  const age = Math.abs(Math.floor(Date.now() / 1000) - Number(parts.t));
  if (!Number.isFinite(Number(parts.t)) || age > 300) return false;   // stale, or a replay
  const expected = createHmac("sha256", secret).update(`${parts.t}.${rawBody}`).digest("hex");
  if (expected.length !== parts.v1.length) return false;
  return timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}

The timestamp is inside the signed string, so an old delivery cannot be re-dated and replayed at you: changing t invalidates the signature.

Answering

Return any 2xx as soon as you have the delivery. If you need to do work, answer first and work afterwards — you get ten seconds, which is generous for "got it" and mean for "let me think".

A redirect is not a success. We do not follow one, because following it would post signed business data to whatever the Location header names.

When it fails

Anything that is not a 2xx, and every timeout, is retried after 1 minute, 5 minutes, 30 minutes, 2 hours and 12 hours. After that the delivery is marked failed and left in the log.

Twenty-five consecutive failures switches the endpoint off and tells the owner. Fix your receiver, then Turn on — that clears the failure count too.

The delivery log

Each endpoint keeps 30 days of attempts: the event, when it went, which attempt it was, the status code that came back and the first 500 characters of the response. Redeliver sends a settled one again as a fresh attempt; the original stays in the log, because the history of what went wrong is the reason the log exists.

The same event twice

Retries mean a receiver can see the same event more than once — a delivery that timed out after your server had already handled it, for instance. Key on X-OrderDen-Event-Id, which is the same for every copy of one event, and ignore an id you have seen.

No public URL? Poll instead

A spreadsheet script or a till behind a shop router cannot hold a public URL. Every list endpoint takes ?updatedSince=<ISO timestamp> and returns rows changed at or since that instant:

GET /api/v1/orders?updatedSince=2026-09-07T12:00:00Z&sort=updated:asc

Store the newest updatedAt you saw and send it back next time. The filter is inclusive, so you will see that one row again — a duplicate you can spot, rather than missing a row written in the same millisecond, which you cannot.

Retrying a write safely

A write that times out leaves you unable to tell whether it happened. Send an Idempotency-Key header — any unique string — and retry with the same key: the first response comes back, byte for byte, and nothing is created twice. Keys are remembered for 24 hours.

Reusing a key for a genuinely different request is refused rather than replayed: that is a bug in the caller, and answering it with the first request's resource would hide it.

Everything on this page is in the free tier — one person, the whole product, no card.

Start free