YumKiosk YumKiosk Docs
Website Agent login Owner panel
API Reference

Webhooks API

Subscribe to YumKiosk events and verify incoming signatures.

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:

  1. 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.
  2. APIPOST /api/v1/webhooks with 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.