Developer Docs

API Reference

Full reference for the MicroQ REST API. All endpoints speak JSON and support multi-tenant scoping out of the box.

Overview

MicroQ is a real-time queue management microservice built on Flask and MongoDB. It exposes a clean REST API designed to function as a queue backend for any product — from customer support flows to appointment systems.

The service is built with SaaS multi-tenancy in mind: every operation is scoped by tenant_id and queue_id, allowing a single deployment to power many isolated queues across many customers.

Base URL

Base URL
http://localhost:5000

Request Format

All POST request bodies must be application/json. Set the Content-Type header accordingly. GET endpoints use query string parameters.

Response Format

All responses are JSON. Successful responses use 2xx status codes. Errors return a consistent shape with an error field.

Quick Start

Up and running in three commands:

shell
# 1. Create a queue for your tenant
curl -X POST http://localhost:5000/api/acme/queues \
  -H "Content-Type: application/json" \
  -d '{"queue_id": "support", "name": "Support Queue"}'
# → Response includes admin_key — save it!

# 2. Join the queue
curl -X POST http://localhost:5000/api/acme/support/join \
  -H "Content-Type: application/json" \
  -d '{"name": "Alex Rivera"}'

# 3. Check your live position
curl "http://localhost:5000/api/acme/support/status/{user_id}"

# 4. Admin: serve the next person (requires admin key)
curl -X POST http://localhost:5000/api/acme/support/serve-next \
  -H "Content-Type: application/json" \
  -H "X-Admin-Key: YOUR_ADMIN_KEY" -d '{}'

# 5. Get a QR code for the public queue page
# Open in browser: http://localhost:5000/api/acme/support/qr

Authentication

Admin operations (e.g. serve-next) are protected by a per-queue admin key. The key is generated automatically when a queue is created and returned in the response body.

Header
X-Admin-Key: <admin_key_from_queue_creation>

Include this header on every POST /serve-next request. Omitting or sending an incorrect key returns 403 Forbidden.

Full JWT / API-key authentication is on the roadmap. The per-queue admin key provides basic operational security until then.

POST /api/<tenant_id>/<queue_id>/join

Adds a new entry to the queue. Returns the created entry, its initial position, and ETA in minutes.

POST /api/<tenant_id>/<queue_id>/join

Request Body

FieldTypeRequiredDefaultDescription
namestringrequiredDisplay name of the user joining.
service_type_idstringoptionalnullUUID of the service type (from the Service Types API).
tenant_idstringoptionalconfig defaultTenant scope.
queue_idstringoptionalconfig defaultQueue scope.
Request
{
  "name": "Alex Rivera",
  "service_type_id": "a1b2c3d4-...",
  "tenant_id": "acme",
  "queue_id": "support"
}
Response · 201
{
  "entry": {
    "id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
    "name": "Alex Rivera",
    "joined_at": "2026-03-25T10:30:00+00:00",
    "service_type_id": "a1b2c3d4-...",
    "service_type_name": "General Inquiry",
    "service_type_code": "GEN",
    "tenant_id": "acme",
    "queue_id": "support",
    "status": "waiting"
  },
  "position": 3,
  "eta_minutes": 12.0
}

GET /api/<tenant_id>/<queue_id>/status/{user_id}

Returns the current queue status for a specific user. Position and ETA are recalculated live on every call.

GET /api/<tenant_id>/<queue_id>/status/{user_id}

Path Parameters

FieldTypeDescription
tenant_idstringTenant scope identifier.
queue_idstringQueue scope identifier.
user_idstring (UUID)UUID returned on join.
Response · 200
{
  "entry": {
    "id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
    "name": "Alex Rivera",
    "joined_at": "2026-03-25T10:30:00+00:00",
    "service_type_id": "a1b2c3d4-...",
    "service_type_name": "General Inquiry",
    "service_type_code": "GEN",
    "tenant_id": "acme",
    "queue_id": "support",
    "status": "waiting"
  },
  "position": 2,
  "eta_minutes": 8.0
}

POST /api/<tenant_id>/<queue_id>/boost

This endpoint has been retired and returns 410 Gone. Queue ordering is now strictly FIFO (first-in, first-out), with optional service types.

POST /api/<tenant_id>/<queue_id>/serve-next

Marks the next waiting user (FIFO order) as served and returns their record. Requires the X-Admin-Key header matching the queue's admin key.

POST /api/<tenant_id>/<queue_id>/serve-next

Headers

HeaderRequiredDescription
X-Admin-KeyrequiredAdmin key returned when the queue was created.
Response · 200 (entry served)
{
  "served": {
    "id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
    "name": "Alex Rivera",
    "service_type_id": "a1b2c3d4-...",
    "service_type_name": "General Inquiry",
    "service_type_code": "GEN",
    "status": "served",
    "served_at": "2026-03-25T10:45:00+00:00",
    "tenant_id": "acme",
    "queue_id": "support"
  }
}
Response · 200 (queue empty)
{
  "message": "queue is empty"
}

GET /api/<tenant_id>/<queue_id>/list

Returns all waiting entries in effective queue order, with live positions and ETAs.

GET /api/<tenant_id>/<queue_id>/list

Path Parameters

FieldTypeDescription
tenant_idstringTenant scope identifier.
queue_idstringQueue scope identifier.
Response · 200
{
  "count": 2,
  "queue": [
    {
      "id": "b2c3d4e5-...",
      "name": "Priya",
      "position": 1,
      "service_type_name": "General Inquiry",
      "service_type_code": "GEN",
      "eta_minutes": 4.0,
      "status": "waiting",
      "joined_at": "2026-03-25T10:25:00+00:00"
    },
    {
      "id": "f47ac10b-...",
      "name": "Alex Rivera",
      "position": 2,
      "service_type_name": "Returns",
      "service_type_code": "RET",
      "eta_minutes": 8.0,
      "status": "waiting",
      "joined_at": "2026-03-25T10:30:00+00:00"
    }
  ]
}

