Files
kooltab-backoffice/docs/api.md
T
brammie15 874f68b4b1
CI / build-and-test (push) Canceled after 0s
feat: add feature
2026-10-19 23:23:36 +02:00

4.2 KiB

API Documentation

Base URL: http://localhost:3001/api/v1

Swagger UI: http://localhost:3001/api/docs

Authentication

Dashboard Users

  • Login to receive JWT access token (24h) + refresh token (30 days)
  • Include access token in Authorization: Bearer <token> header

Devices

  • Register to receive device bearer token (90 days)
  • Include device token in Authorization: Bearer <token> header
  • Device ID must match URL parameter

Endpoints

Health

GET /health - Server health check (503 when the database is unreachable)

Auth

POST /auth/login - Login with email/password POST /auth/refresh - Refresh access token POST /auth/logout - Logout and revoke refresh token

Venues

GET /venues - List venues GET /venues/:venueId - Get venue details GET /venues/:venueId/devices - List devices in venue

Devices

POST /devices/register - Register a new device POST /devices/:deviceId/sync - Sync device state (device auth) POST /devices/:deviceId/commands/:commandId/ack - Acknowledge command (device auth) GET /devices/generation-code - Generate registration code (admin)

Sync semantics:

  • openTabs must be the device's complete list of open tabs. Tabs it no longer reports (e.g. deleted on the terminal) are removed from the dashboard. Settling a tab on the terminal keeps the tab open under the same name with a zero balance, so the name stays listed.

  • Every sync returns all pending commands for the device; cursor is still returned but no longer filters. Devices dedupe by command id and re-send the ack for commands they already processed.

  • The first ack for a command wins; later acks are accepted as no-ops.

  • plannedTabNames only contains planned customers the device has not opened a tab for yet. Once a sync reports an open tab with that name (ignoring case and extra spaces), the customer is marked delivered and no longer sent. The customer (and their email, used for reminders) is kept.

  • events (optional) reports what happened on tabs since the last sync, for sales statistics. Each event has a terminal-generated UUID id, a type, tabId, customerName and occurredAt (ISO-8601, UTC):

    • line_added / line_removed: productName, unitPriceInCents, quantity (always positive)
    • settled: amountInCents, paymentMethod (cash, payconiq, payment_pending or other), optional commandId (the remote close that caused it) and items (the settled lines)

    The response lists acknowledgedEventIds (stored, including events already stored by an earlier sync) and rejectedEvents ([{ id, reason }], invalid and never stored). The terminal removes both from its outbox. An invalid event never fails the sync. At most 1000 events per sync.

Tabs

GET /venues/:venueId/tabs - List open tabs

Planned Tabs (customer list)

GET /venues/:venueId/planned-tabs - List planned customers POST /venues/:venueId/planned-tabs - Add a customer (admin) POST /venues/:venueId/planned-tabs/import - Bulk import { entries: [{ customerName, email? }], mode: 'merge' | 'replace' } (admin). Names match case-insensitively and keep their existing spelling; replace also removes customers missing from the import. PUT /venues/:venueId/planned-tabs/:id - Rename a customer / set email (admin) DELETE /venues/:venueId/planned-tabs/:id - Remove a customer (admin)

Reminders

GET /venues/:venueId/reminders/candidates - Customers with an unpaid balance and email, not reminded in the last 7 days POST /venues/:venueId/reminders/send - Send payment reminders GET /venues/:venueId/reminders - Reminder history POST /venues/:venueId/reminders/test/:plannedTabId - Send a test email (admin)

Commands

POST /venues/:venueId/commands - Create a command GET /venues/:venueId/commands - List command history POST /venues/:venueId/commands/devices/:deviceId/revoke - Revoke device (admin)

All /venues/:venueId/... routes return 403 venue_forbidden unless the user belongs to that venue.

Error Format

{
  "error": {
    "code": "error_code",
    "message": "Human-readable message"
  }
}

HTTP Status Codes

  • 200 Success
  • 201 Created
  • 400 Bad Request
  • 401 Unauthorized
  • 403 Forbidden
  • 404 Not Found
  • 409 Conflict
  • 429 Too Many Requests