All pages
Powered by GitBook
1 of 3

Loading...

Loading...

Loading...

QwantiFi Credential Model

How your integrated application software authenticates to the QwantiFi broker, and how it keeps that access alive on its own without a person logging into a portal.

This is the "why it works this way" companion to the Integration Guide, which covers the topics, the message flow, and the endpoint mechanics. Read this page to understand the credential model you are integrating against; read that one to write the code.

The Big Picture

An integrated application instance talks to devices over the QwantiFi broker. To connect, it needs an MQTT credential. Unlike a physical device β€” which is issued a permanent username/password β€” an integrated application instance is issued a short-lived JSON Web Token (JWT) that it presents as its MQTT password.

Three ideas drive the whole design:

  1. The platform is the credential authority. The broker does not mint, refresh, or revoke tokens β€” it only verifies them on connect. All issuance and refresh logic lives in the platform service.

  2. Authentication and authorization are separate. The JWT answers "who are you?" The broker then answers "what may you do?" from a separate authorization store, looked up by the identity in the token. The token itself carries no permissions.

  3. Identity is a scope, not a single terminal. A credential is keyed to a reseller (and optionally a customer/merchant), not to one physical lane. Every integrated application instance under the same scope shares one identity and one set of permissions.

When your credential is provisioned, a stable username is derived from your scope:

  • Reseller-wide β€” pos-{resellerId}, one identity covering every merchant under a reseller.

  • Customer-scoped β€” pos-{resellerId}-{customerId}, narrowed to a single merchant.

This username is the anchor for everything else: the JWT is issued for it, the broker's permission rules are keyed by it, and an audit record is stored under it. Because the identity is a scope, multiple integrated application terminals can run concurrently under the same username. Each must pick a distinct per-connection clientId so the broker does not treat them as the same session β€” the shared username is the identity, not the connection.

The issued JWT is authentication-only: it asserts the username and carries an expiry, signed with HS256 using a secret the broker also holds. On connect, the broker checks the signature and the expiry β€” nothing else. The token deliberately contains no permission rules. Its lifetime is 8 hours by default and is configurable per environment, so drive your refresh timer off the expiresAt you were handed rather than a hard-coded number.

Separately, a small, least-privilege set of permission rules is written into the broker's authorization store, keyed by your integrated application username. These grant exactly two things, each as a single scope-wide pattern rather than a per-device list:

  • the topics you may publish to (commands toward devices in your scope), and

  • the topics you may subscribe to (responses coming back from those devices).

Because the grant is a scope-wide pattern, you are automatically authorized for any device that appears or disappears under your scope later β€” no per-device maintenance, and no re-provisioning when a device is added.

A successful provisioning hands you everything needed to connect β€” broker coordinates, your username, the JWT to use as the password, the topic patterns you are allowed to use, and the time the JWT expires plus a refresh token. The JWT itself is never stored on the platform side; only audit metadata is kept (who, what scope, when, expiry).

At onboarding, alongside the JWT, you receive a long-lived, opaque refresh token. It is a 256-bit high-entropy random string with no internal structure (not a JWT) and carrying no claims. Only a salted hash of it is stored on the platform side, never the token itself. The plaintext is returned to you exactly once, at issuance. This means a read of the platform's data store does not expose any usable refresh token; the platform can verify a token you present (by hashing it and comparing in constant time) but can never reproduce one.

If you lose it, it cannot be recovered β€” only re-issued through onboarding. Store it durably.

When your JWT is near expiry, call with your username and refresh token. The presented token is hashed, compared against the stored hash, and if it matches and is still valid, a fresh JWT with a new expiry is minted and returned. Swap it in and reconnect.

