For the complete documentation index, see llms.txt. This page is also available as Markdown.

POS Integration Guide

Managing Devices with MQTT Credentials

How an integrated application uses its QwantiFi MQTT credentials to talk to one device or many devices once those credentials have been distributed.

This guide is client-agnostic: it describes the MQTT concepts and message flow with pseudocode, so it applies to any MQTT client library (mqtt.js, MQTTnet, Paho, Eclipse Mosquitto clients, etc.).

Prerequisite: you already hold your integrated application credentials.

Credentials are provisioned for you. They're issued by MagTek and handed to you during onboarding. There is no self-service onboarding endpoint. What you receive is an mqttConfig object plus a one-time refresh token; from there everything you need is self-service (see Token lifecycle and reconnection, below). If you have not received credentials, or need them re-issued, contact whoever onboarded you. For the model behind these credentials, see the QwantiFi Credential Model.

The Big Picture

A single integrated application credential lets one integrated application instance command every device it owns — there is no per-device credential to manage on the integrated application side.

  • A device has exactly one subscribe topic and one publish topic. It listens only for commands addressed to its own serial number and has no routing logic of its own.

  • A POS holds lists of publish and subscribe topics covering all the devices under its scope (a whole reseller, or a single merchant). It addresses each device by publishing to that device's topic and listens for each device's responses on a separate topic.

Commands and responses travel on different topic branches so a device never re-receives its own output and the integrated application is not fighting echo/recursion.

The Credential Payload

The mqttConfig object you were given at onboarding carries everything you need:

Field
Meaning

broker

Broker host to connect to. Environment-specific — always take it from this field rather than hard-coding it.

port

Broker port (1883 plain, 8883 TLS are the usual choices). Environment-specific, like broker.

peerCommonName

Expected TLS certificate common name (when using TLS). May be empty.

username

MQTT username — present in the CONNECT username field. Shared by all integrated application terminals in the same scope.

password

A short-lived HS256 JWT — present in the CONNECT password field. Used for authentication (signature + expiry); the authorization rules live broker-side, not in the token.

publishTopics

List of explicit per-device command topics — one …/{sn}/SendCommand for every device under your scope. Informational (your real grant is a broker-side scope filter). Empty when no devices are onboarded. No wildcard entry: MQTT forbids wildcards in a PUBLISH topic.

subscribeTopics

List of per-device response topics — for each device, its …/device/publish/{sn}/MMSMessage and …/device/publish/{sn}/Status — followed, last, by the two scope filters …/device/publish/+/MMSMessage and …/+/Status. Subscribe to that trailing pair to cover every device in your scope at once.

operatingMode

Transport hint (mqtts). Treat port and your TLS settings as authoritative — see the transport note below.

expiresAt

UTC instant the JWT stops being valid. Refresh before this.

Example of a merchant-scoped credential (a reseller-wide credential looks the same but its username is pos-{reseller} and its topics span every merchant under that reseller). The scope is fixed at issue time — the integrated application does not select it:

Merchant-scoped Credential Example (JSON)

