A small REST API to mint Checks, read dishes and analytics, and receive events. Everything is tenant-scoped to your API key. Base URL: https://wordofdish.com/api/v1
Create a key in Integrations. Keys are shown once. Send it as a bearer token:
Authorization: Bearer wod_live_xxxxxxxxxxxxxxxxxxxxxxxxread (GET) and write (POST). A key carries one or both.429. Retry with backoff.| Method | Path | Scope |
|---|---|---|
| GET | /api/v1/dishes?storeId={id}List a store's dishes (use the ids when minting Checks). | read |
| GET | /api/v1/checks?storeId={id}List a store's Checks, newest first. | read |
| POST | /api/v1/checksMint a Check. Consumes one credit; 402 when out of credits. | write |
| GET | /api/v1/checks/{id}Fetch a single Check by id. | read |
| GET | /api/v1/analyticsAggregate analytics for the tenant. | read |
curl -X POST https://wordofdish.com/api/v1/checks \
-H "Authorization: Bearer wod_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"storeId": "store_123",
"items": [{ "dishId": "dish_abc", "qty": 1 }],
"locale": "en"
}'Returns 201 with the diner URL:
{
"data": {
"id": "chk_...",
"shortId": "8bSilTS0",
"status": "minted",
"url": "https://wordofdish.com/r/8bSilTS0"
}
}Errors are JSON: { "error": "code" }.
401 — missing or invalid bearer key.403 — the key lacks the required scope.402 credit_gate_blocked — out of credits; top up to mint.400 / 404 — bad input / not found (or another tenant's resource).429 — rate limited.Add an endpoint in Integrations and pick events. We POST JSON{ id, event, data } and retry up to 3× on non-2xx.
check.created | A Check was minted (owner app or API). |
check.submitted | A diner completed the survey for a Check. |
feedback.positive | A submission scored positive (happy path). |
feedback.negative | A submission scored negative (routed to recovery). |
recovery.updated | A recovery case was created, replied to, or resolved. |
HMAC-SHA256 of the raw request body under your endpoint secret (whsec_…), hex-encoded. Compare in constant time.
import crypto from "node:crypto";
// Express example. Use the RAW body, not the parsed JSON.
app.post("/webhooks/wod", express.raw({ type: "application/json" }), (req, res) => {
const sig = req.header("X-WoD-Signature") || ""; // "sha256=<hex>"
const expected = "sha256=" + crypto
.createHmac("sha256", process.env.WOD_WEBHOOK_SECRET) // "whsec_..."
.update(req.body) // Buffer of the raw body
.digest("hex");
const ok = sig.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected));
if (!ok) return res.status(401).end();
const { event, data } = JSON.parse(req.body.toString());
// handle event…
res.status(200).end();
});Python:
import hmac, hashlib
def verify(raw_body: bytes, header_sig: str, secret: str) -> bool:
expected = "sha256=" + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, header_sig)Prefer no code? Use our connectors for Zapier, Make, and n8n — authenticate with an API key, trigger on webhook events, and mint Checks as an action.
Connector source + setup guides live in the connectors/ directory of the repo.