Because the refresh endpoint sits on the public gateway, the refresh token is not the only thing standing between an attacker and a fresh credential. You must also present your current MQTT JWT as an Authorization: Bearer token on the refresh call. This proves the caller recently held a legitimately-issued credential, not merely a refresh token. Two properties make this practical:

  • Expired tokens are accepted here. The whole point of refresh is to renew a credential that is about to expire or has just expired, so the JWT's signature is validated but its expiry is deliberately ignored β€” on this endpoint only. Instead the token's age is bounded: a JWT issued more than 90 days ago is refused. Since the issued-at is re-stamped on every refresh, an actively-refreshing integrated application always passes, while an ancient leaked token does not.

  • The identity must match. The JWT's username claim must equal the username being refreshed, so a valid token for one scope cannot be used to refresh another.

  • Refresh does not rotate the refresh token. The same refresh token keeps working for repeated refreshes, which is what lets an integrated application that was offline past its JWT's expiry come back on its own. The refresh response returns "refreshToken": null β€” that is expected; keep the token you already hold and do not overwrite it with the null.

  • Re-onboarding does rotate it. If the portal re-provisions your scope, a brand-new refresh token is issued and the previous one stops working. Because the identity is shared per scope, this affects all integrated application instances under that scope.

The refresh token is issued with 90 days of life, and each successful refresh slides its expiry: if fewer than 60 days remain it is pushed back out to 60 days from now; if more remain it is left alone. The slide only ever extends β€” it never shortens a longer window, and it cannot revive an already-expired or revoked token. The practical effect: an integrated application that refreshes on schedule never falls below 60 days and never needs re-onboarding, while one that stays dark past that window lapses naturally.

A refresh attempt is refused when any of these hold: the access-token factor is missing, malformed, wrongly-signed, older than 90 days, or for a different username; the username is unknown; the refresh token does not match; the refresh token has expired; or your scope has been revoked (Β§5). Two 401 responses are distinguishable, and it is worth branching on them:

Body
Meaning
What to do
  • Persist the refresh token durably and atomically (write-then-rename), so a crash never leaves it half-written.

  • Refresh proactively, with margin β€” around 75–80% of the JWT lifetime is a good trigger. This is also what keeps the sliding window from lapsing.

  • Tolerate clock skew and retry transient failures (502, 503) with backoff.

Access can be withdrawn by whoever issued your credentials β€” a decommissioned terminal, a merchant offboarding, or a credential believed compromised. This is an operator action on the issuing side; there is no endpoint for it on the integrated application-facing surface. What you would observe:

  • Refresh stops working, returning {"error":"Invalid username or refresh token."}.

  • Live connections are disconnected, and reconnecting is blocked β€” even with a JWT that has not expired yet. That block lifts on its own once the JWT would have expired anyway.

  • Publishes and subscribes stop being authorized, because the permission rules for your scope are removed.

Withdrawal comes in two flavors. The default is reversible: if it was done in error, access can be restored in place, and your integrated application simply refreshes with the original refresh token it still holds and reconnects β€” no re-onboarding. The stricter form is permanent, appropriate when a token may be compromised; restoring access then requires full re-onboarding through the portal. In either case, the correct client behavior is the same: stop retrying, surface the state, and contact whoever issued your credentials.

Short-lived JWTs keep blast radius small and require no revocation list for the common case β€” tokens simply expire. Refresh tokens let an unattended integrated application stay alive without a human in the loop, while hash-only storage keeps a refresh-token leak from a database read off the table. Splitting authentication (the token) from authorization (the rules the broker consults live) is the single decision that makes instant withdrawal of access possible at all β€” and pairing it with a self-expiring connection block means it takes effect immediately without leaving lasting residue on a reusable scope identity. Sliding the refresh token's expiry on every use closes the last gap: an actively-running integrated application is never dragged back through a portal on the calendar's say-so, while a credential nobody is using still lapses on schedule.

