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
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:
# 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.
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.
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.
Request Body
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
name | string | required | — | Display name of the user joining. |
service_type_id | string | optional | null | UUID of the service type (from the Service Types API). |
tenant_id | string | optional | config default | Tenant scope. |
queue_id | string | optional | config default | Queue scope. |
{
"name": "Alex Rivera",
"service_type_id": "a1b2c3d4-...",
"tenant_id": "acme",
"queue_id": "support"
}
{
"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.
Path Parameters
| Field | Type | Description |
|---|---|---|
tenant_id | string | Tenant scope identifier. |
queue_id | string | Queue scope identifier. |
user_id | string (UUID) | UUID returned on join. |
{
"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.
Headers
| Header | Required | Description |
|---|---|---|
X-Admin-Key | required | Admin key returned when the queue was created. |
{
"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"
}
}
{
"message": "queue is empty"
}
GET /api/<tenant_id>/<queue_id>/list
Returns all waiting entries in effective queue order, with live positions and ETAs.
Path Parameters
| Field | Type | Description |
|---|---|---|
tenant_id | string | Tenant scope identifier. |
queue_id | string | Queue scope identifier. |
{
"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.
Response
Content-Type: image/svg+xml. The image can be embedded directly:
<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).
Request Body
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
queue_id | string | required | — | Unique identifier for the queue. |
name | string | required | — | Display name of the queue. |
is_public | boolean | optional | true | Whether the queue is publicly joinable. |
avg_service_time | number | optional | 4 | Average minutes per service (for ETA). |
POST /api/acme/queues
{
"queue_id": "support",
"name": "Support Queue",
"is_public": true,
"avg_service_time": 5
}
{
"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.
{
"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.
{
"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
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 accountqueue_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.
# 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_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 Type | Trigger | Meta Fields |
|---|---|---|
JOIN | User joins the queue | name |
BOOST | User's priority is boosted | boost_value |
SERVE | User 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.
| Variable | Default | Description |
|---|---|---|
FLASK_ENV | development | Set to production in live deployments. |
SECRET_KEY | change-me | Flask session signing key. Must change before deploying. |
MONGO_URI | mongodb://localhost:27017 | MongoDB connection string. |
MONGO_DB_NAME | smart_queue | MongoDB database name. |
DEFAULT_TENANT_ID | default_tenant | Fallback tenant when not specified. |
DEFAULT_QUEUE_ID | main | Fallback queue when not specified. |
AVG_SERVICE_TIME_MINUTES | 4 | Base value for ETA calculation. |
WAITING_TIME_WEIGHT | 0.05 | Score multiplier for minutes waiting. Higher = faster natural escalation. |
Error Reference
All error responses share the same shape:
{ "error": "human-readable description" }
| Status | When it occurs |
|---|---|
400 Bad Request | A required field is missing or has an invalid value. |
403 Forbidden | Missing or invalid X-Admin-Key header on a protected endpoint. |
404 Not Found | The requested user, queue, or resource does not exist. |
500 Internal Server Error | Unexpected server error — check application logs. |