All responses use JSON. Ingest failures include a requestId for sanitized log correlation. API requests never redirect to the HTML login page: private endpoints return JSON 401 or 403.
Production defaults to AUTH_MODE=REQUIRED.
Authorization: Bearer <API_KEY>(recommended)x-uplotr-key: <API_KEY>x-heyiot-token: <API_KEY>(legacy compatibility)
UPLOTR_API_SECRET or the legacy WEBHOOK_SECRET may be configured as a static deployment secret. Named keys created in the console are preferred. They are high-entropy, stored as SHA-256 hashes, and shown in full only once.
OPTIONAL accepts requests without a token but rejects an invalid supplied token. OFF disables ingest authentication and should not be used on an internet-facing instance.
- Maximum body: 128 KiB (
413when exceeded) - Default persistent rate limit: 600 requests per key or source IP per minute (
429withRetry-After) - Device ID: 1–128 characters
- Latitude: -90 through 90; longitude: -180 through 180
- Numeric fields must be finite; timestamps must parse as valid dates
- Late events are stored in history but never overwrite a newer device's current state
{
"device_id": "tracker-01",
"lat": 37.7749,
"lon": -122.4194,
"timestamp": "2026-09-02T12:00:00Z",
"battery": 85,
"type": "generic",
"temp": 22.5,
"light": 100,
"metrics": { "altitude": 32, "voltage": 4.08, "gps_fix": true }
}device_id, lat, and lon are required unless an assigned payload mapper supplies coordinates. timestamp accepts ISO 8601, Unix milliseconds, or Unix seconds. Successful requests return 201:
Primitive fields not reserved by the standard schema are also retained as custom telemetry. For the clearest contract, put maker-defined numeric, string, or boolean values in the flat metrics object. Up to 32 metrics are retained per position.
{ "success": true, "deviceId": "database-uuid", "requestId": "request-uuid" }Accepts common TTN, Helium, ChirpStack, and HeyIoT-compatible shapes. Device identity may come from devEui, dev_eui, deviceInfo.devEui, or end_device_ids.dev_eui; decoded coordinates may come from the provider payload or an assigned mapper.
{
"end_device_ids": { "device_id": "field-node", "dev_eui": "A840410000000123" },
"received_at": "2026-09-02T12:00:00Z",
"uplink_message": {
"decoded_payload": { "latitude": 37.7749, "longitude": -122.4194, "battery": 85 },
"rx_metadata": [{ "rssi": -92, "snr": 7.5 }]
}
}An event without coordinates may still update telemetry and event history. Full payloads and precise positions are not written to application logs.
These routes require an authenticated administrator session and same-origin mutations.
GET /api/keysreturns onlyid,name,prefix,createdAt, andlastUsedAt.POST /api/keysaccepts{ "name": "Field gateway" }and returns the fullkeyonly in that successful response.DELETE /api/keys/:idrevokes a key.
Authenticated users may read:
GET /api/devicesGET /api/positions?deviceId=...&range=24hGET /api/positions?deviceId=...&runId=...&limit=10000GET /api/events?deviceId=...GET /api/runs?deviceId=...GET /api/runs/:idGET /api/runs/compare?left=...&right=...
Administrators may start a field test with POST /api/runs and complete or edit one with PATCH /api/runs/:id. Only one run may be active per device. Positions received while it is active are attached automatically.
POST /api/runs/import accepts administrator-authenticated multipart/form-data with deviceId, name, file, and an optional format (AUTO, GENERIC_CSV, PX4_CSV, ARDUPILOT_CSV, BETAFLIGHT_CSV, or GPX). Optional metadata fields are hardwareVersion, firmwareVersion, and notes. Files are limited to 5 MiB and 10,000 valid points. Imported logs become completed FLIGHT runs.
Administrator access is also required for users, API keys, system settings, mapping templates, device updates/deletion, manual retention cleanup, and mapper assignment.
GET /api/health is public and returns only overall status, database availability, and the package version. It never exposes configuration or credentials.
GET /api/maintenance/cleanup is reserved for Vercel Cron and requires an existing, matching CRON_SECRET. Authenticated administrators may use POST from the console.
| Status | Meaning |
|---|---|
400 |
Invalid JSON, identifier, coordinate, timestamp, or number |
401 |
Missing or invalid session/API key |
403 |
Authenticated but insufficient role, wrong origin, or invalid Cron secret |
413 |
Request body exceeds 128 KiB |
429 |
Persistent rate limit exceeded; inspect Retry-After |
500 |
Internal error; use requestId to correlate sanitized logs |
503 |
Health check cannot reach PostgreSQL |