Custom API Endpoint - Third-Party Developer Guide¶
This document describes the API contract for receiving forwarded wardrive data from MeshMapper. When a user enables "Custom API Endpoint" in MeshMapper settings, every successful wardrive batch upload is also forwarded to your endpoint.
Request Format¶
| Field | Value |
|---|---|
| Method | POST |
| Content-Type | application/json |
| Authentication | X-API-Key header (value configured by the user in MeshMapper settings) |
Request Body¶
{
"data": [
{ /* ping object */ },
{ /* ping object */ },
...
]
}
The data array contains 1-50 ping objects per batch. Batches arrive approximately every 15-30 seconds during an active wardriving session.
Ping Object Schema¶
Every ping object contains a type field that determines which additional fields are present.
Common Fields (all types)¶
| Field | Type | Description |
|---|---|---|
type |
string |
Ping type: "TX", "RX", "DISC", or "TRACE" |
lat |
number |
Latitude (WGS84, decimal degrees) |
lon |
number |
Longitude (WGS84, decimal degrees) |
timestamp |
integer |
Unix timestamp in seconds |
external_antenna |
boolean |
Whether an external antenna is connected to the device |
noisefloor |
integer\|null |
Ambient noise floor in dBm (e.g., -103). Null if unavailable. |
power |
string\|null |
Radio TX power formatted as "X.Xw" (e.g., "0.3w", "1.0w", "2.0w"). Null if unavailable. |
contact |
string\|absent |
First 8 hex chars of the wardriver's MeshCore device public key (e.g., "D873B1F2"). Only present when the user enables "Include Contact Key" in settings. Useful for cross-referencing with MQTT observer data. |
iata |
string\|absent |
MeshMapper zone code (e.g., "RDU", "MSP", "YOW"). Present when the wardriver is in a zone. |
TX Ping (type: "TX")¶
A transmitted ping broadcast on the wardriving channel, with repeater echo results.
| Field | Type | Description |
|---|---|---|
heard_repeats |
string |
Comma-separated repeater echoes: "id(snr),id(snr)" or "None" if no repeaters heard. IDs are hex strings (1-6 chars). SNR is in dB. |
Example:
{
"type": "TX",
"lat": 45.26974,
"lon": -75.77746,
"noisefloor": -103,
"heard_repeats": "4e(12.25),77(8.50)",
"timestamp": 1768762843,
"external_antenna": false,
"power": "0.3w",
"contact": "D873B1F2",
"iata": "YOW"
}
Example (no repeaters heard):
{
"type": "TX",
"lat": 45.27001,
"lon": -75.77802,
"noisefloor": -101,
"heard_repeats": "None",
"timestamp": 1768762873,
"external_antenna": false,
"power": "0.3w",
"contact": "D873B1F2",
"iata": "YOW"
}
RX Ping (type: "RX")¶
A passively observed mesh packet from a nearby repeater.
| Field | Type | Description |
|---|---|---|
heard_repeats |
string |
Single repeater with SNR: "id(snr)" (e.g., "4e(12.0)"). |
Example:
{
"type": "RX",
"lat": 45.26950,
"lon": -75.77700,
"noisefloor": -105,
"heard_repeats": "4e(12.00)",
"timestamp": 1768762900,
"external_antenna": false,
"power": "0.3w",
"contact": "D873B1F2",
"iata": "YOW"
}
DISC Ping (type: "DISC") - Discovery Response¶
A response from a discovered repeater or room node.
| Field | Type | Description |
|---|---|---|
repeater_id |
string |
Hex ID of the discovered node (e.g., "a3b2c1"), or "None" for failed discovery. |
node_type |
string |
Node type: "REPEATER" or "ROOM". Absent on failed discovery. |
local_snr |
number |
SNR measured locally (dB). |
local_rssi |
integer |
RSSI measured locally (dBm, negative). |
remote_snr |
number |
SNR reported by the remote node (dB). |
public_key |
string |
Full 32-byte public key of the node (64 hex chars). |
Example (successful discovery):
{
"type": "DISC",
"lat": 45.26980,
"lon": -75.77750,
"noisefloor": -102,
"repeater_id": "a3b2c1",
"node_type": "REPEATER",
"local_snr": 11.75,
"local_rssi": -85,
"remote_snr": 9.50,
"public_key": "a3b2c1d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2",
"timestamp": 1768762950,
"external_antenna": true,
"power": "1.0w",
"contact": "D873B1F2",
"iata": "YOW"
}
Example (failed discovery - no nodes responded):
{
"type": "DISC",
"lat": 45.27010,
"lon": -75.77800,
"noisefloor": -100,
"repeater_id": "None",
"timestamp": 1768762980,
"external_antenna": true,
"power": "1.0w",
"contact": "D873B1F2",
"iata": "YOW"
}
TRACE Ping (type: "TRACE")¶
A targeted zero-hop trace to a specific repeater.
| Field | Type | Description |
|---|---|---|
repeater_id |
string |
Hex ID of the targeted repeater. |
local_snr |
number |
SNR measured locally (dB). |
local_rssi |
integer |
RSSI measured locally (dBm, negative). |
remote_snr |
number |
SNR reported by the repeater (dB). |
Example:
{
"type": "TRACE",
"lat": 45.26990,
"lon": -75.77730,
"noisefloor": -104,
"repeater_id": "4e2f",
"local_snr": 10.50,
"local_rssi": -88,
"remote_snr": 8.25,
"timestamp": 1768763010,
"external_antenna": false,
"power": "0.3w",
"contact": "D873B1F2",
"iata": "YOW"
}
Expected Response¶
Your endpoint should return any 2xx HTTP status code on success. The response body is ignored by MeshMapper.
| Status | MeshMapper Behavior |
|---|---|
200-299 |
Logged as success. No further action. |
4xx |
Error logged to user's error tab (throttled to once per minute per status code). |
5xx |
Error logged to user's error tab (throttled to once per minute per status code). |
| Timeout (>10s) | Error logged as timeout. |
| Network error | Error logged as network error. |
MeshMapper does not retry failed custom API requests. Each batch is sent exactly once.
Configuration Link for End Users¶
As an endpoint operator, you can generate a configuration link that your users paste into their clipboard. In MeshMapper Settings > API Endpoints, they tap Import from Clipboard to auto-fill the URL and API key.
Format:
meshmapper://custom-api?url=your-server.com/api/wardrive&key=your-api-key-here
url— Your endpoint host and path, withouthttps://(MeshMapper prepends it automatically).key— The API key the user should send. This will be set as theirX-API-Keyheader value.
Example:
meshmapper://custom-api?url=data.myproject.org/ingest/wardrive&key=sk_live_abc123def456
The user copies this link, opens MeshMapper Settings > API Endpoints, and taps "Import from Clipboard." Both fields are populated instantly.
Security Notes¶
- HTTPS required: MeshMapper validates that the configured URL uses HTTPS. HTTP endpoints are rejected at the settings level.
- API key in header: The user-configured API key is sent as
X-API-Keyheader, not in the request body. - Contact key is opt-in: The
contactfield (device public key prefix) is controlled by the user via "Include Contact Key" toggle. It defaults to ON but can be disabled. - Fire-and-forget: Custom API errors never affect MeshMapper's primary data submission. A broken custom endpoint cannot disrupt wardriving.
Rate and Volume¶
- Batch frequency: Every 15-30 seconds during active wardriving (depends on auto-ping interval and batch timer).
- Batch size: 1-50 ping objects per request (typically 1-10).
- Session duration: Wardriving sessions commonly last 30 minutes to several hours.
- Concurrent users: Plan for multiple users if distributing your endpoint URL. Each user sends independently.