Skip to content
Free shipping on orders over $2,000.

Provisioning a device

What a QuickComm device carries in non-volatile storage, the QR payload that puts it there, the self-registration call your server can answer, and the reachability check to run before you flash a fleet.

8 min read

Provisioning is the only moment a device learns who it is and where to send its audio. Get this right and the rest is mechanical.

What the device ends up holding

Everything the device retains across a power cycle is in this table. There is nothing else in non-volatile storage that affects how it behaves.

KeyExampleNotes
mac_addressAA:BB:CC:DD:EE:FFThe burned-in MAC, or one you assign at provisioning
property_id4Required. No default, no guess — the device refuses to run without it
organization_id2Carried for convenience
team_id or table_id7Whichever your placement model uses
user_id19Optional — who is carrying it
comm_modeudpSelects the transport: udp, ai or websocket
server host and port192.168.1.50:8000Your backend
ssid / passwordWi-Fi credentials

The QR provisioning payload

Devices ship with a QR provisioning card. The device boots into SoftAP mode and publishes a captive portal at 192.168.4.1; the installer scans the QR with a phone, the phone opens the URL, and the device parses the query string into non-volatile storage and reboots.

textThe payload, in full
http://192.168.4.1/?mac_address=AA:BB:CC:DD:EE:FF
                    &property_id=4
                    &organization_id=2
                    &team_id=7
                    &user_id=19
                    &comm_mode=websocket        ← udp | ai | websocket
                    &mode=local                 ← where the device reports
                    &local_ip=192.168.1.50
                    &local_port=8000
                    &ssid=RestaurantWiFi
                    &password=...

Assigning identity rather than reading it off a sticker

mac_address does not have to be the burned-in one. A MAC you assign at provisioning travels to the unit inside the QR payload and becomes its operating identity for heartbeats, presence, print claiming and everything else.

This exists so an installer never has to transcribe a MAC off a sticker — one transcription error per table is one silently broken table. If your workflow can accept a provisioned identity, use it; it removes an entire class of field error.

Self-registration from the device

A device can announce itself to your server with no operator credential. This is how a factory-flashed unit joins a fleet without anyone creating a record for it first. Implement it if you want devices to be self-serve; return 404 if you would rather every device be pre-registered in your own system.

httpPOST /api/devices/register — what the device sends
POST /api/devices/register HTTP/1.1
Content-Type: application/json

{
  "mac_address": "AA:BB:CC:DD:EE:FF",
  "property_id": 4,
  "team_id": 7,
  "user_id": 19,
  "ip_address": "192.168.1.88",
  "communication_mode": "udp"
}

Four behaviours worth matching in your own implementation, each learned the hard way:

  • Normalise the MAC to AA:BB:CC:DD:EE:FF. Accept colon, dash and bare 12-character hex on the way in, and rewrite anything already stored in another form.
  • Make the call idempotent. An existing device is updated, not duplicated.
  • Preserve communication_mode if it is omitted. A re-registration that forgot the field used to silently reset udp devices to ai and take them off the air.
  • Validate relationships rather than silently correcting them. A team that does not belong to the property is a 4xx, not a quiet reassignment.

A bearer credential for the device's own calls

httpPOST /api/auth/device/token
POST /api/auth/device/token HTTP/1.1
Content-Type: application/json

{ "mac_address": "AA:BB:CC:DD:EE:FF", "ip_address": "192.168.1.88" }

──────────────────────────────────────────────
{ "access_token": "...", "refresh_token": "...",
  "token_type": "bearer", "expires_at": "..." }

Endpoint reachability — read this before you deploy

The device-facing routes do not all live under one prefix. A reverse proxy configured to forward only /api will publish the fleet plane perfectly and silently swallow every audio, print and notification route.

RouteUnder /api?Transport
POST /api/devices/…, POST /api/auth/…YesHTTPS
POST /voice, POST /voiceechoNoHTTPS
GET /sse/notificationsNoHTTPS
GET /restaurant/print-jobs, POST /restaurant/ordersNoHTTPS
wss://<host>/wsNoWebSocket over TLS
udp-v1 audio ingestNoUDP, default port 12345
bashreachability.sh — run this before you flash a fleet
HOST=https://api.yourcompany.com
MAC=AA:BB:CC:DD:EE:FF

# Fleet plane — must answer 200
curl -s -o /dev/null -w "heartbeat  %{http_code}\n" -X POST $HOST/api/devices/heartbeat \
  -H "Content-Type: application/json" -d "{\"mac_address\":\"$MAC\"}"

# Mode B downlink — 204 is the healthy empty answer; 404 means unpublished
curl -s -o /dev/null -w "notify     %{http_code}\n" $HOST/sse/notifications \
  -H "x-device-id: $MAC"

# Mode C print bridge — 204 is healthy and empty
curl -s -o /dev/null -w "print      %{http_code}\n" $HOST/restaurant/print-jobs \
  -H "x-device-id: $MAC"

# Mode C socket — expect 101 Switching Protocols
curl -s -o /dev/null -w "websocket  %{http_code}\n" \
  -H "Connection: Upgrade" -H "Upgrade: websocket" \
  -H "Sec-WebSocket-Version: 13" -H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" \
  $HOST/ws

# Mode A ingest — UDP is fire-and-forget, so watch the server as you run this
printf 'v1,1,probe01,%s,,4,,,\n0000000000' "$MAC" | nc -u -w1 api.yourcompany.com 12345

Something wrong or missing on this page? Tell us.