GET /api/<tenant_id>/<queue_id>/qr

Returns an SVG QR code that encodes the public queue link (/queue/:tenant/:queue). Use it in print materials, kiosk displays, or embed in a page with an <img> tag.

GET /api/<tenant_id>/<queue_id>/qr

Response

Content-Type: image/svg+xml. The image can be embedded directly:

HTML
<img src="/api/acme/support/qr" alt="Queue QR Code">

POST /api/<tenant_id>/queues

Creates a new queue configuration for a tenant. If the queue already exists, the existing config is preserved (idempotent).

POST /api/<tenant_id>/queues

Request Body

FieldTypeRequiredDefaultDescription
queue_idstringrequiredUnique identifier for the queue.
namestringrequiredDisplay name of the queue.
is_publicbooleanoptionaltrueWhether the queue is publicly joinable.
avg_service_timenumberoptional4Average minutes per service (for ETA).
Request
POST /api/acme/queues

{
  "queue_id": "support",
  "name": "Support Queue",
  "is_public": true,
  "avg_service_time": 5
}
Response · 201
{
  "tenant_id": "acme",
  "queue_id": "support",
  "name": "Support Queue",
  "is_public": true,
  "avg_service_time": 5.0,
  "admin_key": "a1b2c3d4...",
  "created_at": "2026-03-25T10:00:00+00:00"
}

GET /api/<tenant_id>/queues

Lists all queue configurations for a given tenant.

GET /api/<tenant_id>/queues
Response · 200
{
  "count": 2,
  "queues": [
    { "queue_id": "support", "name": "Support Queue", ... },
    { "queue_id": "billing", "name": "Billing Queue", ... }
  ]
}

GET /api/<tenant_id>/queues/<queue_id>

Returns the configuration for a specific queue.

GET /api/<tenant_id>/queues/<queue_id>
Response · 200
{
  "tenant_id": "acme",
  "queue_id": "support",
  "name": "Support Queue",
  "is_public": true,
  "avg_service_time": 5.0,
  "created_at": "2026-03-25T10:00:00+00:00"
}

Queue Algorithm

The queue uses a strict FIFO (first-in, first-out) ordering. Entries are sorted by their joined_at timestamp — the earliest joiner is served first.

Admins can define Service Types (e.g. "General Inquiry", "Returns") via the admin panel or API. When a customer joins, they can optionally select a service type, which is displayed in the queue list and kiosk view.

ETA Formula

Formula
eta_minutes = position × avg_service_time

avg_service_time is determined automatically: once a queue has 3 or more served entries, the system computes a rolling average from the last 20 actual service durations (served_at − joined_at). Until then, it falls back to the per-queue config value (default 4 minutes).

Multi-Tenant

All endpoints are scoped by two required path parameters:

  • tenant_id — identifies the organisation or customer account
  • queue_id — identifies a specific queue within that tenant

This structure is enforced in the URL itself: /api/<tenant_id>/<queue_id>/... — there is no way to accidentally leak data across tenants or queues.

shell — scoped request examples
# Join the support queue for tenant 'acme'
curl -X POST http://localhost:5000/api/acme/support/join \
  -H "Content-Type: application/json" \
  -d '{"name": "Sam"}'

# List the billing queue for tenant 'acme'
curl "http://localhost:5000/api/acme/billing/list"

# Public queue page (browser)
http://localhost:5000/queue/acme/support

Queue Configuration

Each queue can have its own avg_service_time. Create queues via the Queue Management API. If a queue entry references a queue that hasn't been explicitly created, the system uses global defaults from the environment config.

Tenant isolation is enforced at the URL-routing and data-query levels. For strict security, add a middleware layer that validates the JWT's tenant_id claim against the URL parameter before it reaches the service layer.

Event Log

Every state-changing operation emits an event to the queue_events collection. Events are stored with tenant and queue scope and include a timestamp, event type, user ID, and an optional metadata object.

Event TypeTriggerMeta Fields
JOINUser joins the queuename
BOOSTUser's priority is boostedboost_value
SERVEUser is served (dequeued)name

Events power the rolling ETA system: when 3+ entries have been served in a queue, the ETA formula switches from the static avg_service_time to a rolling average computed from actual served_at − joined_at durations of the last 20 served entries.

Configuration

Settings are loaded from environment variables via a .env file. Copy .env.example to .env and adjust before running.

VariableDefaultDescription
FLASK_ENVdevelopmentSet to production in live deployments.
SECRET_KEYchange-meFlask session signing key. Must change before deploying.
MONGO_URImongodb://localhost:27017MongoDB connection string.
MONGO_DB_NAMEsmart_queueMongoDB database name.
DEFAULT_TENANT_IDdefault_tenantFallback tenant when not specified.
DEFAULT_QUEUE_IDmainFallback queue when not specified.
AVG_SERVICE_TIME_MINUTES4Base value for ETA calculation.
WAITING_TIME_WEIGHT0.05Score multiplier for minutes waiting. Higher = faster natural escalation.

Error Reference

All error responses share the same shape:

Error shape
{ "error": "human-readable description" }
StatusWhen it occurs
400 Bad RequestA required field is missing or has an invalid value.
403 ForbiddenMissing or invalid X-Admin-Key header on a protected endpoint.
404 Not FoundThe requested user, queue, or resource does not exist.
500 Internal Server ErrorUnexpected server error — check application logs.