{Two ways to read subscribeTopics. The leading entries are explicit, per-device convenience topics — one pair per device provisioned to your scope right now, growing and shrinking as devices come and go. The last two entries are the scope filters: subscribe to those instead and you receive from every device in your scope in two SUBSCRIBEs, including devices onboarded after this call. Pick per-device when each device needs its own handler; pick the filters when you would otherwise be managing a subscription list. publishTopics has no such pair — MQTT forbids wildcards in a PUBLISH topic, so commands always go out one device at a time.

The JWT is the password. Do not also send a separate password — the broker validates the JWT signature to authenticate you, then applies your authorization rules from its broker-side store, matched by your username.

Transport: trust port, not operatingMode. operatingMode is a coarse hint and is currently reported as mqtts regardless of environment. Decide TLS from the port you were issued and from what your onboarding contact told you the environment uses; if peerCommonName is populated, verify the server certificate against it.

  • Publish grant (what you may use): MagTek/13204/2026213/+/SendCommand

  • Actual command you publish (one device): MagTek/13204/2026213/TEST-001/SendCommand

  • Subscribe grant (what you may use): MagTek/13204/2026213/device/publish/+/MMSMessage and …/+/Status

  • Actual response topics (one device): MagTek/13204/2026213/device/publish/TEST-001/MMSMessage and …/TEST-001/Status

The grant is leaf-specific: publishing is allowed only on the SendCommand leaf, and subscribing only on the MMSMessage and Status leaves — a broad …/{sn}/# will not match the grant and is denied. {% endhint %}

Connecting to the Broker

Open a standard MQTT connection using the credential fields:

ClientId Must Be Unique Per Connection

Multiple POS terminals (lanes, stations) for the same merchant connect with the same username. The broker requires a unique clientId per live connection — if two clients use the same clientId, the broker disconnects the earlier one. Always append a per-terminal/per-process suffix:

The username is a safe shared identity; the clientId is what must be unique.

Understanding the Topic Model

All topics follow the hierarchy:

Two distinct branches are used:

Direction
Topic shape
Who publishes
Who subscribes

Command (to device)

MagTek/{r}/{c}/{sn}/SendCommand

POS (and backend)

the device

Response (from device)

MagTek/{r}/{c}/device/publish/{sn}/MMSMessage and …/{sn}/Status

the device

POS (and dashboards)

  • The device is authorized for its whole subtree MagTek/{r}/{c}/{sn}/# and listens on the concrete …/{sn}/SendCommand leaf — the command topic your POS credential is authorized to publish to.

  • The device publishes its output to the separate …/device/publish/{sn}/… branch — never back into its own command subtree — which is why command and response do not loop. It emits two event kinds: MMSMessage (transaction/message data) and Status.

Managing a Single Device

To command one device with serial TEST-001 (reseller 13204, customer 2026213):

  1. Subscribe for its responses (do this before sending, so you don't miss the reply). A device emits two event kinds, so subscribe to both leaves:

  1. Publish a command to that device's command topic:

  1. Handle the response on your message callback:

Payload framing: the SendCommand leaf and the exact binary framing of payload are defined by the device protocol, not by the QwantiFi platform. Use the device's command spec — see the DynaFlex Programmer's Manual to build/parse the bytes. Treat the payload as opaque at the MQTT layer.

Managing Many Devices

You have two equivalent strategies — choose per your routing needs.

Wildcard subscriptions (simplest)

Subscribe once per event leaf to the scope wildcard and receive every device's responses on a single handler:

You do not have to build those strings yourself — they are the last two entries of mqttConfig.subscribeTopics, already correct for your scope:

This is the recommended form for anything beyond a handful of devices: two subscriptions total, and devices onboarded later are picked up automatically with no resubscribe. Extract the device serial from the topic to know who replied:

To send, publish to each device's command topic:

Per-device Subscriptions (Explicit)

Subscribe to each device's two response leaves individually — these all fall under your authorized scope wildcard, so the broker permits them:

Useful when different devices feed different handlers, or when you want the subscription set to mirror exactly the devices you manage.

Discovering Your Device Serials

Your credential's topic lists enumerate the devices provisioned to your scope at the moment it was issued — a convenient starting address book. Because authorization is a scope wildcard, you are not limited to that snapshot: devices added after you connected are reachable immediately by addressing the new serial (no new credential needed).

The cleanest way to enumerate them at runtime is POST /api/pos-device-list , which returns a roster — one entry per device, each already pairing the serial with its topics — so you never parse serials out of topic paths. Alternatives: your own provisioning records, or re-fetching the flat lists via POST /api/pos-credentials/refresh or POST /api/pos-topics-lis.

Checking Whether a Device is Reachable

Before sending to a device you have not heard from in a while, you can ask whether it currently holds a broker session, rather than publishing and waiting on silence — use POST /api/pos-device-status/{deviceSn}. It reports connected, status (PENDING/PROVISIONED), and blocked.

connected: false is not proof the device is off. It is also what you get when the broker's management API is unreachable from the platform. Treat it as "not known to be connected" — good for surfacing "device may be offline" to an operator, not as a hard gate that refuses to send.

Correlating Responses to Requests

MQTT is asynchronous — a response arrives as a separate message. To match a reply to the command and device that produced it:

  • By device: the response topic contains the device serial, so the topic itself tells you which device replied and which event kind it is.

  • By command: if you issue several commands to the same device, include a correlation token in your command payload (per the device protocol) and echo it in the response, or serialize commands per device.

Keep a per-device in-flight map keyed by serial (and correlation token if available) so concurrent operations across many devices do not get cross-wired.

Token Lifecycle and Reconnection

The JWT in password is short-lived — 8 hours by default, though configurable per environment, so always drive your timer off expiresAt rather than a hard-coded 8. You were also handed a long-lived, opaque refresh token once, at onboarding (store it securely) — it is what lets the integrated application mint fresh JWTs on its own, with no portal involvement.

  • Refresh ahead of expiry. Before expiresAt, mint a fresh JWT (same scope, same username) with POST /api/pos-credentials/refresh =, passing your username + refresh token. A good practice is to refresh at ~75–80% of the lifetime. This re-mints the JWT without rotating the refresh token.

  • Don't let the JWT lapse — the lookup endpoints require a live one. pos-topics-list, pos-device-list, and pos-device-status reject an expired JWT with a 401. Only pos-credentials/refresh still accepts one, precisely so an expired integrated application can recover.

  • Reconnect with the new token. The broker checks the JWT at CONNECT time. An existing connection is not retroactively killed at the instant of expiry, but any reconnect requires a still-valid token.

  • Re-subscribe after reconnect unless you use a persistent session (see below).

For the full credential model behind refresh, see the QwantiFi Credential Model.

Operational Notes

  • Clean vs persistent session. A clean session means you must re-subscribe on every reconnect. A persistent session (clean=false with a stable clientId) lets the broker queue messages and retain subscriptions across brief disconnects. The clientId still has to be unique across concurrently live terminals.

  • Multiple terminals. Same username, different clientId each. They share the identity, JWT, refresh token, and authorization scope, so each terminal is authorized for every device under the scope. Partitioning devices across lanes is a client-side concern.

  • Payload framing is device-defined. The MQTT platform moves opaque bytes; the SendCommand leaf and binary layout come from the device command protocol, not this platform.

  • Security. Treat the JWT like a password — never log it. Use TLS wherever the environment offers it. Both the JWT and the refresh token are bearer secrets.

Quick Reference

Scope examples use reseller 13204, customer 2026213, device TEST-001.

Action
Topic

Send command to one device

MagTek/13204/2026213/TEST-001/SendCommand (publish)

Receive one device's responses

MagTek/13204/2026213/device/publish/TEST-001/MMSMessage and …/TEST-001/Status (subscribe)

Receive all merchant devices' responses

MagTek/13204/2026213/device/publish/+/MMSMessage and …/+/Status (subscribe)

Receive all reseller devices' responses

MagTek/13204/+/device/publish/+/MMSMessage and …/+/Status (subscribe)

Command-scope grant (merchant)

MagTek/13204/2026213/+/SendCommand (broker rule, not sendable)

For the complete endpoint surface — request shapes, headers, and status codes — see the API Reference.

Connect checklist

Last updated