Files
kooltab-backoffice/docs/api.md
T
brammie15 8b1e38cb4d feat: reminders, Excel customer import and security hardening
Features
- Payment reminders via Resend (candidates, send, history, test email)
- Bulk import of the customer list from Excel/CSV in the dashboard
- Dashboard i18n (en/nl) with language selector

Security
- Enforce venue isolation on all /venues/:venueId routes
- Require admin for planned-tab changes and pairing codes
- Rate-limit login and device registration; opt-in TRUST_PROXY
- Require JWT_SECRET in production; stop leaking internal errors
- Escape customer names in reminder emails

Sync and commands
- Remove tabs the terminal no longer reports; validate nested sync payload
- Return all pending commands on every sync (fixes missed-command race)
- First command ack wins; atomic registration code claim
- Reject commands for revoked devices, stale versions or duplicates
- Remind only unpaid balances, with a 7-day cooldown

Dashboard
- Share a single token refresh across concurrent 401s (random logouts)
- Keep the session alive for the refresh token lifetime

Deployment
- Replace `db push --accept-data-loss` with migrations (baselines legacy DBs)
- Set NODE_ENV=production; pass Resend and TRUST_PROXY env in prod compose
2026-10-07 23:18:37 +02:00

3.1 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.

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