{"info":{"_postman_id":"aab7e829-5d6e-4491-9e02-7f73b59e75a2","name":"QwantiFi Integrated Applications Self-Service API","description":"<html><head></head><body><h1 id=\"intended-audience\"><strong>Intended Audience</strong></h1>\n<p><strong>This document is intended to support an application developer (hands on technical programmer) during their integration with Magensa services.</strong></p>\n<p>The integrating developer should be familiar with the concepts of Restful APIs and have a good understanding of payment device communications.</p>\n<p>This set of examples is designed to get a developer up and running quickly. It is not designed to be exhaustive of all possible integration scenarios. For more advanced integrations and other cases not covered here, please contact your assigned solutions architect for information.</p>\n<p>In order to fully benefit from the examples provided, the integrating developer should have a Postman account where the collection can be copied to. Create a new Postman account here: <a href=\"https://www.postman.com\">https://www.postman.com</a></p>\n<h1 id=\"overview\">Overview</h1>\n<p><strong>Integrated Applications credentials are provisioned for you.</strong> Onboarding is a portal / server-to-server operation and is not part of this API — an Integrated Application cannot mint itself a credential or a new refresh token. If you have no credentials, or your refresh token has expired, contact whoever onboarded you.</p>\n<h3 id=\"rms-device-management-for-qwantifi-cloud\">RMS Device Management for QwantiFi Cloud</h3>\n<p>Please see this collection for the RMS device management for QwantiFi Cloud: <a href=\"https://rmsv2.developer.magtek.com/#919d6207-be9d-41b6-942b-c9dadb1aa66a\">QwantiFi Cloud Management</a></p>\n<h2 id=\"authentication\">Authentication</h2>\n<p>Every request in this collection carries <strong>two factors</strong>, and both must pass.</p>\n<p><strong>1.</strong> <strong><code>Authorization: Bearer</code></strong> — your current Integrated Applications access token. This is <code>mqttConfig.password</code> from your last issuance or refresh: the same token you place in the MQTT <code>CONNECT</code> password field. It is not a separate API key. It is configured once as collection-level Bearer auth, so every request inherits it — you do not add the header yourself.</p>\n<p><strong>2.</strong> <strong><code>username</code></strong> <strong>and</strong> <strong><code>refreshToken</code></strong> <strong>in the request body</strong> — your Integrated Application username and the opaque refresh token you received at onboarding. The refresh token is verified against a stored salted hash; the platform never holds the plaintext, and it cannot be recovered if you lose it.</p>\n<p><strong>Your access token must be unexpired — except on refresh.</strong> <code>pos-credentials/refresh</code> accepts an expired token, because replacing an expired credential is exactly what it is for. The three read-only requests reject one with <code>401</code>. If yours has lapsed, refresh first and retry with the token that comes back.</p>\n<p><strong>Keep your last access token even after it expires</strong> — it is what you present to refresh. Losing it means you cannot refresh, and there is no self-service way back.</p>\n<p>A request is rejected when the token's signature does not verify, when its <code>username</code> claim does not match the <code>username</code> in the body, when it has expired (on the three read-only requests), when it was issued more than the refresh-token lifetime ago (90 days by default — this one applies everywhere, including refresh), or when the refresh token is invalid, expired, or revoked. Any failure of either factor returns a single generic <code>401</code> with the body <code>{ \"error\": \"Unauthorized.\" }</code> and no further detail, by design.</p>\n<h2 id=\"quickstart-guide\">Quickstart Guide</h2>\n<ol>\n<li><p>Set <code>posUsername</code>, <code>posRefreshToken</code>, and <code>posAccessToken</code> from the payload you received at onboarding.</p>\n</li>\n<li><p>Set <code>baseUrl</code> for your environment. To test against the MagTek DEV environment, use <code>https://devgw.magensa.dev/MagensaMQTT</code>.</p>\n</li>\n<li><p>Run <strong>Refresh Access Token</strong> first. Its test script writes the new token back into <code>posAccessToken</code>, so the three read-only requests work immediately afterward.</p>\n</li>\n</ol>\n<p>The saved example responses throughout this collection show the MagTek DEV environment. Broker coordinates, reseller and customer ids, and device serials all differ per environment and per account.</p>\n<h2 id=\"notes\">Notes</h2>\n<ul>\n<li><p><strong>Only refresh mints anything.</strong> The three <strong>Device Discovery</strong> requests are strictly read-only: no token is issued and your refresh token is never rotated.</p>\n</li>\n<li><p><strong>Refresh does not rotate your refresh token.</strong> <code>refreshToken</code> comes back <code>null</code> on the refresh response — keep using the one you already have. Refreshing on schedule does slide its expiry forward, so an actively-refreshing Integrated Application is never forced back through onboarding.</p>\n</li>\n<li><p><strong>Your scope is inferred server-side</strong> from your stored Integrated Application record — either reseller-wide or a single customer/merchant. You never name a reseller or customer in a request body, and you can only ever see your own devices.</p>\n</li>\n<li><p>Errors are always shaped <code>{ \"error\": \"...\" }</code>. <code>400</code> means a required field is missing, <code>401</code> an authentication failure, <code>404</code> a device you do not own or that does not exist, and <code>503</code> that the service is temporarily unavailable — retry that one with backoff.</p>\n</li>\n</ul>\n</body></html>","schema":"https://schema.getpostman.com/json/collection/v2.0.0/collection.json","toc":[{"content":"Intended Audience","slug":"intended-audience"},{"content":"Overview","slug":"overview"}],"owner":"33729815","collectionId":"aab7e829-5d6e-4491-9e02-7f73b59e75a2","publishedId":"2sBYB1MTR2","public":true,"customColor":{"top-bar":"FFFFFF","right-sidebar":"303030","highlight":"FF6C37"},"publishDate":"2026-09-16T21:40:52.000Z"},"item":[{"name":"Credentials","item":[{"name":"pos-credentials/refresh (Refresh Access Token)","event":[{"listen":"test","script":{"type":"text/javascript","exec":["pm.test(\"Success Status Code\", function () {","    pm.response.to.have.status(200);","});","","// Capture the new access token so the Device Discovery requests can run against it.","// mqttConfig.password is both your MQTT CONNECT password and your API bearer token.","pm.test(\"Access token returned\", function () {","    const body = pm.response.json();","    pm.expect(body.mqttConfig).to.have.property(\"password\");","    pm.collectionVariables.set(\"posAccessToken\", body.mqttConfig.password);","});"],"id":"f2be182b-915b-44d2-84c6-4b72bee782a5"}}],"id":"a0ccabd9-cabb-491d-a917-76378eb410f3","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"body":{"mode":"raw","raw":"{\n  \"username\": \"pos-13204-2026213\",\n  \"refreshToken\": \"<your refresh token from onboarding>\"\n}","options":{"raw":{"language":"json"}}},"url":"https://mqtt.magensa.com/MagensaMQTT/api/pos-credentials/refresh","description":"<h1 id=\"refresh-access-token\">Refresh Access Token</h1>\n<p>Mints a fresh POS access token from the refresh token you were issued at onboarding — no portal involvement. Present your current access token as <code>Authorization: Bearer</code> alongside your <code>username</code> and <code>refreshToken</code>.</p>\n<h2 id=\"request-body\">Request body</h2>\n<ul>\n<li><code>username</code> — your POS username: <code>pos-{resellerId}</code> (reseller-wide) or <code>pos-{resellerId}-{customerId}</code> (customer-scoped).</li>\n<li><code>refreshToken</code> — the opaque token returned once at onboarding.</li>\n</ul>\n<h2 id=\"response\">Response</h2>\n<p><code>200 OK</code> returns <code>status: \"ISSUED\"</code> and a full <code>mqttConfig</code>: <code>broker</code>, <code>port</code>, <code>peerCommonName</code>, <code>qos</code>, <code>username</code>, <code>password</code>, <code>publishTopics</code>, <code>subscribeTopics</code>, <code>operatingMode</code>, and <code>expiresAt</code>.</p>\n<p><code>mqttConfig.password</code> is your <strong>new access token</strong> — place it in the MQTT <code>CONNECT</code> password field and present it as <code>Authorization: Bearer</code> on subsequent calls. Drive your refresh timer off <code>expiresAt</code> rather than a hard-coded interval; the lifetime is configurable per environment.</p>\n<p><code>refreshToken</code> in the response is always <code>null</code> — the refresh path does not rotate it. Keep using the one you already have.</p>\n<h2 id=\"notes\">Notes</h2>\n<ul>\n<li><strong>This is the only request that accepts an expired access token.</strong> The three <strong>Device Discovery</strong> requests reject one with <code>401</code>. A token issued more than the refresh-token lifetime ago (90 days by default) is refused here too.</li>\n<li>The topic lists are recomputed from your current device set, so refreshing is also how you pick up devices onboarded since your last token.</li>\n<li>Refreshing slides your refresh token's own expiry forward when it has burned below the sliding window, so a POS that refreshes on schedule is never forced back through onboarding.</li>\n<li><code>400</code> when <code>username</code> or <code>refreshToken</code> is missing. <code>401</code> for a missing, malformed, mismatched, or over-age access token, or an invalid, expired, or revoked refresh token. <code>503</code> when the service is temporarily unavailable.</li>\n</ul>\n<p>This request's test script captures <code>mqttConfig.password</code> into the <code>posAccessToken</code> collection variable, so run it first and the rest of the collection is ready to go.</p>\n","auth":{"type":"bearer","bearer":{"basicConfig":[{"key":"token","value":"<your current access token>"}]},"isInherited":true,"source":{"_postman_id":"aab7e829-5d6e-4491-9e02-7f73b59e75a2","id":"aab7e829-5d6e-4491-9e02-7f73b59e75a2","name":"QwantiFi Integrated Applications Self-Service API","type":"collection"}},"urlObject":{"path":["api","pos-credentials","refresh"],"host":["https://mqtt.magensa.com/MagensaMQTT"],"query":[],"variable":[]}},"response":[{"id":"c8f673ba-e335-4912-b26f-e77a212c1ec0","name":"Refresh Access Token - 200 OK","originalRequest":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"body":{"mode":"raw","raw":"{\n  \"username\": \"pos-13204-2026213\",\n  \"refreshToken\": \"<your refresh token from onboarding>\"\n}","options":{"raw":{"language":"json"}}},"url":"https://mqtt.magensa.com/MagensaMQTT/api/pos-credentials/refresh"},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json; charset=utf-8"}],"cookie":[],"responseTime":null,"body":"{\n  \"posClientId\": \"POS-Station-1\",\n  \"status\": \"ISSUED\",\n  \"mqttConfig\": {\n    \"broker\": \"dev-mqtt.magensa.dev\",\n    \"port\": 8883,\n    \"peerCommonName\": \"dev-mqtt.magensa.dev\",\n    \"qos\": 0,\n    \"username\": \"pos-13204-2026213\",\n    \"password\": \"<your new access token - place in the MQTT CONNECT password field>\",\n    \"publishTopics\": [\n      \"MagTek/13204/2026213/DEVSN001/SendCommand\",\n      \"MagTek/13204/2026213/DEVSN002/SendCommand\"\n    ],\n    \"subscribeTopics\": [\n      \"MagTek/13204/2026213/device/publish/DEVSN001/MMSMessage\",\n      \"MagTek/13204/2026213/device/publish/DEVSN001/Status\",\n      \"MagTek/13204/2026213/device/publish/DEVSN002/MMSMessage\",\n      \"MagTek/13204/2026213/device/publish/DEVSN002/Status\",\n      \"MagTek/13204/2026213/device/publish/+/MMSMessage\",\n      \"MagTek/13204/2026213/device/publish/+/Status\"\n    ],\n    \"operatingMode\": \"mqtts\",\n    \"expiresAt\": \"2026-08-18T02:43:00Z\"\n  },\n  \"refreshToken\": null\n}"}],"_postman_id":"a0ccabd9-cabb-491d-a917-76378eb410f3"}],"id":"173d740b-4103-4b9f-a284-4b26123aceeb","description":"<h1 id=\"credentials\">Credentials</h1>\n<p>Endpoints in this folder support <strong>keeping your POS access token alive without a portal login</strong>.</p>\n<p>Use these requests to:</p>\n<ul>\n<li><strong>Mint a fresh access token</strong> before or after your current one expires</li>\n<li><strong>Pick up devices</strong> added to your scope since your token was issued</li>\n</ul>\n<h2 id=\"requests-included\">Requests included</h2>\n<ol>\n<li><strong>pos-credentials/refresh</strong> — exchanges your <code>username</code> and <code>refreshToken</code>, together with your current access token, for a fresh access token and an up-to-date MQTT profile.</li>\n</ol>\n<h2 id=\"common-variables\">Common variables</h2>\n<p>These requests rely on the following collection variables:</p>\n<ul>\n<li><code>baseUrl</code> — host and virtual path of the gateway.</li>\n<li><code>posUsername</code> — your POS username, <code>pos-{resellerId}</code> or <code>pos-{resellerId}-{customerId}</code>.</li>\n<li><code>posRefreshToken</code> — the opaque refresh token issued at onboarding.</li>\n<li><code>posAccessToken</code> — your current access token. The refresh request rewrites this on success.</li>\n</ul>\n<h2 id=\"notes\">Notes</h2>\n<ul>\n<li>This is the <strong>only</strong> request in the collection that accepts an expired access token.</li>\n<li>The returned <code>mqttConfig.publishTopics</code> and <code>mqttConfig.subscribeTopics</code> are recomputed from your current device set, so a refresh doubles as a device-roster refresh.</li>\n<li>Your refresh token is <strong>not</strong> rotated; <code>refreshToken</code> in the response is always <code>null</code>.</li>\n</ul>\n","_postman_id":"173d740b-4103-4b9f-a284-4b26123aceeb","auth":{"type":"bearer","bearer":{"basicConfig":[{"key":"token","value":"<your current access token>"}]},"isInherited":true,"source":{"_postman_id":"aab7e829-5d6e-4491-9e02-7f73b59e75a2","id":"aab7e829-5d6e-4491-9e02-7f73b59e75a2","name":"QwantiFi Integrated Applications Self-Service API","type":"collection"}}},{"name":"Device Discovery","item":[{"name":"pos-topics-list (List Scope Topics)","id":"15562f00-0e24-414e-82c5-62b9d868e40f","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"body":{"mode":"raw","raw":"{\n  \"username\": \"pos-13204-2026213\",\n  \"refreshToken\": \"<your refresh token from onboarding>\"\n}","options":{"raw":{"language":"json"}}},"url":"https://mqtt.magensa.com/MagensaMQTT/api/pos-topics-list","description":"<h1 id=\"list-scope-topics\">List Scope Topics</h1>\n<p>Returns the up-to-date publish and subscribe topics for every device in your scope, without issuing or refreshing anything. Ideal as a periodic poll to discover devices added or removed since your access token was minted.</p>\n<h2 id=\"request-body\">Request body</h2>\n<p>Identical to <strong>Refresh Access Token</strong>: <code>username</code> and <code>refreshToken</code>. Your scope is inferred from your stored POS record, so the body names no reseller or customer.</p>\n<h2 id=\"response\">Response</h2>\n<p><code>200 OK</code> returns exactly two lists.</p>\n<p><code>publishTopics</code> — one <code>SendCommand</code> topic per device, <code>MagTek/{resellerId}/{customerId}/{deviceSn}/SendCommand</code>. There is deliberately no wildcard entry here: MQTT forbids wildcards in a PUBLISH topic, so commands are always published one device at a time.</p>\n<p><code>subscribeTopics</code> — each device's two event topics (<code>.../device/publish/{deviceSn}/MMSMessage</code> then <code>.../Status</code>), followed by the <strong>two scope filters</strong> <code>.../device/publish/+/MMSMessage</code> and <code>.../device/publish/+/Status</code>. Subscribe to those two instead of the per-device entries to receive from every device in your scope — including ones onboarded later — in two SUBSCRIBEs.</p>\n<p>With no devices in scope, <code>publishTopics</code> is empty and <code>subscribeTopics</code> holds just the two filters.</p>\n<h2 id=\"notes\">Notes</h2>\n<ul>\n<li><strong>Read-only.</strong> No token is minted and your refresh token is not rotated; both credentials are used purely to authenticate.</li>\n<li><strong>An expired access token is rejected</strong> with <code>401</code>. Run <strong>Refresh Access Token</strong> first and retry with the token it returns.</li>\n<li><code>400</code> when <code>username</code> or <code>refreshToken</code> is missing. <code>503</code> when the service is temporarily unavailable.</li>\n</ul>\n","auth":{"type":"bearer","bearer":{"basicConfig":[{"key":"token","value":"<your current access token>"}]},"isInherited":true,"source":{"_postman_id":"aab7e829-5d6e-4491-9e02-7f73b59e75a2","id":"aab7e829-5d6e-4491-9e02-7f73b59e75a2","name":"QwantiFi Integrated Applications Self-Service API","type":"collection"}},"urlObject":{"path":["api","pos-topics-list"],"host":["https://mqtt.magensa.com/MagensaMQTT"],"query":[],"variable":[]}},"response":[{"id":"b8bad838-597e-48b6-8dbc-c63e19698f0b","name":"List Scope Topics - 200 OK","originalRequest":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"body":{"mode":"raw","raw":"{\n  \"username\": \"pos-13204-2026213\",\n  \"refreshToken\": \"<your refresh token from onboarding>\"\n}","options":{"raw":{"language":"json"}}},"url":"https://mqtt.magensa.com/MagensaMQTT/api/pos-topics-list"},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json; charset=utf-8"}],"cookie":[],"responseTime":null,"body":"{\n  \"publishTopics\": [\n    \"MagTek/13204/2026213/DEVSN001/SendCommand\",\n    \"MagTek/13204/2026213/DEVSN002/SendCommand\"\n  ],\n  \"subscribeTopics\": [\n    \"MagTek/13204/2026213/device/publish/DEVSN001/MMSMessage\",\n    \"MagTek/13204/2026213/device/publish/DEVSN001/Status\",\n    \"MagTek/13204/2026213/device/publish/DEVSN002/MMSMessage\",\n    \"MagTek/13204/2026213/device/publish/DEVSN002/Status\",\n    \"MagTek/13204/2026213/device/publish/+/MMSMessage\",\n    \"MagTek/13204/2026213/device/publish/+/Status\"\n  ]\n}"}],"_postman_id":"15562f00-0e24-414e-82c5-62b9d868e40f"},{"name":"pos-device-list (List Devices)","id":"d31be510-afe4-4681-b3f8-c2844538fb67","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"body":{"mode":"raw","raw":"{\n  \"username\": \"pos-13204-2026213\",\n  \"refreshToken\": \"<your refresh token from onboarding>\"\n}","options":{"raw":{"language":"json"}}},"url":"https://mqtt.magensa.com/MagensaMQTT/api/pos-device-list","description":"<h1 id=\"list-devices\">List Devices</h1>\n<p>The same per-device information as <strong>List Scope Topics</strong>, grouped one entry per device. Use this when your POS needs to address a specific device, rather than parsing serials out of topic paths or relying on positional pairing between the two flat lists.</p>\n<h2 id=\"request-body\">Request body</h2>\n<p>Identical to <strong>Refresh Access Token</strong>: <code>username</code> and <code>refreshToken</code>. Your scope — reseller-wide or a single customer/merchant — is inferred from your stored POS record, so the body names no entity.</p>\n<h2 id=\"response\">Response</h2>\n<p><code>200 OK</code> returns <code>devices</code>, an array of entries:</p>\n<ul>\n<li><code>deviceSn</code> — the device serial number. This is the identifier; key off it.</li>\n<li><code>status</code> — the stored lifecycle value, <code>PENDING</code> or <code>PROVISIONED</code>. Devices of both are listed; a <code>PENDING</code> device holds no MQTT credential yet, so its topics are not live.</li>\n<li><code>subscribeTopics</code> — exactly two entries, <code>MMSMessage</code> then <code>Status</code>.</li>\n<li><code>publishTopic</code> — <strong>singular</strong>. The one <code>SendCommand</code> topic for this device.</li>\n<li><code>nickname</code> — the operator-facing label from the device profile, for display instead of a bare serial. <strong>The key is omitted entirely when no nickname is set</strong> — never <code>null</code>, never an empty string — so code it as an optional property. It is display text only: not unique, not stable, not an identifier.</li>\n</ul>\n<p><code>devices</code> is <code>[]</code> when you own none. A reseller-wide POS sees every customer's devices, each entry's topics carrying that device's own <code>customerId</code>.</p>\n<h2 id=\"notes\">Notes</h2>\n<ul>\n<li>Note that <code>publishTopic</code> is <strong>singular</strong> here while <code>subscribeTopics</code> is plural — a common source of confusion. Each device has exactly one command topic and two event topics.</li>\n<li>The two scope-wide subscribe filters that <strong>List Scope Topics</strong> appends are deliberately absent here: they belong to no single device. Use that request to subscribe across your whole scope at once.</li>\n<li><strong>Read-only</strong> — nothing is minted and your refresh token is not rotated.</li>\n<li><strong>An expired access token is rejected</strong> with <code>401</code>. Run <strong>Refresh Access Token</strong> first.</li>\n</ul>\n","auth":{"type":"bearer","bearer":{"basicConfig":[{"key":"token","value":"<your current access token>"}]},"isInherited":true,"source":{"_postman_id":"aab7e829-5d6e-4491-9e02-7f73b59e75a2","id":"aab7e829-5d6e-4491-9e02-7f73b59e75a2","name":"QwantiFi Integrated Applications Self-Service API","type":"collection"}},"urlObject":{"path":["api","pos-device-list"],"host":["https://mqtt.magensa.com/MagensaMQTT"],"query":[],"variable":[]}},"response":[{"id":"fed68a09-4627-4ddb-882c-dc4365ea9dcb","name":"List Devices - 200 OK","originalRequest":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"body":{"mode":"raw","raw":"{\n  \"username\": \"pos-13204-2026213\",\n  \"refreshToken\": \"<your refresh token from onboarding>\"\n}","options":{"raw":{"language":"json"}}},"url":"https://mqtt.magensa.com/MagensaMQTT/api/pos-device-list"},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json; charset=utf-8"}],"cookie":[],"responseTime":null,"body":"{\n  \"devices\": [\n    {\n      \"deviceSn\": \"DEVSN001\",\n      \"status\": \"PROVISIONED\",\n      \"subscribeTopics\": [\n        \"MagTek/13204/2026213/device/publish/DEVSN001/MMSMessage\",\n        \"MagTek/13204/2026213/device/publish/DEVSN001/Status\"\n      ],\n      \"publishTopic\": \"MagTek/13204/2026213/DEVSN001/SendCommand\",\n      \"nickname\": \"Front counter 1\"\n    },\n    {\n      \"deviceSn\": \"DEVSN002\",\n      \"status\": \"PENDING\",\n      \"subscribeTopics\": [\n        \"MagTek/13204/2026213/device/publish/DEVSN002/MMSMessage\",\n        \"MagTek/13204/2026213/device/publish/DEVSN002/Status\"\n      ],\n      \"publishTopic\": \"MagTek/13204/2026213/DEVSN002/SendCommand\"\n    }\n  ]\n}"}],"_postman_id":"d31be510-afe4-4681-b3f8-c2844538fb67"},{"name":"pos-device-status (Get Device Status)","id":"2f945660-65bc-40b9-bb7c-163240da32b6","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"body":{"mode":"raw","raw":"{\n  \"username\": \"pos-13204-2026213\",\n  \"refreshToken\": \"<your refresh token from onboarding>\"\n}","options":{"raw":{"language":"json"}}},"url":"https://mqtt.magensa.com/MagensaMQTT/api/pos-device-status/DEVSN001","description":"<h1 id=\"get-device-status\">Get Device Status</h1>\n<p>Asks whether one specific device is reachable <strong>right now</strong>, rather than publishing a command and inferring failure from silence. The device serial comes from the URL path, so the body carries only your two authentication values.</p>\n<h2 id=\"path-parameter\">Path parameter</h2>\n<ul>\n<li><code>deviceSn</code> — the device serial number, maximum 64 characters.</li>\n</ul>\n<h2 id=\"request-body\">Request body</h2>\n<p>Identical to <strong>Refresh Access Token</strong>: <code>username</code> and <code>refreshToken</code>. Your scope is inferred from your stored POS record.</p>\n<h2 id=\"response\">Response</h2>\n<p><code>200 OK</code> returns:</p>\n<ul>\n<li><code>connected</code> — read <strong>live from the broker</strong> at request time; it is not a stored field. It is also <code>false</code> when the broker's management API is unreachable, so read it as \"not known to be connected\" rather than proof the device is absent.</li>\n<li><code>status</code> — the stored lifecycle value, <code>PENDING</code> or <code>PROVISIONED</code>. A <code>PENDING</code> device holds no MQTT credential and so is never connected.</li>\n<li><code>blocked</code> — the stored policy flag. A blocked device is banned on the broker.</li>\n<li><code>deviceSubscribe</code> — a single topic.</li>\n<li><code>devicePublish</code> — two topics, <code>MMSMessage</code> then <code>Status</code>.</li>\n</ul>\n<h2 id=\"notes\">Notes</h2>\n<ul>\n<li><strong>The two topic fields are named from the <em>device's</em> perspective</strong>, the mirror of the naming in <strong>List Devices</strong>. Your POS <strong>publishes to</strong> <code>deviceSubscribe</code> and <strong>subscribes to</strong> the entries in <code>devicePublish</code>. Read them as \"what the device does\" and your own direction is the opposite.</li>\n<li>A serial outside your scope and one that does not exist both return the same generic <code>404</code>, with the serial <strong>not</strong> echoed — so the route cannot be walked to discover which serials exist.</li>\n<li><strong>Read-only</strong> — nothing is minted and your refresh token is not rotated.</li>\n<li><strong>An expired access token is rejected</strong> with <code>401</code>. Run <strong>Refresh Access Token</strong> first.</li>\n<li><code>400</code> when <code>deviceSn</code>, <code>username</code>, or <code>refreshToken</code> is missing, or <code>deviceSn</code> exceeds 64 characters. <code>503</code> when the service is temporarily unavailable.</li>\n</ul>\n","auth":{"type":"bearer","bearer":{"basicConfig":[{"key":"token","value":"<your current access token>"}]},"isInherited":true,"source":{"_postman_id":"aab7e829-5d6e-4491-9e02-7f73b59e75a2","id":"aab7e829-5d6e-4491-9e02-7f73b59e75a2","name":"QwantiFi Integrated Applications Self-Service API","type":"collection"}},"urlObject":{"path":["api","pos-device-status","DEVSN001"],"host":["https://mqtt.magensa.com/MagensaMQTT"],"query":[],"variable":[{"id":"223f4666-7b69-40d9-a356-607672be8be7","description":{"content":"<p>Serial number of the device to inspect. Maximum 64 characters.</p>\n","type":"text/plain"},"type":"any","value":"DEVSN001","key":"deviceSn"}]}},"response":[{"id":"c7141e30-9e36-4717-8430-36b8ded0b8c1","name":"Get Device Status - 200 OK","originalRequest":{"method":"POST","header":[{"key":"Content-Type","value":"application/json"}],"body":{"mode":"raw","raw":"{\n  \"username\": \"pos-13204-2026213\",\n  \"refreshToken\": \"<your refresh token from onboarding>\"\n}","options":{"raw":{"language":"json"}}},"url":{"raw":"https://mqtt.magensa.com/MagensaMQTT/api/pos-device-status/DEVSN001","host":["https://mqtt.magensa.com/MagensaMQTT"],"path":["api","pos-device-status","DEVSN001"],"variable":[{"key":"deviceSn","value":"DEVSN001","description":"Serial number of the device to inspect. Maximum 64 characters."}]}},"status":"OK","code":200,"_postman_previewlanguage":"json","header":[{"key":"Content-Type","value":"application/json; charset=utf-8"}],"cookie":[],"responseTime":null,"body":"{\n  \"connected\": true,\n  \"status\": \"PROVISIONED\",\n  \"blocked\": false,\n  \"deviceSubscribe\": \"MagTek/13204/2026213/DEVSN001/SendCommand\",\n  \"devicePublish\": [\n    \"MagTek/13204/2026213/device/publish/DEVSN001/MMSMessage\",\n    \"MagTek/13204/2026213/device/publish/DEVSN001/Status\"\n  ]\n}"}],"_postman_id":"2f945660-65bc-40b9-bb7c-163240da32b6"}],"id":"bb55e855-de4c-40d2-acb4-57c0a3e54300","description":"<h1 id=\"device-discovery\">Device Discovery</h1>\n<p>Endpoints in this folder support <strong>read-only lookups of the devices and MQTT topics in your scope</strong>.</p>\n<p>Use these requests to:</p>\n<ul>\n<li><strong>List every topic</strong> your POS may publish to and subscribe to</li>\n<li><strong>Enumerate your devices</strong> one entry at a time, each with its own topics</li>\n<li><strong>Check whether one device is reachable right now</strong></li>\n</ul>\n<h2 id=\"requests-included\">Requests included</h2>\n<ol>\n<li><strong>pos-topics-list</strong> — two flat lists, <code>publishTopics</code> and <code>subscribeTopics</code>, covering every device in your scope plus the two scope-wide subscribe filters.</li>\n<li><strong>pos-device-list</strong> — the same topics grouped one entry per device, with each device's lifecycle <code>status</code> and optional <code>nickname</code>.</li>\n<li><strong>pos-device-status</strong> — one device's live <code>connected</code> flag, its stored <code>status</code> and <code>blocked</code> flag, and its topics.</li>\n</ol>\n<h2 id=\"common-variables\">Common variables</h2>\n<p>These requests rely on the following collection variables:</p>\n<ul>\n<li><code>baseUrl</code> — host and virtual path of the gateway.</li>\n<li><code>posUsername</code> — your POS username, <code>pos-{resellerId}</code> or <code>pos-{resellerId}-{customerId}</code>.</li>\n<li><code>posRefreshToken</code> — the opaque refresh token issued at onboarding.</li>\n<li><code>posAccessToken</code> — your current access token, which must <strong>not</strong> be expired for these three.</li>\n<li><code>deviceSn</code> — the serial number of the device to inspect, used by <strong>pos-device-status</strong>.</li>\n</ul>\n<h2 id=\"typical-usage\">Typical usage</h2>\n<ol>\n<li>Run <strong>Refresh Access Token</strong> in the <strong>Credentials</strong> folder if your access token has lapsed — all three requests here reject an expired one with <code>401</code>.</li>\n<li>Poll <strong>pos-topics-list</strong> to keep your subscriptions current, or <strong>pos-device-list</strong> when you need to address devices individually.</li>\n<li>Call <strong>pos-device-status</strong> before sending a command to a specific device, so a silent device is distinguishable from a failed command.</li>\n</ol>\n<h2 id=\"notes\">Notes</h2>\n<ul>\n<li>All three are <strong>strictly read-only</strong> — nothing is minted and your refresh token is not rotated. Both credentials are used purely to authenticate.</li>\n<li>Your scope is inferred from your stored POS record. The request body never names a reseller or customer, so you can only ever read your own devices.</li>\n</ul>\n","_postman_id":"bb55e855-de4c-40d2-acb4-57c0a3e54300","auth":{"type":"bearer","bearer":{"basicConfig":[{"key":"token","value":"<your current access token>"}]},"isInherited":true,"source":{"_postman_id":"aab7e829-5d6e-4491-9e02-7f73b59e75a2","id":"aab7e829-5d6e-4491-9e02-7f73b59e75a2","name":"QwantiFi Integrated Applications Self-Service API","type":"collection"}}}],"auth":{"type":"bearer","bearer":{"basicConfig":[{"key":"token","value":"<your current access token>"}]}},"variable":[{"key":"baseUrl","value":"https://mqtt.magensa.com/MagensaMQTT","description":"Gateway host and virtual path. For the MagTek DEV environment use https://devgw.magensa.dev/MagensaMQTT"},{"key":"posUsername","value":"pos-13204-2026213","description":"Your POS username from onboarding: pos-{resellerId} (reseller-wide) or pos-{resellerId}-{customerId} (customer-scoped)."},{"key":"posRefreshToken","value":"<your refresh token from onboarding>","description":"The opaque refresh token returned once at onboarding. Store it durably; it cannot be recovered."},{"key":"posAccessToken","value":"<your current access token>","description":"Your current access token (mqttConfig.password from the last issuance or refresh). Refresh Access Token overwrites this on success."},{"key":"deviceSn","value":"DEVSN001","description":"Serial number of the device inspected by Get Device Status."}]}