Sub-Guides:
- Exhaustive Express Route Inventory — The complete list of all route mounts, modules, and classifications.
- Manual-Only Safety Guide — Dangerous/destructive operations and test automation rules.
- Wiki version (diagrams): API Reference
This document serves as the curated, operator-facing REST API reference for the ShadowCheck platform. For a complete mapping of all developer routes, see the route inventory linked above.
http://localhost:3001/api
Protected endpoints require authentication. Two methods are supported:
Sessions are managed via HTTP-only cookies. After a successful POST /api/auth/login the server sets an HTTP-only session_token cookie. The client must pass credentials: 'include' on every subsequent request.
Authorization: Bearer <token>Pass the token returned by POST /api/auth/login in the Authorization header for non-browser clients.
Note:
x-api-keyheader authentication is not implemented. The middleware (authMiddleware.ts) accepts only thesession_tokencookie andAuthorization: Bearerheader.
- 1000 requests per 15 minutes per IP
- Returns
429 Too Many Requestswhen exceeded
Public GeoJSON endpoints for geospatial visualization. Note: /agency-offices and /federal-courthouses are mounted at the root level to bypass standard API auth for map display. The ALPR/ShotSpotter layers require standard user authentication.
Returns a GeoJSON FeatureCollection of all FBI Field Offices and Resident Agencies.
Response:
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": { "type": "Point", "coordinates": [-83.0458, 42.3314] },
"properties": {
"name": "Detroit Field Office",
"office_type": "field_office",
"address": "477 Michigan Ave, Detroit, MI 48226"
}
}
]
}Returns public, read-only agency office counts grouped by office type.
Returns a GeoJSON FeatureCollection of all Federal Courthouses.
Returns a GeoJSON FeatureCollection of Flock Safety ALPR (Automatic License Plate Reader) camera locations.
Returns a GeoJSON FeatureCollection of acoustic gunshot detection coverage zones.
Returns a GeoJSON FeatureCollection of ShotSpotter acoustic sensor points (from the WIRED 2024 leaked dataset).
Platform statistics (canonical v1 route).
Response:
{
"totalNetworks": 173326,
"threatsCount": 1842,
"surveillanceCount": 256,
"enrichedCount": 45123
}Note:
GET /api/dashboard-metricswas a legacy alias for this route and has been removed. Use/api/dashboard/metrics(v1) or/api/v2/dashboard/metrics(v2).
Returns the current dashboard threat list. Requires an authenticated user.
Returns summary counts for networks, threats, critical threats, and active surveillance. Requires an authenticated user.
Dashboard statistics (v2).
Fast paginated threat detection.
Parameters:
page(int, default: 1)limit(int, default: 100, max: 5000)minSeverity(int, default: 40, range: 0-100)
Response:
{
"threats": [
{
"bssid": "AA:BB:CC:DD:EE:FF",
"ssid": "Hidden Network",
"type": "W",
"threat_score": 75,
"distance_range_km": 2.5,
"observation_count": 45,
"unique_days": 8,
"seen_at_home": true,
"seen_away_from_home": true,
"max_speed_kmh": 65,
"manufacturer": "Apple Inc."
}
],
"pagination": {
"page": 1,
"total": 1842,
"totalPages": 19
}
}Advanced threat detection with speed calculations.
Generate a threat report for one network in JSON, Markdown, HTML, or PDF format.
Threat data optimized for map display.
Threat counts by severity level.
List networks with pagination and filtering.
Parameters:
page(int, default: 1) - Page numberlimit(int, default: 100, max: 5000) - Results per pagesort(string) - Sort field (bssid, ssid, last_seen, threat_score, etc.)order(string) - Sort order (ASC, DESC)location_mode(string) - Data source mode:latest_observation- Uses latest observation data (recommended)aggregated- Uses materialized view aggregated data
distance_from_home(float) - Filter by distance from home location
Response:
{
"networks": [
{
"bssid": "AA:BB:CC:DD:EE:FF",
"ssid": "MyNetwork",
"type": "W",
"signal": -45,
"frequency": 2437,
"channel": 6,
"manufacturer": "Apple Inc.",
"max_distance_meters": 1250.5,
"threat_score": 25,
"last_seen": "2026-01-30T06:30:19.059Z"
}
],
"pagination": {
"page": 1,
"total": 173326,
"totalPages": 1734
}
}Note: The API now uses latest observation data by default for accurate real-time information. Manufacturer fields are populated via OUI prefix matching from BSSID MAC addresses.
Get all observations for a network.
Response: observations[].time is a JavaScript-safe numeric epoch timestamp in
milliseconds.
Search by SSID.
List tagged networks.
Tag a network (requires authentication).
Request:
{
"threat_tag": "LEGIT",
"threat_confidence": 0.95,
"notes": "Home router"
}Tag Types:
LEGIT: SafeFALSE_POSITIVE: Incorrectly flaggedINVESTIGATE: Needs reviewTHREAT: Confirmed threat
Remove tag.
Lookup manufacturer from MAC OUI.
Tag multiple networks as threats.
Retrieves the nearest law enforcement agency offices for a specific network observation point.
Batch retrieves nearest agencies for multiple BSSIDs.
Request:
{
"bssids": ["00:11:22:33:44:55"]
}Batch retrieves nearest federal courthouses for multiple BSSIDs.
Get notes for a network (user-facing view).
Add a new note to a network.
Update an existing note for a network. Requires admin privileges.
Delete a note by its ID. Requires admin privileges.
List networks with pagination.
Get specific network details.
Filtered network list with universal filter support. Powers the Geospatial Explorer and filtered table views.
Parameters:
page(int, default: 1)limit(int, default: 100, max: 5000)sort(string) - Field to sort by (ssid, bssid, observed_at, threat_score, etc.)order(string) - Sort direction (ASC, DESC)filters(JSON string) - Universal filter payload (see below)enabled(JSON string) - Map of which filters are active
Universal Filter Payload Structure:
{
"filters": {
"ssid": "Target SSID",
"bssid": "00:11:22:*",
"threatLevel": ["HIGH", "CRITICAL"],
"timeframe": {
"scope": "LAST_SEEN",
"relativeWindow": "30d"
},
"wigle_v3_observation_count_min": 10,
"geocodedCity": "Detroit"
},
"enabled": {
"ssid": true,
"threatLevel": true,
"timeframe": true
}
}Response:
{
"ok": true,
"data": [...],
"pagination": { "page": 1, "total": 173326, "totalPages": 1734 },
"filters": { "applied": [...], "ignored": [...], "warnings": [...] }
}Filtered networks optimized for geospatial display (GeoJSON-like points).
Parameters:
- Same as
/api/v2/networks/filtered bbox(string) - Bounding box filter "minLon,minLat,maxLon,maxLat"
Filtered observations with network context. Returns high-volume observation stream for heatmaps and routes.
Aggregated analytics (counts, averages) derived from the current filter set.
Get tags for a network.
Add tag to network.
Request:
{
"threat_tag": "THREAT",
"threat_confidence": 0.9,
"notes": "Suspicious activity"
}Mark as false positive.
Mark as confirmed threat.
Update notes.
Mark for investigation.
Remove tag.
List all tagged networks.
Export tags for ML training.
List explorer networks (legacy endpoint).
Enhanced explorer with additional geocoding and physical-measurement fields, compiled from the database explorer materialized view.
Retrieves the complete, geocoded materialized view record for a single network. Alias fields (first_observed_at, last_observed_at) are provided to match standard geospatial payloads.
Network type distribution.
Signal strength histogram.
Hourly observation patterns over time.
Parameters:
range:24h,7d,30d,90d,all(default:all)
Response:
{
"data": [
{
"hour": 0,
"observations": 1250
}
]
}Network types distribution over time periods.
Parameters:
range:24h,7d,30d,90d,all(default:all)
Response:
{
"data": [
{
"period": "2026-01-29",
"wifi": 1500,
"bluetooth": 250,
"cellular": 100
}
]
}Threat score trends over time.
Parameters:
range:24h,7d,30d,90d,all(default:all)
Response:
{
"data": [
{
"period": "2026-01-29",
"avg_threat_score": 35.2,
"threat_count": 45
}
]
}Top networks by observation count.
Parameters:
limit(int, default: 10, max: 100) - Number of results
Response:
{
"data": [
{
"bssid": "AA:BB:CC:DD:EE:FF",
"ssid": "Popular Network",
"observations": 2500
}
]
}Security analysis metrics.
Dashboard analytics.
Bulk analytics data.
Threat distribution analysis.
Note: All analytics endpoints now properly handle null values and use appropriate data sources (materialized views for aggregated data, observations table for temporal data).
Filtered analytics (public endpoint).
Train threat detection model.
Model training status.
Score all networks.
Get ML scores for a network.
Get networks by score level.
Get all markers.
Get home location.
Set home location.
Remove home marker.
Get current home location.
Get the current home location and radius for the admin panel. Requires admin access.
Set home location and radius.
Visual Intelligence (VISINT) endpoints correlate uploaded field images against local
observations. Both endpoints accept multipart/form-data. Full pipeline documentation
(EXIF extraction, scoring, tag derivation, safety contract) is in
docs/features/visint-evidence-pipeline.md.
Safety: Both endpoints are marked
manualOnly: trueinapiTestEndpoints.ts. Automated test runners must not call them against the working database.
Auto-correlates an uploaded image against app.observations via PostGIS spatial-temporal
query. Defaults to preview mode — pass commit=true to persist.
Request: multipart/form-data
| Field | Required | Notes |
|---|---|---|
image |
✅ | JPEG or PNG, max 25 MB |
commit |
No | "true" to persist. Default: "false" (preview only). |
radius_meters |
No | Spatial radius (default 50 m) |
window_hours |
No | Time window ± (default 2 h) |
limit |
No | Max candidates (default 5) |
Response (preview):
{
"ok": true,
"status": "MATCHED",
"observation_id": "1234",
"detection_score": 2,
"dist_meters": 12.5,
"delta_minutes": 4.3,
"tags_applied": ["SHOTSPOTTER_SENSOR", "VISINT_VERIFIED"],
"exif": { "lat": 40.712, "lon": -74.006, "ts": "2026-06-01 14:23:00-05:00" },
"candidates": [...]
}Error codes: ExifMissingError (400), ExifToolUnavailableError (503),
VISINT_INVALID_NUMERIC_PARAMS (400), payload too large (413).
Commits a VISINT image to a specific operator-selected BSSID. Always writes to
app.network_media and app.network_tags. Requires confirm_fallback=true when
targeting the VISINT_UNMATCHED sentinel.
Request: multipart/form-data
| Field | Required | Notes |
|---|---|---|
image |
✅ | JPEG or PNG, max 25 MB |
bssid |
No | Target BSSID. Defaults to VISINT_UNMATCHED. |
detection_score |
No | Score from correlate response |
manual_override |
No | "true" for ground-truth evidence path |
device_type |
No | SHOTSPOTTER_SENSOR or FLOCK_SAFETY_CAMERA |
confirm_fallback |
No | Required when bssid=VISINT_UNMATCHED |
Response:
{ "ok": true, "success": true, "tags_applied": ["VISINT_CONFIRMED", "GROUND_TRUTH_IMAGE"] }Get WiGLE observation data for a specific network.
Response:
{
"bssid": "AA:BB:CC:DD:EE:FF",
"observations": [
{
"lat": 40.7128,
"lon": -74.006,
"accuracy": 10,
"timestamp": "2026-01-30T06:30:19.059Z"
}
],
"stats": {
"total": 15,
"accuracy_avg": 12.5
}
}Batch fetch WiGLE observations for multiple networks.
Request:
{
"bssids": ["AA:BB:CC:DD:EE:FF", "11:22:33:44:55:66"]
}Response:
{
"results": [
{
"bssid": "AA:BB:CC:DD:EE:FF",
"observations": [...],
"stats": {...}
}
]
}Check WiGLE API connectivity and status.
Response:
{
"status": "connected",
"api_key_valid": true,
"rate_limit_remaining": 95
}Retrieve cached WiGLE account statistics and rank.
Live WiGLE data for a BSSID.
Local WiGLE database lookup.
Search WiGLE database.
Fetch WiGLE v2 networks for map testing.
Fetch WiGLE v3 networks for map testing. Forensic Note: Results are automatically enriched with local threat scores, geocoding, and capture metrics when a BSSID match exists in the local database.
Returns filtered, paginated KML point data for map display. Requires an authenticated user.
Retrieve aggregate KML observation statistics for one BSSID.
Search the remote WiGLE API with query parameters. This admin endpoint is manual-only on the API Test Page.
Search WiGLE API directly.
Get WiGLE detail for network. Forensic Note: Returns enriched local forensic data if the network is present in the local database.
Get Bluetooth detail.
Import WiGLE v3 data into local tables (app.wigle_v3_observations, app.wigle_v3_network_details).
Get WiGLE observations for network.
Parameters:
limit(int, optional, max: 100000) — Number of observations to returnoffset(int, optional, max: 10000000) — Pagination offset
Response:
{
"ok": true,
"count": 15,
"total": 42,
"observations": [...]
}Note: WiGLE observations now use the correct 'app' schema namespace instead of 'public'.
Return the current WiGLE request-ledger quota status (daily call counts, remaining budget, reset time). Requires admin role.
Response:
{
"ok": true,
"quota": {
"used": 42,
"limit": 500,
"resetAt": "2026-04-29T00:00:00.000Z"
}
}List WiGLE request-ledger events and import runs with cursor pagination.
List and query WiGLE V2 search import runs. Requires admin role.
Parameters:
limit(int, default: 20)offset(int, default: 0)status(string, optional) — Filter by state:running,paused,failed,completed,cancelledstate(string, optional) — Filter by US state code (e.g.,VA)searchTerm(string, optional) — Filter search query termsincompleteOnly(boolean, optional) — Only show active/failed runssortBy(string, optional) — Sort columnsortDir(string, optional) — Sort direction (asc|desc)
Retrieve import-run completeness reporting by search term and state.
Retrieve one WiGLE import run by ID.
Retrieve the latest resumable WiGLE import run matching the query.
Resume a paused or failed WiGLE import run from its last saved cursor checkpoint. Requires admin role.
Pause a running WiGLE import run at the next page iteration boundary. Requires admin role.
Permanently cancel/stop an import run. Requires admin role.
List saved SSID search terms used by WiGLE imports.
Update soft limits in the running server process dynamically. Requires admin role.
Body:
{
"search": 75,
"detail": 200,
"stats": 50
}Local database lookup returning the full enriched network record used by the WiGLE page detail panel. Tries the materialized view first, falls back to a live four-query fan-out if the MV is unavailable or returns no row.
Parameters:
:netid(path, required) — BSSID / network ID (MAC address format validated bymacParamMiddleware)
Response: Enriched network object, or 404 if not found in the local WiGLE database.
Get statistics on the number of networks that need WiGLE database enrichment. Requires admin role.
Get a list of recent enrichment runs and execution history. Requires admin role.
Start a new WiGLE v3 offline database enrichment batch run. Requires admin role.
Resume a paused or failed enrichment run by ID. Requires admin role.
Force clear an enrichment run state to allow starting new runs. Requires admin role.
Get data for Kepler.gl visualization.
Get observations layer data.
Get networks layer data.
Get the currently configured Mapbox access token.
Get the styled Mapbox layer configurations (e.g. satellite background).
Proxies style asset requests directly to Mapbox API.
Get the currently configured Google Maps API token.
Proxies Google Maps tiles to bypass browser CORS gates.
Lookup manufacturer from MAC OUI.
Check for duplicate observations.
Geocode an address.
Import WiGLE data.
Data quality metrics.
Legacy root-mounted alias for data quality metrics.
System health check.
Response:
{
"status": "healthy",
"checks": {
"database": "ok",
"memory": "ok"
}
}Remove duplicate observations.
Refresh colocation data.
Run the surveillance-detection pipeline in dry-run (preview) mode. No data is persisted when dry-run=true.
Set the active state for a user (activate/deactivate account). Request body: { "active": true|false }.
Force a user password reset. Admins may set a temporary password or trigger a reset email.
Trigger an immediate WiGLE KML synchronization job (admin-only).
Change the current authenticated user's password. Requires current password confirmation.
Return demo context-menu payload used by UI prototypes (non-production/demo use).
List networks associated with the given manufacturer's OUI (useful for vendor grouping and analytics).
Serve a stored media file (image/video) by filename.
Remove a configured Mapbox token identified by :label from the runtime config.
Update Smarty (address verification) integration settings (admin-only).
Add a free-form administrative note to the system audit log (admin-only). Useful for tagging runs, import notes, or operator annotations.
Import a SQLite backup file into the canonical observation pipeline.
Behavior:
- records the run in
app.import_history - optionally takes a pre-import PostgreSQL backup
- imports observations into
app.observations - preserves parent-only network rows in
app.networks_orphans - leaves canonical
app.networksobservation-backed only
Admin UI:
Admin -> Data Import -> Import SQLite
Import KML files (WiGLE/KML) into the observations pipeline and summarize imported BSSIDs.
Run a raw SQL import script into the staging schema (admin-only, use with caution).
Start processing of a previously uploaded mobile capture (by uploadId) into the ETL pipeline.
Related endpoints:
GET /api/admin/import-historyGET /api/admin/device-sourcesGET /api/admin/orphan-networksPOST /api/admin/orphan-networks/:bssid/check-wigle
Retrieve a demo OUI grouping visualization used by the admin demo pages (non-production).
Fetch detection evidence for a specific network BSSID, including observations and scoring factors used by the detection pipeline.
List secret keys currently known to the Secrets Manager integration (admin-only).
Remove a secret entry by key from the runtime Secrets Manager cache. Use with caution.
Create or update a secret key in the runtime cache (does not persist to AWS unless configured).
Trigger a named background job immediately (admin-only). :jobName is the registered job identifier.
Perform local-stack actions for development (e.g., start, stop, restart) — admin-only and intended for safe, non-production environments.
Cancel a running sibling-detection background job.
Retrieve the sibling component (connected graph) for a single BSSID.
Bulk delete sibling pairs using request criteria (destructive; admin-only).
List recent administrative data import runs.
List configured observation device sources.
List local KML import files and their processing status.
Retrieve WiGLE KML sync readiness and local KML import totals.
List remote WiGLE KML upload transactions.
List preserved orphan network rows from app.networks_orphans plus backfill status from app.orphan_network_backfills.
Perform a lightweight WiGLE check for a single orphan row.
Behavior:
- on match, imports into:
app.wigle_v3_network_detailsapp.wigle_v3_observations
- on miss, records
no_wigle_matchinapp.orphan_network_backfills - does not automatically promote data into canonical
app.networks
Get complete network summary.
Retrieve database table, storage, and activity statistics.
Test admin routes.
Set or override the sibling relationship between two networks.
Request:
{
"bssidA": "AA:BB:CC:DD:EE:FF",
"bssidB": "11:22:33:44:55:66",
"relation": "sibling",
"notes": "Same AP, sequential MACs"
}bssidA,bssidB(string, required) — MAC addresses to pair; must be differentrelation(string) —"sibling"(default) or"not_sibling"notes(string, optional) — Free-text annotation
Response:
{
"ok": true,
"pair": {
"bssidA": "AA:BB:CC:DD:EE:FF",
"bssidB": "11:22:33:44:55:66",
"relation": "sibling"
}
}Retrieve all known sibling links for a single BSSID.
Parameters:
:bssid(path, required) — MAC address to look up
Response:
{
"ok": true,
"bssid": "AA:BB:CC:DD:EE:FF",
"links": [...]
}Retrieve sibling links for multiple BSSIDs in a single request.
Request:
{
"bssids": ["AA:BB:CC:DD:EE:FF", "11:22:33:44:55:66"]
}Response:
{
"ok": true,
"links": [...]
}Start a background sibling-detection refresh job.
Request (all fields optional):
{
"batchSize": 500,
"maxOctetDelta": 3,
"maxDistanceM": 200,
"minCandidateConf": 0.5,
"minStrongConf": 0.85,
"maxBatches": 100
}Response: 202 Accepted when the job starts; 409 Conflict if already running.
Poll the running sibling refresh job status.
Response:
{
"ok": true,
"status": { ... }
}Aggregate statistics for the sibling detection dataset.
Response:
{
"ok": true,
"stats": { ... }
}List OUI groups.
OUI details.
Suspect randomization.
Analyze OUI data.
Get tags for network.
Search by tags.
Toggle tag.
Remove a specific tag from a network. Unlike the general tag clearing endpoint, this endpoint selectively removes a single tag from the BSSID's tag list.
Request Body:
{
"bssid": "AA:BB:CC:DD:EE:FF",
"tag": "SUSPECT"
}Response:
{
"ok": true,
"message": "Tag 'SUSPECT' removed from network AA:BB:CC:DD:EE:FF",
"network": {
"bssid": "AA:BB:CC:DD:EE:FF",
"tags": ["THREAT"],
"notes": "Network notes content"
}
}Add note to network.
Get all notes for a network.
Delete note.
Upload media to note.
Get media attachments for a specific note.
Delete a media attachment associated with a network note.
Upload media (image/video) to network.
Get media list for network.
Download media file.
Serve media inline, using the stored thumbnail when thumbnail=true. Requires admin access.
Add notation to network.
Get all notations for network.
List all settings.
Get setting.
Update setting.
Toggle ML blending.
Retrieve background job runtime status and recent history.
Retrieve runtime feature flags and environment-backed settings.
Retrieve geocoding cache statistics and coverage.
Parameters:
precision(int, default: 5) - S2/Geohash precision level for clustering.
Response:
{
"ok": true,
"stats": {
"total": 125430,
"cached": 85420,
"coverage": 68.1,
"pending": 40010,
"lastUpdated": "2026-03-27T14:30:00.000Z"
}
}Start a background job to update the geocoding cache.
Request:
{
"provider": "mapbox",
"mode": "address-only",
"limit": 1000,
"precision": 5,
"perMinute": 200,
"permanent": true
}Options:
provider:mapbox,nominatim,overpass,opencage,geocodio,locationiq.mode:address-only,poi-only,full.limit: Maximum records to process.perMinute: Rate limit for the provider.
Requeue failed or stalled geocoding jobs for reprocessing.
Get status of the persistent geocoding daemon.
Start the geocoding daemon for continuous background enrichment.
Stop the geocoding daemon.
Test a geocoding provider with a sample coordinate.
pgAdmin status.
Start pgAdmin.
Stop pgAdmin.
Destroy all pgAdmin containers and associated temporary state (admin-only, destructive). Use with caution.
AWS resources overview.
Request a reboot of an EC2 instance (admin only).
Start an EC2 instance.
Stop an EC2 instance.
Terminate an EC2 instance (destructive).
Run full database backup.
List S3 backups.
Delete S3 backup.
User login.
Request:
{
"username": "admin",
"password": "securepassword"
}Response:
{
"success": true,
"token": "abc123...",
"user": {
"id": 1,
"username": "admin",
"email": "admin@example.com",
"role": "admin"
}
}User logout.
Get current user.
List user profiles available to administrators.
Create a new user profile (admin only).
Retrieve the currently configured AWS configuration settings (region, profile).
Update the AWS configurations.
Reload cached secrets dynamically from AWS Secrets Manager.
List configured secret-setting keys.
Retrieve the configured WiGLE credential status.
Retrieve the configured Smarty credential status.
Retrieve the configured Mapbox Unlimited provider setting. This admin settings endpoint is manual-only on the API Test Page.
Update the Mapbox Unlimited provider setting. This admin settings endpoint is manual-only on the API Test Page.
Retrieve the configured Google Maps API key setting. This admin settings endpoint is manual-only on the API Test Page.
Update the Google Maps API key setting. This admin settings endpoint is manual-only on the API Test Page.
Retrieve the configured OpenCage API key setting. This admin settings endpoint is manual-only on the API Test Page.
Update the OpenCage API key setting. This admin settings endpoint is manual-only on the API Test Page.
Retrieve the configured Geocodio API key setting. This admin settings endpoint is manual-only on the API Test Page.
Update the Geocodio API key setting. This admin settings endpoint is manual-only on the API Test Page.
Retrieve the configured LocationIQ API key setting. This admin settings endpoint is manual-only on the API Test Page.
Update the LocationIQ API key setting. This admin settings endpoint is manual-only on the API Test Page.
Retrieve the configured Mapbox token setting. This admin settings endpoint is manual-only on the API Test Page.
Update the Mapbox token setting. This admin settings endpoint is manual-only on the API Test Page.
Run a full database backup (no auth yet).
Download a legacy JSON backup of observations, networks, and network tags. Requires admin access.
Upload a legacy JSON backup file for restore staging. Requires admin access and performs destructive restore preparation.
Export observations as CSV (full dataset).
Export observations + networks as JSON (full dataset).
Export observations as GeoJSON (full dataset).
Note: Backups/exports are currently unauthenticated and intended for trusted environments only.
Download a full app-schema snapshot in JSON format. Requires admin access.
Download observations for requested BSSIDs in KML format. Requires an authenticated user.
API-key authorized endpoints used by mobile capture units to request upload links and log completed SQLite captures.
Canonical mobile ingest endpoint for generating a presigned S3 upload URL. Requires SHADOWCHECK_API_KEY in headers.
Generates a presigned S3 upload URL for uploading a mobile SQLite database file. Requires SHADOWCHECK_API_KEY in headers.
Request:
{
"fileName": "capture_20260611.sqlite",
"case_id": "case_101",
"filesize": 10485760
}Response:
{
"success": true,
"uploadUrl": "https://shadowcheck-bucket.s3.amazonaws.com/uploads/...",
"s3Key": "uploads/case_101/20260611/capture_20260611.sqlite"
}Canonical mobile ingest endpoint for registering a completed S3 SQLite upload. Requires SHADOWCHECK_API_KEY in headers.
Registers a successfully uploaded S3 SQLite key for the ETL background ingestion queue. Requires SHADOWCHECK_API_KEY in headers.
Request:
{
"s3Key": "uploads/case_101/20260611/capture_20260611.sqlite",
"sourceTag": "mobile-unit-alpha",
"deviceModel": "Pixel 9 Pro",
"deviceId": "dev_abc123"
}AWS Bedrock-backed analysis endpoints. No authentication is required by the route handlers themselves, but req.user (if present) is used to scope insight history.
Submit a list of networks for AI threat analysis. Calls AWS Bedrock (Claude Haiku), persists the result, and returns analysis + recent history.
Request:
{
"networks": [
{
"bssid": "AA:BB:CC:DD:EE:FF",
"ssid": "TestNet",
"type": "W",
"threat_score": 75,
"observation_count": 42,
"unique_days": 7,
"seen_at_home": true,
"seen_away": true
}
],
"question": "Is this network a surveillance threat?"
}networks(array, required) — Non-empty array of network objectsquestion(string, optional) — Analysis question; defaults to a standard threat-identification prompt
Response:
{
"ok": true,
"analysis": "...",
"suggestions": [...],
"insightId": 42,
"history": [...],
"meta": {
"networksAnalyzed": 1,
"model": "us.anthropic.claude-haiku-4-5-20251001-v1:0"
}
}Retrieve AI analysis history for the current user (or anonymous session).
Parameters:
limit(int, default: 20, max: 100) — Number of history records to return
Response:
{
"ok": true,
"history": [...],
"count": 5
}Record user feedback on an AI insight.
Parameters:
:id(path, required) — Insight ID (positive integer)
Request:
{
"useful": true
}Response:
{
"ok": true,
"id": 42,
"useful": true
}Connectivity check for the AWS Bedrock integration.
Response:
{
"ok": true,
"connected": true
}W: WiFiE: BLEB: BluetoothL: LTEN: 5G NRG: GSM
ShadowCheck v4.0 uses a behavioral scoring engine with the following weighted components:
| Component | Weight | Criteria |
|---|---|---|
| Following Pattern | 35% | Multiple clusters >2km from home; max distance spread. |
| Parked Surveillance | 20% | Repeated detections within 100m and 10-minute windows. |
| Location Correlation | 15% | Percentage of observations near home vs. distinct clusters. |
| Equipment Profile | 10% | Manufacturer OUI matching (industrial/vehicular) and SSID patterns. |
| Temporal Persistence | 5% | Number of distinct days observed. |
| Fleet Bonus | 15% | Correlation with other high-score networks (same manufacturer/SSID). |
Thresholds:
- CRITICAL: 81+
- HIGH: 61-80
- MEDIUM: 41-60
- LOW: 21-40
- NONE: <21
Default display threshold: 40
Purpose: Build analytics queries for different data domains
Modules:
coreAnalytics.ts- Temporal, signal, radio type queries (~140 lines)threatAnalytics.ts- Security & threat analysis queries (~120 lines)networkAnalytics.ts- Network-specific queries (~100 lines)helpers.ts- Normalization & formatting utilities (~85 lines)index.ts- Service coordinator (re-exports)
Why modularized: Each analytics domain is independent. New query types are added to their domain file.
Usage:
import { buildTemporalAnalytics } from '../services/analytics';
const query = buildTemporalAnalytics({ startDate, endDate });Purpose: Validate data by type and domain
Modules:
networkSchemas.ts- BSSID, SSID, channels (~404 lines)geospatialSchemas.ts- Coordinates, radius, altitude (~342 lines)temporalSchemas.ts- Timestamps, date ranges (~283 lines)commonSchemas.ts- String, number, email, URL (~458 lines)complexValidators.ts- Complex validation logic (~447 lines)schemas.ts- Index that re-exports all (coordinator)
Why modularized: Each validation domain is independent. Validators are grouped logically for maintainability.
Usage:
import { validateBSSID, validateCoordinates } from '../validation/schemas';| Code | Description |
|---|---|
| 400 | Bad Request |
| 401 | Unauthorized |
| 403 | Forbidden |
| 404 | Not Found |
| 429 | Rate Limited |
| 500 | Server Error |
Update WiGLE integration settings (admin-only).
Run a test WiGLE connectivity check using current credentials (admin-only).
Apply one or more tags to a network. Request body: { "bssid": "AA:BB:CC:DD:EE:FF", "tags": ["THREAT"] }.
Remove tags from a network or clear all tags for the provided BSSID (admin-only).
Batch fetch multiple v2 networks by BSSID list. Use for large multi-BSSID queries.
Debug endpoint returning additional diagnostic metadata (SQL/explain) for filtered v2 queries. Developer/admin use only.
POST variant of the filtered observations endpoint for large filter payloads (accepts complex JSON filters).
Fetch WiGLE detail records for multiple netids in a single batch request (admin-only).
Reset WiGLE quota counters and ledger state (admin-only).
Kick off a Bluetooth import run using the WiGLE search API (admin-only).
Trigger a full import across all saved WiGLE search terms. Starts a background import run and returns a run identifier (admin-only).
Delete a saved WiGLE import run and its associated artifacts by run ID. Destructive; admin-only.
Remove temporary cluster-cleanup artifacts produced during import post-processing (admin-only).
Resume the most recent resumable WiGLE import run. Useful for automated recovery after failures (admin-only).
Create or update a saved SSID search term used for scheduled WiGLE imports.
Delete a saved SSID search term by ID (admin-only).
Legacy root health-check endpoint (alias for GET /api/health). Returns basic service checks (database, memory).
🔒 = Requires authentication (session or API key)