Webhooks API
Webhooks push events from YumKiosk to your own HTTP endpoints in real time. Whenever an order is fired, a payment is captured, or a kiosk goes offline, YumKiosk POSTs a small JSON payload to every matching subscription. This page covers how to register subscriptions, how signature verification works, and the full event catalog.
Creating a subscription
You have two paths:
- Owner panel — navigate to Developers → Webhooks → New webhook. Pick the URL and which events to subscribe to, then save. The panel shows the signing secret once; copy it into your receiver's environment.
- API —
POST /api/v1/webhookswith a JSON body. This is the one place in the API where the signing secret is returned, and only on the initial create call.
curl -X POST https://admin.yumkiosk.com/api/v1/webhooks \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/yumkiosk/webhook",
"events": ["order.created", "payment.captured"]
}'
Response:
{
"data": {
"id": 17,
"url": "https://example.com/yumkiosk/webhook",
"events": ["order.created", "payment.captured"],
"active": true,
"failure_count": 0,
"last_delivered_at": null,
"last_failed_at": null,
"created_at": "2026-04-10T15:00:00+00:00"
},
"secret": "a1b2c3...64 hex chars..."
}
Keep the secret safe — it is shown once. If you lose it, delete the subscription and create a new one.
Delivery format
When an event fires, YumKiosk immediately POSTs a JSON body to your URL with these headers:
POST /yumkiosk/webhook HTTP/1.1
Content-Type: application/json
X-YumKiosk-Event: order.created
X-YumKiosk-Signature: 4f2a9e8c...hex...
X-YumKiosk-Delivery: 4821
User-Agent: YumKiosk-Webhook/1.0
The body is a compact envelope:
{
"event": "order.created",
"data": { /* event-specific payload */ },
"timestamp": "2026-04-10T14:22:05+00:00"
}
We expect a 2xx response within 5 seconds. Anything else — non-2xx, timeout, connection error — counts as a failure. After 10 consecutive failures, YumKiosk auto-pauses the subscription so we are not hammering a dead endpoint. Re-enable it from the owner panel once you have fixed the receiver.
Signature verification
Every delivery is signed with HMAC-SHA256 using the raw body and your secret. To verify, recompute the signature on your end and compare constant-time.
Node.js
const crypto = require('crypto');
function verify(rawBody, headerSignature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(headerSignature)
);
}
Python
import hmac, hashlib
def verify(raw_body: bytes, header_signature: str, secret: str) -> bool:
expected = hmac.new(
secret.encode(),
raw_body,
hashlib.sha256,
).hexdigest()
return hmac.compare_digest(expected, header_signature)
PHP
function verify(string $rawBody, string $headerSignature, string $secret): bool
{
$expected = hash_hmac('sha256', $rawBody, $secret);
return hash_equals($expected, $headerSignature);
}
Never compute the signature over the parsed JSON — always use the exact raw bytes of the request body. Any reserialisation will break verification.
Event catalog
| Event | Fires when |
|---|---|
order.created |
An order is fired to the kitchen (payment captured) |
order.status_changed |
An order transitions lifecycle (preparing, ready, completed) |
payment.captured |
Payment is successfully captured on an order |
session.started |
A customer taps Tap to Order on a kiosk |
session.accepted |
An agent accepts an incoming session |
session.ended |
A session ends (normally or cancelled) |
kiosk.online |
A kiosk transitions from offline to online |
kiosk.offline |
A kiosk stops sending heartbeats (>90s silence) |
inventory.low |
A tracked menu item crosses its low-stock threshold |
order.created payload
{
"order_id": 501,
"order_number": "YK-ABC123",
"total": 26.64,
"items_count": 2,
"items": [ { "name": "Double cheeseburger", "quantity": 2, "options": [3] } ],
"agent_name": "Alice",
"kiosk_name": "Front counter",
"status": "confirmed",
"created_at": 1744291124
}
session.ended payload
{
"session_id": 9912,
"agent_id": 42,
"duration_seconds": 389,
"cost_cents": 108,
"cost_dollars": "1.08",
"ended_at": "2026-04-10T14:22:30+00:00"
}
kiosk.offline payload
{
"kiosk_id": 5,
"kiosk_name": "Front counter",
"location": "Main dining room",
"offline_at": "2026-04-10T15:01:12+00:00"
}
Managing subscriptions
GET /api/v1/webhooks— list all subscriptions for your owner.POST /api/v1/webhooks— create a new one (returns the secret once).DELETE /api/v1/webhooks/{id}— revoke a subscription permanently.
The owner panel at Developers → Webhooks is the recommended place to manage subscriptions interactively, but the API is there for infrastructure-as-code workflows.
Testing
Every subscription has a "Test" action in the owner panel that POSTs a synthetic webhook.test payload to your URL. Use it to confirm your receiver is wired up before your first real event fires.
For local development, expose your dev server with a tunnel tool like ngrok or cloudflared and point a sandbox subscription at the tunnel URL.