Keep one clean fallback: if a refresh is ever hard-rejected (expired or revoked), stop retrying and surface a "needs re-onboarding" state rather than hammering the endpoint.

  • Treat both secrets as passwords. Never log the JWT or the refresh token.

  • {"error":"Unauthorized."}

    The JWT factor failed.

    Usually recoverable β€” refresh and retry once.

    {"error":"Invalid username or refresh token."}

    The refresh-token factor failed β€” wrong, expired, or revoked.

    Lifecycle at a Glance

    The Integrated Application Identity

    Authentication (the JWT)

    Authorization (The Permission Rules)

    Why split them? Keeping permissions out of the token is what makes instant revocation possible. If permissions lived inside the JWT, they could not be taken away without waiting for the token to expire. Because they live in a store the broker consults on every action, they can be changed at any moment.

    What You Receive

    Refresh Tokens: Staying Alive Without the Portal

    What Is A Refresh Token?

    How Refresh Works

    An Up-front Second Factor: Your Current Access Token

    Two Deliberate Properties

    Refreshing Also Keeps the Refresh Token Alive

    When Refresh Stops Working

    What Your Integrated Application Is Responsible For

    If Your Access Is Withdrawn

    Design Rationale

    POST /api/pos-credentials/refresh

    Not recoverable by retrying. Stop and surface a "needs re-onboarding" state.

    Qwantifi

    These guides cover integrating a point-of-sale or backend application with QwantiFi, MagTek's cloud MQTT layer for commanding readers over the network. Read the credential model first to understand how your application authenticates to the broker and renews that access on its own, then follow the Integration Guide to connect, address devices by topic, and handle their responses.

    New to QwantiFi? Start with the QwantiFi Overview under Services for the big picture.

    In This Section

    Reference Section

    Information Available

    Explains how an integrated application authenticates to the QwantiFi broker with a short-lived token plus a long-lived refresh token, how it renews access without a portal login, and what happens when access is withdrawn.

    Integration Guide

    Walks through connecting an integrated application to the broker and using QwantiFi topics to command one device or an entire fleet and handle their responses, with worked examples.

    Need More Help

    Need Help?

    For additional support, please contact MagTek Support:

    Technical Support:

    • πŸ“§ Email: support@magtek.com

    • πŸ“ž Phone: 1-800-788-6835 (US) | +1-562-546-6616 (International)

    • πŸ• Hours: Monday-Friday, 6:00 AM - 5:00 PM PST

    Online Resources:

    • 🌐 Official Site:

    • πŸ’¬ Developer Forum:

    Documentation Feedback:

    Help us improve this documentation!

    QwantiFi Credential Model
    https://www.magtek.com
    https://forum.magtek.com
    Submit feedback

    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 .

    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 mqttConfig object you were given at onboarding carries everything you need:

    Field
    Meaning

    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:

    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

    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 %}

    Open a standard MQTT connection using the credential fields:

    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.

    All topics follow the hierarchy:

    Two distinct branches are used:

    Direction
    Topic shape
    Who publishes
    Who subscribes
    • 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.

    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:

    You have two equivalent strategies β€” choose per your routing needs.

    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:

    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.

    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 , 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 or .

    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 . It reports connected, status (PENDING/PROVISIONED), and blocked.

    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.

    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 =, 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

    For the full credential model behind refresh, see the .

    • 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

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

    Action
    Topic

    For the complete endpoint surface β€” request shapes, headers, and status codes β€” see the .

    Connect checklist

    Actual response topics (one device): MagTek/13204/2026213/device/publish/TEST-001/MMSMessage and …/TEST-001/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).

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

  • 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

    {
      "broker": "<your-broker-host>",
      "port": 8883,
      "peerCommonName": "",
      "username": "pos-13204-2026213",
      "password": "<HS256 JWT>",
      "publishTopics":   ["MagTek/13204/2026213/TEST-001/SendCommand"],
      "subscribeTopics": [
        "MagTek/13204/2026213/device/publish/TEST-001/MMSMessage",
        "MagTek/13204/2026213/device/publish/TEST-001/Status",
        "MagTek/13204/2026213/device/publish/+/MMSMessage",
        "MagTek/13204/2026213/device/publish/+/Status"
      ],
      "operatingMode": "mqtts",
      "expiresAt": "2026-06-19T01:43:00Z"
    }
    client = mqtt.connect({
        host:     mqttConfig.broker,
        port:     mqttConfig.port,
        username: mqttConfig.username,      // CONNECT username
        password: mqttConfig.password,      // CONNECT password = the JWT
        clientId: makeUniqueClientId(mqttConfig.username),
        tls:               port_is_tls,     // when port is a TLS port
        expectedCommonName: mqttConfig.peerCommonName   // verify against server cert
    })
    makeUniqueClientId(username) = username + "-" + stationId + "-" + randomShortId()
    //  e.g.  "pos-13204-2026213-lane1-9fa3"
    MagTek / {ResellerId} / {CustomerId} / {DeviceSN}

    Command (to device)

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

    POS (and backend)

    the device

    client.subscribe("MagTek/13204/2026213/device/publish/TEST-001/MMSMessage", qos=1)
    client.subscribe("MagTek/13204/2026213/device/publish/TEST-001/Status",     qos=1)
    topic   = "MagTek/13204/2026213/TEST-001/SendCommand"
    payload = buildDeviceCommand(...)        // device-protocol bytes β€” see note below
    client.publish(topic, payload, qos=1)
    on_message(topic, payload):
        if topic.startsWith("MagTek/13204/2026213/device/publish/TEST-001/"):
            handleDeviceResponse("TEST-001", topic, payload)
    client.subscribe("MagTek/13204/2026213/device/publish/+/MMSMessage", qos=1)   // merchant scope
    client.subscribe("MagTek/13204/2026213/device/publish/+/Status",     qos=1)
    // reseller scope would be:  "MagTek/13204/+/device/publish/+/MMSMessage" (and .../+/Status)
    filters = mqttConfig.subscribeTopics.slice(-2)     // [ .../+/MMSMessage, .../+/Status ]
    for topic in filters:
        client.subscribe(topic, qos=1)
    on_message(topic, payload):
        // topic = ".../device/publish/<SN>/<MMSMessage|Status>"
        parts = topic.split("/")
        sn    = parts[parts.length - 2]
        handleDeviceResponse(sn, topic, payload)
    for sn in mySerials:
        client.publish("MagTek/13204/2026213/" + sn + "/SendCommand", buildCommand(sn), qos=1)
    for sn in mySerials:
        client.subscribe("MagTek/13204/2026213/device/publish/" + sn + "/MMSMessage", qos=1)
        client.subscribe("MagTek/13204/2026213/device/publish/" + sn + "/Status",     qos=1)
    loop:
        if now() > expiresAt - refreshMargin:
            mqttConfig = POST /api/pos-credentials/refresh { username, refreshToken }
            reconnectWith(mqttConfig)

    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

    The Credential Payload

    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.

    Don't rely on device ordering. Two things are guaranteed: within a device's pair MMSMessage comes before Status, and the two scope filters are always the last two entries of subscribeTopics. The order the devices themselves appear in is not stable between calls, so never pair the lists positionally. When you need serial-to-topic pairing, use , which returns them already associated.

    Authorization scope vs. target topic. Your broker-side grant is a scope filter with a + at the device-serial position β€” the boundary of what you are allowed to use β€” covering every device (present and future) under your scope. You target one device by publishing/subscribing to its concrete topic, which the broker permits because it falls inside the filter:

    Connecting to the Broker

    ClientId Must Be Unique Per Connection

    Understanding the Topic Model

    Managing a Single Device

    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 to build/parse the bytes. Treat the payload as opaque at the MQTT layer.

    Managing Many Devices

    Wildcard subscriptions (simplest)

    Per-device Subscriptions (Explicit)

    Discovering Your Device Serials

    Checking Whether a Device is Reachable

    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

    Token Lifecycle and Reconnection

    Operational Notes

    Quick Reference

    POST /api/pos-device-list
    POST /api/pos-credentials/refresh
    POST /api/pos-topics-lis
    POST /api/pos-device-status/{deviceSn}
    POST /api/pos-credentials/refresh
    QwantiFi Credential Model
    API Reference
    QwantiFi Credential Model

    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.

    Response (from device)

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

    the device

    POS (and dashboards)

    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)

    POST /api/pos-device-list
    DynaFlex Programmer's Manual