Technical requirements for building the EXE integration
/scan on the assessment computer. The page calls createScanSession, which mints a device_session_id and a short session_code (e.g. SCAN-A1B2), and renders a QR of /authorize-scan?session=<device_session_id>.paired, and issues a one-time upload_key./scan page is polling and shows "Authorized for <client>". The EXE now claims the session and uploads (below).The EXE needs the session_code shown on the /scan page. Easiest path: the packaged EXE is launched by the /scan page (custom URL protocol handler or a small downloaded launcher file that carries the code), OR the tech copies/pastes the session_code into the EXE once. This is the only value the EXE ever needs.
Only works after the tech has authorized on their phone (status = paired):
// Request
{ "session_code": "SCAN-A1B2" }
// Response (200) — bound to the exact client the phone authorized
{
"success": true,
"upload_key": "…one-time key…",
"customer_id": "…",
"project_id": "…"
}
// 403 if not yet authorized / expired — poll every few seconds until 200The customer_id/project_id are resolved server-side from the session the phone authorized — the EXE never picks the client and can't upload to the wrong one.
Header Authorization: Bearer <upload_key>, body { "assessment_data": { … } } (full schema in the section below).
On a successful upload the server burns the upload_key, sets the session to assessment_uploaded, and stamps expired_at. The /scan page detects this and shows "Upload complete — session closed", so the session on that computer can't be reused. The EXE should discard the key from memory after a 200.
The browser's /scan page holds the livedevice_session_id / session_code. When the EXE launches on this computer it must pick up that same session ID (via the launcher file / URL protocol that opened it, or the pastedsession_code), confirm the session is paired, and bind every scan it runs to that one session so results land on the correct prospect.
On launch the EXE presents a menu of the four scan types. The tech can run any of them; results are uploaded per-type and merged into this session's assessment.
The EXE is generic — never recompiled per prospect. Right after pairing, it calls the checklist endpoint with the upload_key it just received. The server resolves the prospect and returns which of the four scan types already have data. The EXE then checks & disables the completed ones and pre-checks the outstanding ones. Re-call it after each upload to refresh the boxes live.
// Request (Authorization: Bearer <upload_key>, or in body)
{ "upload_key": "…the key from validatePairing…" }
// Response (200)
{
"success": true,
"scan_completion": { "workstation": true, "network": false, "wifi": false, "external": false },
"scans": [
{ "key": "workstation", "label": "Workstation Scan", "completed": true },
{ "key": "network", "label": "Network Scan", "completed": false },
{ "key": "wifi", "label": "WiFi Analysis", "completed": false },
{ "key": "external", "label": "External Scan", "completed": false }
],
"outstanding": ["network", "wifi", "external"]
}completed: true → render checked & disabled.completed: false → render pre-checked so the tech runs it. EachcompleteAssessmentUpload flips its type to complete, so the next call reflects it — across all machines and sites.
location_label so results are grouped by site.XXX-XX-XXXX pattern (and the 9-digit unformatted form), excluding known-invalid ranges (000, 666, 900–999 area; 00 group; 0000 serial)..txt .csv .log .rtf .doc/.docx .xls/.xlsx .pdf .json .xml .html. Skip binaries, media, and system/program directories.assessment_dataAdd a pii_scan object to the normalcompleteAssessmentUpload payload. No new endpoint — same session, same upload key.
{
"assessment_data": {
// ...other scan sections...
"pii_scan": {
"files_scanned": 48213,
"credit_card_matches": 12,
"ssn_matches": 5,
"files_with_findings": [
{ "path": "C:\\Users\\jdoe\\Desktop\\customers.csv", "credit_card_matches": 9, "ssn_matches": 0 },
{ "path": "C:\\Shared\\HR\\employees.xlsx", "credit_card_matches": 0, "ssn_matches": 5 },
{ "path": "C:\\Temp\\export.txt", "credit_card_matches": 3, "ssn_matches": 0 }
]
// NOTE: never include the matched values themselves — counts + paths only
}
}
}files_with_findings may also be a plain array of path strings if you don't want per-file counts. The platform tallies the counts, lists the files, and computes the hard/soft breach-cost exposure automatically.
The technician generates a short code in the dashboard and types it into the tool. The tool exchanges it for a one-time upload_key via the validate endpoint:
// Request
{ "session_code": "MSP-A1B2" }
// Response (200)
{
"success": true,
"upload_key": "…long random one-time key…",
"customer_id": "…",
"project_id": "…"
}Keep the upload_key in volatile memory only — never write it to disk or registry.
Gather and structure this data during assessment:
When assessment completes, POST the data here:
Headers: Authorization: Bearer <upload_key>
Request Body (JSON):
{
"assessment_data": {
"hardware_inventory": [...],
"software_inventory": [...],
"network_info": {...},
"security_posture": {...},
"disk_space": {...},
"custom_data": {...}
}
}The customer_id and project_id are resolved server-side from the upload key — the tool never needs to know them.
upload_key in the Authorization: Bearer header. The key is issued only after a valid pairing, is bound to a single client's assessment, and is burned after the upload succeeds.Handle these HTTP responses:
// Sent with header: Authorization: Bearer <upload_key>
{
"assessment_data": {
"hardware_inventory": [
{"component": "CPU", "details": "Intel Core i7-9700K"},
{"component": "RAM", "details": "32GB DDR4"}
],
"software_inventory": [
{"name": "Microsoft Office 365", "version": "2024"},
{"name": "Google Chrome", "version": "120.0.0.0"}
],
"network_info": {
"ip_address": "192.168.1.100",
"gateway": "192.168.1.1",
"dns": ["8.8.8.8", "8.8.4.4"]
},
"security_posture": {
"antivirus": "Windows Defender (active)",
"firewall": "enabled"
},
"disk_space": {
"C": {"total_gb": 500, "used_gb": 250, "free_gb": 250}
},
"external_ip_addresses": [
{"ip_address": "203.0.113.45", "ip_type": "ipv4", "description": "Primary WAN"},
{"ip_address": "203.0.113.46", "ip_type": "ipv4", "description": "Backup WAN"}
],
"custom_data": {
"notes": "System running smoothly, no issues detected"
}
}
}MSPOnboard.Scanner.exe on the target computer.Questions? Contact your MSP administrator for support.