{"info":{"_postman_id":"126f45e0-caa0-4313-9034-0668fac77439","name":"ShadowGPS App API","description":"<html><head></head><body><p>Device, Timeline, Homepage &amp; Dashboard routes.</p>\n<p>Set collection variables: <code>baseUrl</code>, <code>token</code> (JWT) or <code>apiKey</code> (sgps_...), <code>deviceId</code>, <code>intervalId</code>.</p>\n</body></html>","schema":"https://schema.getpostman.com/json/collection/v2.0.0/collection.json","toc":[],"owner":"55488887","collectionId":"126f45e0-caa0-4313-9034-0668fac77439","publishedId":"2sBXwyGSt7","public":true,"customColor":{"top-bar":"FFFFFF","right-sidebar":"303030","highlight":"FF6C37"},"publishDate":"2026-06-28T17:29:56.000Z"},"item":[{"name":"Device","item":[{"name":"List Devices","id":"8997cc8c-ddf1-4157-9429-4af8c2c4714b","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"auth":{"type":"inherit","inherit":{"basicConfig":[]},"isInherited":false},"method":"GET","header":[{"key":"x-api-key","value":""}],"body":{"mode":"raw","raw":""},"url":"http://localhost:5000/api/app/device/list","description":"<p>No yearly plan required for API key.</p>\n","urlObject":{"path":["api","app","device","list"],"host":["http://localhost:5000"],"query":[],"variable":[]}},"response":[],"_postman_id":"8997cc8c-ddf1-4157-9429-4af8c2c4714b"},{"name":"Get Single Device","id":"d5f788fb-e58e-46bd-a3dd-f3c578e415f4","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"auth":{"type":"inherit","inherit":{"basicConfig":[]},"isInherited":false},"method":"GET","header":[{"key":"x-api-key","value":"{{x-api-key}}"}],"body":{"mode":"raw","raw":""},"url":"http://localhost:5000/api/app/device/getSingleDevice/","description":"<p>Yearly plan required for API key access.</p>\n","urlObject":{"path":["api","app","device","getSingleDevice",""],"host":["http://localhost:5000"],"query":[],"variable":[]}},"response":[],"_postman_id":"d5f788fb-e58e-46bd-a3dd-f3c578e415f4"},{"name":"Get Assigned Users","id":"970f363e-2a60-402c-a170-2f72e7cad6bc","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"auth":{"type":"inherit","inherit":{"basicConfig":[]},"isInherited":false},"method":"GET","header":[{"key":"x-api-key","value":"{{x-api-key}}"}],"body":{"mode":"raw","raw":""},"url":"http://localhost:5000/api/app/device/getAssignUsers/","urlObject":{"path":["api","app","device","getAssignUsers",""],"host":["http://localhost:5000"],"query":[],"variable":[]}},"response":[],"_postman_id":"970f363e-2a60-402c-a170-2f72e7cad6bc"},{"name":"Link Device","id":"5b33b283-a5e6-4881-811e-e379792d33d2","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"auth":{"type":"inherit","inherit":{"basicConfig":[]},"isInherited":false},"method":"POST","header":[{"key":"x-api-key","value":"{{x-api-key}}"},{"key":"Content-Type","value":"application/json"}],"body":{"mode":"raw","raw":"{\n  \"serialNumber\": \"\",\n  \"name\": \"\"\n}","options":{"raw":{"language":"json"}}},"url":"http://localhost:5000/api/app/device/link-device","urlObject":{"path":["api","app","device","link-device"],"host":["http://localhost:5000"],"query":[],"variable":[]}},"response":[],"_postman_id":"5b33b283-a5e6-4881-811e-e379792d33d2"},{"name":"Update Device","id":"dd74333d-f1b7-41c6-b545-c516c46cc164","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"auth":{"type":"inherit","inherit":{"basicConfig":[]},"isInherited":false},"method":"PUT","header":[{"key":"x-api-key","value":"{{x-api-key}}"}],"body":{"mode":"formdata","formdata":[{"key":"customizeDevice[imageUrl]","value":"","type":"text"},{"key":"customizeDevice[name]","value":"","type":"text"}]},"url":"http://localhost:5000/api/app/device/update-device/","urlObject":{"path":["api","app","device","update-device",""],"host":["http://localhost:5000"],"query":[],"variable":[]}},"response":[],"_postman_id":"dd74333d-f1b7-41c6-b545-c516c46cc164"},{"name":"Share Access by Email","id":"72a00022-af0f-4dfc-83f2-720d9dc6ad51","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"auth":{"type":"inherit","inherit":{"basicConfig":[]},"isInherited":false},"method":"POST","header":[{"key":"x-api-key","value":"{{x-api-key}}"},{"key":"Content-Type","value":"application/json"}],"body":{"mode":"raw","raw":"{\n  \"email\": \"\"\n}","options":{"raw":{"language":"json"}}},"url":"http://localhost:5000/api/app/device/shareAccessUserByEmail/","urlObject":{"path":["api","app","device","shareAccessUserByEmail",""],"host":["http://localhost:5000"],"query":[],"variable":[]}},"response":[],"_postman_id":"72a00022-af0f-4dfc-83f2-720d9dc6ad51"},{"name":"Remove Access by User ID","id":"e76b32f8-a0d9-448b-9321-acad8453bd02","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"auth":{"type":"inherit","inherit":{"basicConfig":[]},"isInherited":false},"method":"POST","header":[{"key":"x-api-key","value":"{{x-api-key}}"},{"key":"Content-Type","value":"application/json"}],"body":{"mode":"raw","raw":"{\n  \"userId\": \"\"\n}","options":{"raw":{"language":"json"}}},"url":"http://localhost:5000/api/app/device/removeAccessUserById/","urlObject":{"path":["api","app","device","removeAccessUserById",""],"host":["http://localhost:5000"],"query":[],"variable":[]}},"response":[],"_postman_id":"e76b32f8-a0d9-448b-9321-acad8453bd02"},{"name":"Delete Device","id":"0e60850a-29b0-42d0-aab5-dd445b653ee5","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"auth":{"type":"inherit","inherit":{"basicConfig":[]},"isInherited":false},"method":"DELETE","header":[{"key":"x-api-key","value":"{{x-api-key}}"}],"body":{"mode":"raw","raw":""},"url":"http://localhost:5000/api/app/device/deleteDevice/","urlObject":{"path":["api","app","device","deleteDevice",""],"host":["http://localhost:5000"],"query":[],"variable":[]}},"response":[],"_postman_id":"0e60850a-29b0-42d0-aab5-dd445b653ee5"},{"name":"Update Device Positions","id":"bf63a2c2-3569-47b8-8a1a-a6b7b2704990","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"auth":{"type":"inherit","inherit":{"basicConfig":[]},"isInherited":false},"method":"POST","header":[{"key":"x-api-key","value":"{{x-api-key}}"},{"key":"Content-Type","value":"application/json"}],"body":{"mode":"raw","raw":"{\n  \"positions\": []\n}","options":{"raw":{"language":"json"}}},"url":"http://localhost:5000/api/app/device/updateDevicePositions","urlObject":{"path":["api","app","device","updateDevicePositions"],"host":["http://localhost:5000"],"query":[],"variable":[]}},"response":[],"_postman_id":"bf63a2c2-3569-47b8-8a1a-a6b7b2704990"}],"id":"00f916d2-cf01-41e0-bfd3-c532a98734fe","description":"<p>JWT + API Key supported. Device-specific endpoints require yearly plan for API key access.</p>\n","_postman_id":"00f916d2-cf01-41e0-bfd3-c532a98734fe","auth":{"type":"bearer","bearer":{"basicConfig":[{"key":"token","value":""}]},"isInherited":true,"source":{"_postman_id":"126f45e0-caa0-4313-9034-0668fac77439","id":"126f45e0-caa0-4313-9034-0668fac77439","name":"ShadowGPS App API","type":"collection"}}},{"name":"Timeline","item":[{"name":"Get Trip Dates","id":"0e4a6126-2672-4a10-be15-d4d62b639d9e","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"auth":{"type":"inherit","inherit":{"basicConfig":[]},"isInherited":false},"method":"GET","header":[{"key":"x-api-key","value":"{{x-api-key}}"}],"body":{"mode":"raw","raw":""},"url":"http://localhost:5000/api/app/timeline/device//dates","urlObject":{"path":["api","app","timeline","device","","dates"],"host":["http://localhost:5000"],"query":[],"variable":[]}},"response":[],"_postman_id":"0e4a6126-2672-4a10-be15-d4d62b639d9e"},{"name":"Get Latest Trip Date","id":"ac5fb656-8e9c-4ac5-97b4-a68866534a03","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"auth":{"type":"inherit","inherit":{"basicConfig":[]},"isInherited":false},"method":"GET","header":[{"key":"x-api-key","value":"{{x-api-key}}"}],"body":{"mode":"raw","raw":""},"url":"http://localhost:5000/api/app/timeline/device//latest-date","urlObject":{"path":["api","app","timeline","device","","latest-date"],"host":["http://localhost:5000"],"query":[],"variable":[]}},"response":[],"_postman_id":"ac5fb656-8e9c-4ac5-97b4-a68866534a03"},{"name":"Get Timeline by Date","id":"d31c0a36-3b6e-4444-841e-c70a0f601bbe","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"auth":{"type":"inherit","inherit":{"basicConfig":[]},"isInherited":false},"method":"GET","header":[{"key":"x-api-key","value":"{{x-api-key}}"}],"body":{"mode":"raw","raw":""},"url":"http://localhost:5000/api/app/timeline/device//by-date?date=2026-06-04","urlObject":{"path":["api","app","timeline","device","","by-date"],"host":["http://localhost:5000"],"query":[{"key":"date","value":"2026-06-04"}],"variable":[]}},"response":[],"_postman_id":"d31c0a36-3b6e-4444-841e-c70a0f601bbe"},{"name":"Get Trip Points","id":"cc94ccde-86f6-4106-8580-3721bb554fcb","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"auth":{"type":"inherit","inherit":{"basicConfig":[]},"isInherited":false},"method":"GET","header":[{"key":"x-api-key","value":"{{x-api-key}}"}],"body":{"mode":"raw","raw":""},"url":"http://localhost:5000/api/app/timeline/device//trip//points","urlObject":{"path":["api","app","timeline","device","","trip","","points"],"host":["http://localhost:5000"],"query":[],"variable":[]}},"response":[],"_postman_id":"cc94ccde-86f6-4106-8580-3721bb554fcb"},{"name":"Get Trip Events","id":"a779560b-4501-46e6-b972-7351fb56afa8","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"auth":{"type":"inherit","inherit":{"basicConfig":[]},"isInherited":false},"method":"GET","header":[{"key":"x-api-key","value":"{{x-api-key}}"}],"body":{"mode":"raw","raw":""},"url":"http://localhost:5000/api/app/timeline/device//trip//events","urlObject":{"path":["api","app","timeline","device","","trip","","events"],"host":["http://localhost:5000"],"query":[],"variable":[]}},"response":[],"_postman_id":"a779560b-4501-46e6-b972-7351fb56afa8"},{"name":"All Trips Playback","id":"21c184c9-76f0-4523-8ac9-b282720c75ab","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"auth":{"type":"inherit","inherit":{"basicConfig":[]},"isInherited":false},"method":"GET","header":[{"key":"x-api-key","value":"{{x-api-key}}"}],"body":{"mode":"raw","raw":""},"url":"http://localhost:5000/api/app/timeline/device//all-trips-playback?date=2026-06-04","urlObject":{"path":["api","app","timeline","device","","all-trips-playback"],"host":["http://localhost:5000"],"query":[{"key":"date","value":"2026-06-04"}],"variable":[]}},"response":[],"_postman_id":"21c184c9-76f0-4523-8ac9-b282720c75ab"},{"name":"Timeline List (legacy)","id":"c78e7f3a-fd6b-48d3-99d0-e87d0ea13f24","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"auth":{"type":"inherit","inherit":{"basicConfig":[]},"isInherited":false},"method":"POST","header":[{"key":"x-api-key","value":"{{x-api-key}}"},{"key":"Content-Type","value":"application/json"}],"body":{"mode":"raw","raw":"{}","options":{"raw":{"language":"json"}}},"url":"http://localhost:5000/api/app/timeline/timelineList/","urlObject":{"path":["api","app","timeline","timelineList",""],"host":["http://localhost:5000"],"query":[],"variable":[]}},"response":[],"_postman_id":"c78e7f3a-fd6b-48d3-99d0-e87d0ea13f24"},{"name":"Geofence Total","id":"4f03ae4b-644d-47a5-b611-dfec27576b93","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"auth":{"type":"inherit","inherit":{"basicConfig":[]},"isInherited":false},"method":"GET","header":[{"key":"x-api-key","value":"{{x-api-key}}"}],"body":{"mode":"raw","raw":""},"url":"http://localhost:5000/api/app/timeline/geofenceTotal/","urlObject":{"path":["api","app","timeline","geofenceTotal",""],"host":["http://localhost:5000"],"query":[],"variable":[]}},"response":[],"_postman_id":"4f03ae4b-644d-47a5-b611-dfec27576b93"}],"id":"d3f8e6d3-c0b1-4f98-aae6-75233c51df7b","description":"<p>All endpoints require deviceId. Yearly plan enforced for API key access.</p>\n","_postman_id":"d3f8e6d3-c0b1-4f98-aae6-75233c51df7b","auth":{"type":"bearer","bearer":{"basicConfig":[{"key":"token","value":""}]},"isInherited":true,"source":{"_postman_id":"126f45e0-caa0-4313-9034-0668fac77439","id":"126f45e0-caa0-4313-9034-0668fac77439","name":"ShadowGPS App API","type":"collection"}}},{"name":"Homepage","item":[{"name":"Get Homepage","id":"f0610b37-3a91-422b-a583-3fa88510cacd","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"auth":{"type":"inherit","inherit":{"basicConfig":[]},"isInherited":false},"method":"GET","header":[{"key":"x-api-key","value":"{{x-api-key}}"}],"body":{"mode":"raw","raw":""},"url":"http://localhost:5000/api/app/homepage/getHomepage/","urlObject":{"path":["api","app","homepage","getHomepage",""],"host":["http://localhost:5000"],"query":[],"variable":[]}},"response":[],"_postman_id":"f0610b37-3a91-422b-a583-3fa88510cacd"},{"name":"Battery Levels (Non-OBD)","id":"b8d894c4-955a-419a-8d53-cc5a99be9128","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"auth":{"type":"inherit","inherit":{"basicConfig":[]},"isInherited":false},"method":"GET","header":[{"key":"x-api-key","value":"{{x-api-key}}"}],"body":{"mode":"raw","raw":""},"url":"http://localhost:5000/api/app/homepage/getNonObdBatteryLevelsDetail/","urlObject":{"path":["api","app","homepage","getNonObdBatteryLevelsDetail",""],"host":["http://localhost:5000"],"query":[],"variable":[]}},"response":[],"_postman_id":"b8d894c4-955a-419a-8d53-cc5a99be9128"},{"name":"Fuel Levels (OBD)","id":"ae288ef4-dc8e-4b48-babd-b60bfa24d1db","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"auth":{"type":"inherit","inherit":{"basicConfig":[]},"isInherited":false},"method":"GET","header":[{"key":"x-api-key","value":"{{x-api-key}}"}],"body":{"mode":"raw","raw":""},"url":"http://localhost:5000/api/app/homepage/getObdFuelLevelsDetail/","urlObject":{"path":["api","app","homepage","getObdFuelLevelsDetail",""],"host":["http://localhost:5000"],"query":[],"variable":[]}},"response":[],"_postman_id":"ae288ef4-dc8e-4b48-babd-b60bfa24d1db"},{"name":"Create Location Sharing Link","id":"a5157aeb-8ce2-4427-be62-2760c9b4bcf8","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"auth":{"type":"inherit","inherit":{"basicConfig":[]},"isInherited":false},"method":"POST","header":[{"key":"x-api-key","value":"{{x-api-key}}"},{"key":"Content-Type","value":"application/json"}],"body":{"mode":"raw","raw":"{}","options":{"raw":{"language":"json"}}},"url":"http://localhost:5000/api/app/homepage/createLocationSharingLink/","urlObject":{"path":["api","app","homepage","createLocationSharingLink",""],"host":["http://localhost:5000"],"query":[],"variable":[]}},"response":[],"_postman_id":"a5157aeb-8ce2-4427-be62-2760c9b4bcf8"},{"name":"Update Share Device","id":"b664a2a7-103d-430f-8df6-51198f34d37f","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"auth":{"type":"inherit","inherit":{"basicConfig":[]},"isInherited":false},"method":"PUT","header":[{"key":"x-api-key","value":"{{x-api-key}}"},{"key":"Content-Type","value":"application/json"}],"body":{"mode":"raw","raw":"{}","options":{"raw":{"language":"json"}}},"url":"http://localhost:5000/api/app/homepage/updateShareDevice/","urlObject":{"path":["api","app","homepage","updateShareDevice",""],"host":["http://localhost:5000"],"query":[],"variable":[]}},"response":[],"_postman_id":"b664a2a7-103d-430f-8df6-51198f34d37f"},{"name":"Delete Share Device","id":"fd34da08-f650-4b6a-a688-27bfc454b1f5","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"auth":{"type":"inherit","inherit":{"basicConfig":[]},"isInherited":false},"method":"DELETE","header":[{"key":"x-api-key","value":"{{x-api-key}}"}],"body":{"mode":"raw","raw":""},"url":"http://localhost:5000/api/app/homepage/deleteShareDevice/","urlObject":{"path":["api","app","homepage","deleteShareDevice",""],"host":["http://localhost:5000"],"query":[],"variable":[]}},"response":[],"_postman_id":"fd34da08-f650-4b6a-a688-27bfc454b1f5"},{"name":"Update Address Request","id":"9b2878dc-32a6-4f31-92c0-7389f3edd6a0","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"auth":{"type":"inherit","inherit":{"basicConfig":[]},"isInherited":false},"method":"GET","header":[{"key":"x-api-key","value":"{{x-api-key}}"}],"body":{"mode":"raw","raw":""},"url":"http://localhost:5000/api/app/homepage/updateAddressReq//:refreshType","urlObject":{"path":["api","app","homepage","updateAddressReq","",":refreshType"],"host":["http://localhost:5000"],"query":[],"variable":[{"type":"any","key":"refreshType"}]}},"response":[],"_postman_id":"9b2878dc-32a6-4f31-92c0-7389f3edd6a0"}],"id":"93811b1a-62c4-4e01-84f9-36629b4645c8","description":"<p>All require deviceId. Yearly plan enforced for API key access.</p>\n","_postman_id":"93811b1a-62c4-4e01-84f9-36629b4645c8","auth":{"type":"bearer","bearer":{"basicConfig":[{"key":"token","value":""}]},"isInherited":true,"source":{"_postman_id":"126f45e0-caa0-4313-9034-0668fac77439","id":"126f45e0-caa0-4313-9034-0668fac77439","name":"ShadowGPS App API","type":"collection"}}},{"name":"Dashboard","item":[{"name":"Get Drive Data","id":"7e2aa9d2-e11a-4e03-962d-3cb260f3a921","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"auth":{"type":"inherit","inherit":{"basicConfig":[]},"isInherited":false},"method":"POST","header":[{"key":"x-api-key","value":"{{x-api-key}}"},{"key":"Content-Type","value":"application/json"}],"body":{"mode":"raw","raw":"{\n  \"deviceId\": \"\",\n  \"startDate\": \"2026-06-01\",\n  \"endDate\": \"2026-06-04\"\n}","options":{"raw":{"language":"json"}}},"url":"http://localhost:5000/api/app/dashboardApp/getDriveData","urlObject":{"path":["api","app","dashboardApp","getDriveData"],"host":["http://localhost:5000"],"query":[],"variable":[]}},"response":[],"_postman_id":"7e2aa9d2-e11a-4e03-962d-3cb260f3a921"},{"name":"Get Routes Data","id":"12a948f1-f216-4233-8b67-4de56418e903","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"auth":{"type":"inherit","inherit":{"basicConfig":[]},"isInherited":false},"method":"POST","header":[{"key":"x-api-key","value":"{{x-api-key}}"},{"key":"Content-Type","value":"application/json"}],"body":{"mode":"raw","raw":"{\n  \"deviceId\": \"\",\n  \"startDate\": \"2026-06-01\",\n  \"endDate\": \"2026-06-04\"\n}","options":{"raw":{"language":"json"}}},"url":"http://localhost:5000/api/app/dashboardApp/getRoutesData","urlObject":{"path":["api","app","dashboardApp","getRoutesData"],"host":["http://localhost:5000"],"query":[],"variable":[]}},"response":[],"_postman_id":"12a948f1-f216-4233-8b67-4de56418e903"},{"name":"Get Trip & Park History","id":"8ba78d6f-d7d0-4595-b23b-b93c2ee3235c","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"auth":{"type":"inherit","inherit":{"basicConfig":[]},"isInherited":false},"method":"POST","header":[{"key":"x-api-key","value":"{{x-api-key}}"},{"key":"Content-Type","value":"application/json"}],"body":{"mode":"raw","raw":"{\n  \"deviceId\": \"\",\n  \"startDate\": \"2026-06-01\",\n  \"endDate\": \"2026-06-04\"\n}","options":{"raw":{"language":"json"}}},"url":"http://localhost:5000/api/app/dashboardApp/getTripAndParkHistory","urlObject":{"path":["api","app","dashboardApp","getTripAndParkHistory"],"host":["http://localhost:5000"],"query":[],"variable":[]}},"response":[],"_postman_id":"8ba78d6f-d7d0-4595-b23b-b93c2ee3235c"}],"id":"873a0059-8595-4b55-87d4-6bf23eea4f47","description":"<p>Body must contain deviceId. Yearly plan enforced for API key access.</p>\n","_postman_id":"873a0059-8595-4b55-87d4-6bf23eea4f47","auth":{"type":"bearer","bearer":{"basicConfig":[{"key":"token","value":""}]},"isInherited":true,"source":{"_postman_id":"126f45e0-caa0-4313-9034-0668fac77439","id":"126f45e0-caa0-4313-9034-0668fac77439","name":"ShadowGPS App API","type":"collection"}}},{"name":"Live Tracking","item":[{"name":"Connect with API Key — Guide","id":"ad5e5883-39e6-48fb-a5c0-82773946d2e5","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"GET","header":[],"url":"http://localhost:5000","description":"<h1 id=\"connecting-via-api-key\">Connecting via API Key</h1>\n<p>Install the client library:</p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-bash\">npm install socket.io-client\n</code></pre>\n<p>Set the <code>apiKey</code> collection variable to your <code>sgps_...</code> key before running.</p>\n<hr />\n<h2 id=\"0-get-flespiid-from-device-list-api\">0. Get flespiId from Device List API</h2>\n<p><code>flespiId</code> is the Flespi device identifier. Fetch it from the device list before connecting the socket:</p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-js\">// GET http://localhost:5000/api/device/list\n// Header: x-api-key: sgps_...\n\nconst res = await fetch('http://localhost:5000/api/device/list', {\n  headers: { 'x-api-key': '{{apiKey}}' },\n});\nconst { devices } = await res.json();\n\n// Each device has a flespiId field:\n// [\n//   { deviceId: '...', name: 'My Car', flespiId: 6476889, ... },\n//   ...\n// ]\n\nconst flespiId = devices[0].flespiId;  // pick the device you want\n</code></pre>\n<blockquote>\n<p>No yearly plan required to call the device list. Yearly plan is only required to <strong>subscribe</strong> to live data.</p>\n</blockquote>\n<hr />\n<h2 id=\"1-connect\">1. Connect</h2>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-js\">const { io } = require('socket.io-client');\n\nconst socket = io('http://localhost:5000', {\n  auth: { apiKey: '{{apiKey}}' },\n  transports: ['websocket', 'polling'],\n  reconnection: false,\n});\n\nsocket.on('connect', () =&gt; {\n  console.log('Connected:', socket.id);\n});\n\nsocket.on('connect_error', (err) =&gt; {\n  // Fires when API key is invalid or missing\n  console.error('Auth failed:', err.message);\n});\n</code></pre>\n<h2 id=\"2-subscribe-to-a-device\">2. Subscribe to a Device</h2>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-js\">socket.on('connect', () =&gt; {\n  socket.emit('requestLiveTripData', { flespiId: {{flespiId}} });\n});\n\nsocket.on('liveTripStatus', ({ flespiId, subscribed, message }) =&gt; {\n  if (subscribed) {\n    console.log('Subscribed to device', flespiId);\n  } else {\n    console.error('Failed:', message);\n  }\n});\n</code></pre>\n<blockquote>\n<p><code>flespiId</code> must be a <strong>number</strong> (not a string), minimum 5 digits.</p>\n</blockquote>\n<h2 id=\"3-receive-live-position\">3. Receive Live Position</h2>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-js\">socket.on('livePosition', (pos) =&gt; {\n  // Broadcast every ~5 s while subscribed\n  console.log(pos);\n  // { flespiId, lat, lon, speed, heading, timestamp, ... }\n});\n</code></pre>\n<h2 id=\"4-unsubscribe\">4. Unsubscribe</h2>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-js\">socket.emit('unsubscribeLiveTripData', { flespiId: {{flespiId}} });\n// liveTripStatus fires with subscribed: false to confirm\n</code></pre>\n<h2 id=\"5-disconnect\">5. Disconnect</h2>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-js\">socket.disconnect();\n</code></pre>\n<hr />\n<h2 id=\"full-example-fetch-flespiid-then-connect\">Full Example (fetch flespiId then connect)</h2>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-js\">const { io } = require('socket.io-client');\n\nconst API_KEY  = '{{apiKey}}';\nconst BASE_URL = 'http://localhost:5000';\n\nasync function main() {\n  // Step 0 — get flespiId from device list\n  const res = await fetch(`${BASE_URL}/api/device/list`, {\n    headers: { 'x-api-key': API_KEY },\n  });\n  const { devices } = await res.json();\n\n  if (!devices || devices.length === 0) {\n    console.error('No devices found for this API key');\n    return;\n  }\n\n  const flespiId = devices[0].flespiId;  // pick your target device\n  console.log('Targeting device flespiId:', flespiId);\n\n  // Step 1 — connect socket\n  const socket = io(BASE_URL, {\n    auth: { apiKey: API_KEY },\n    transports: ['websocket', 'polling'],\n    reconnection: false,\n  });\n\n  socket.on('connect', () =&gt; {\n    console.log('Connected:', socket.id);\n    // Step 2 — subscribe\n    socket.emit('requestLiveTripData', { flespiId });\n  });\n\n  socket.on('liveTripStatus', ({ flespiId, subscribed, message }) =&gt; {\n    console.log(subscribed ? `Subscribed to ${flespiId}` : `Error: ${message}`);\n  });\n\n  socket.on('livePosition', (pos) =&gt; {\n    console.log(`[${pos.timestamp}] lat=${pos.lat} lon=${pos.lon} speed=${pos.speed}`);\n  });\n\n  socket.on('unreadCount', (count) =&gt; {\n    console.log('Unread notifications:', count);\n  });\n\n  socket.on('connect_error', (err) =&gt; {\n    console.error('Auth failed:', err.message);\n  });\n\n  socket.on('disconnect', (reason) =&gt; {\n    console.log('Disconnected:', reason);\n  });\n}\n\nmain();\n</code></pre>\n","auth":{"type":"bearer","bearer":{"basicConfig":[{"key":"token","value":""}]},"isInherited":true,"source":{"_postman_id":"126f45e0-caa0-4313-9034-0668fac77439","id":"126f45e0-caa0-4313-9034-0668fac77439","name":"ShadowGPS App API","type":"collection"}},"urlObject":{"host":["http://localhost:5000"],"query":[],"variable":[]}},"response":[],"_postman_id":"ad5e5883-39e6-48fb-a5c0-82773946d2e5"},{"name":"Connect with JWT — Guide","id":"4efbfbc5-b8f9-409c-9f92-7b9a8e57980a","protocolProfileBehavior":{"disableBodyPruning":true},"request":{"method":"GET","header":[],"url":"http://localhost:5000","description":"<h1 id=\"connecting-via-jwt\">Connecting via JWT</h1>\n<p>For mobile apps and web clients that already have a JWT from the login flow.</p>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-js\">const { io } = require('socket.io-client');\n\nconst socket = io('http://localhost:5000', {\n  auth: {\n    token:    '',\n    mobileId: '&lt;device-uuid&gt;',\n  },\n  transports: ['websocket', 'polling'],\n  reconnection: false,\n});\n\nsocket.on('connect', () =&gt; {\n  socket.emit('requestLiveTripData', { flespiId: {{flespiId}} });\n});\n\nsocket.on('liveTripStatus', ({ flespiId, subscribed, message }) =&gt; {\n  console.log(subscribed ? `Subscribed to ${flespiId}` : `Error: ${message}`);\n});\n\nsocket.on('livePosition', (pos) =&gt; {\n  console.log(pos);\n});\n\nsocket.on('connect_error', (err) =&gt; {\n  console.error('Auth failed:', err.message);\n});\n</code></pre>\n<blockquote>\n<p>JWT flow does not require a yearly subscription — subscription is checked against the user's plan.</p>\n</blockquote>\n","auth":{"type":"bearer","bearer":{"basicConfig":[{"key":"token","value":""}]},"isInherited":true,"source":{"_postman_id":"126f45e0-caa0-4313-9034-0668fac77439","id":"126f45e0-caa0-4313-9034-0668fac77439","name":"ShadowGPS App API","type":"collection"}},"urlObject":{"host":["http://localhost:5000"],"query":[],"variable":[]}},"response":[],"_postman_id":"4efbfbc5-b8f9-409c-9f92-7b9a8e57980a"}],"id":"0acbe1ff-67c1-453a-98bc-3360a00ae566","description":"<h1 id=\"socketio-real-time-connection\">Socket.IO Real-time Connection</h1>\n<p>Socket server runs on the same host as the REST API — connect directly to <code>http://localhost:5000</code>.</p>\n<blockquote>\n<p>Socket.IO is not HTTP. These items are documentation guides — use <code>socket.io-client</code> in your code, not Postman's HTTP runner.</p>\n</blockquote>\n<hr />\n<h2 id=\"authentication\">Authentication</h2>\n<p>Pass credentials in the <code>auth</code> object when calling <code>io(url, { auth })</code>. Do <strong>not</strong> use HTTP headers.</p>\n<h3 id=\"api-key\">API Key</h3>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-json\">{ \"apiKey\": \"sgps_...\" }\n</code></pre>\n<blockquote>\n<p><strong>Yearly plan required</strong> — API key connections can only subscribe to devices with an active, trialing, or grace-period yearly subscription.</p>\n</blockquote>\n<h3 id=\"jwt-mobile--web\">JWT (mobile / web)</h3>\n<pre class=\"click-to-expand-wrapper is-snippet-wrapper\"><code class=\"language-json\">{ \"token\": \"&lt;JWT&gt;\", \"mobileId\": \"&lt;device-uuid&gt;\" }\n</code></pre>\n<hr />\n<h2 id=\"client-events-emit\">Client Events (emit)</h2>\n<div class=\"click-to-expand-wrapper is-table-wrapper\"><table>\n<thead>\n<tr>\n<th>Event</th>\n<th>Payload</th>\n<th>Description</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>requestLiveTripData</code></td>\n<td><code>{ flespiId: number }</code></td>\n<td>Subscribe to live position + trip updates. <code>flespiId</code> must be a number ≥ 5 digits.</td>\n</tr>\n<tr>\n<td><code>unsubscribeLiveTripData</code></td>\n<td><code>{ flespiId: number }</code></td>\n<td>Stop receiving updates for a device.</td>\n</tr>\n<tr>\n<td><code>requestNotificationCount</code></td>\n<td><em>(none)</em></td>\n<td>Request current unread notification count.</td>\n</tr>\n</tbody>\n</table>\n</div><h2 id=\"server-events-listen\">Server Events (listen)</h2>\n<div class=\"click-to-expand-wrapper is-table-wrapper\"><table>\n<thead>\n<tr>\n<th>Event</th>\n<th>Payload</th>\n<th>Description</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>liveTripStatus</code></td>\n<td><code>{ flespiId, subscribed: bool, message: string }</code></td>\n<td>Result of subscribe / unsubscribe. <code>subscribed: false</code> means failure — check <code>message</code>.</td>\n</tr>\n<tr>\n<td><code>livePosition</code></td>\n<td>position object</td>\n<td>Broadcast every ~5 s while subscribed. Contains GPS coords, speed, heading, timestamps.</td>\n</tr>\n<tr>\n<td><code>unreadCount</code></td>\n<td><code>number</code></td>\n<td>Sent on connect and whenever notification count changes.</td>\n</tr>\n<tr>\n<td><code>error</code></td>\n<td><code>{ message: string }</code></td>\n<td>Server-side error (auth failure, internal error).</td>\n</tr>\n</tbody>\n</table>\n</div><hr />\n<h2 id=\"error-reference\">Error reference</h2>\n<div class=\"click-to-expand-wrapper is-table-wrapper\"><table>\n<thead>\n<tr>\n<th><code>liveTripStatus.message</code></th>\n<th>Cause</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>flespiId is required and must be a number</code></td>\n<td>Payload missing or wrong type</td>\n</tr>\n<tr>\n<td><code>Device not found</code></td>\n<td>No device with that flespiId exists</td>\n</tr>\n<tr>\n<td><code>Unauthorized access to device</code></td>\n<td>API key owner does not own/share this device</td>\n</tr>\n<tr>\n<td><code>This device requires an active yearly subscription for API key access.</code></td>\n<td>No active/trialing/grace yearly plan</td>\n</tr>\n<tr>\n<td><code>Device subscription has expired</code></td>\n<td>Subscription canceled or deleted (JWT flow)</td>\n</tr>\n</tbody>\n</table>\n</div>","_postman_id":"0acbe1ff-67c1-453a-98bc-3360a00ae566","auth":{"type":"bearer","bearer":{"basicConfig":[{"key":"token","value":""}]},"isInherited":true,"source":{"_postman_id":"126f45e0-caa0-4313-9034-0668fac77439","id":"126f45e0-caa0-4313-9034-0668fac77439","name":"ShadowGPS App API","type":"collection"}}}],"auth":{"type":"bearer","bearer":{"basicConfig":[{"key":"token","value":""}]}},"event":[{"listen":"prerequest","script":{"id":"c91581e7-4b80-44b1-b65d-99520b06101b","type":"text/javascript","packages":{},"requests":{},"exec":[""]}},{"listen":"test","script":{"id":"a22ef50a-606b-404d-84c8-28d29f2bf03d","type":"text/javascript","packages":{},"requests":{},"exec":[""]}}],"variable":[{"key":"baseUrl","value":"http://localhost:5000"},{"key":"token","value":"","description":"JWT — paste after login"},{"key":"x_api_key","value":"","description":"API Key (sgps_...) — add x-api-key header instead of Bearer when using this"},{"key":"deviceId","value":""},{"key":"intervalId","value":""}]}