HTTP API
API documentation
Practical quick reference for integrators: curl examples, auth, rate limits (~64KB / ~60 req/min/device for ingest), and common errors. Download the OpenAPI spec for the full machine-readable contract; VitePress developer docs cover longer walkthroughs.
Authentication
| Audience | Mechanism |
|---|---|
| Device firmware | Device key in /api/ingest/<deviceKey> (or X-Ingest-Key for MQTT bridge) |
| Pro integrations | Bearer API key from Dashboard → Share |
| Browser dashboard | Session cookie (not for third-party clients) |
| Share / inbound | Opaque token in the URL path (+ optional HMAC on inbound) |
| Claim puck / Bay Buddy |
Companion session: Authorization: Bearer +
X-SB-Refresh-Token
(claim puck)
|
POST /api/ingest/<deviceKey>
Primary hardware path. Push JSON from ESP/Arduino or any HTTPS client. Sensor keys auto-import on first POST.
Optional top-level battery / battery_pct and rssi update device health.
Limits: max ~64KB body; ~60 requests per minute per device key (per Worker isolate).
Exceeding size → 413; exceeding rate → 429 with Retry-After when set.
curl -X POST "https://thermaltrace.dev/api/ingest/YOUR_DEVICE_KEY" \
-H "Content-Type: application/json" \
-d '{"door1": true, "temp1": 42.5, "battery": 87, "rssi": -62}' 200 OK example:
{
"ok": true,
"readings": 2,
"sensors_created": 2
} Kinds include temperature, humidity, CO₂, pressure, PM2.5, VOC, water level, energy, door, power, flood, and motion. Sample sketches: sketches/ · Adding devices · Ingest & webhooks · JSON probe schema
POST /api/ingest/mqtt
MQTT→HTTPS bridge. Keep Mosquitto on the LAN; mirror topics with header X-Ingest-Key.
curl -X POST "https://thermaltrace.dev/api/ingest/mqtt" \
-H "Content-Type: application/json" \
-H "X-Ingest-Key: YOUR_DEVICE_KEY" \
-d '{"topic":"home/garage/temp","message":{"temp1":42.5,"humidity":38}}' Full recipe: MQTT bridge guide
Claim puck (/api/pucks + bay mood)
RP2040-Zero presence key. Companion session auth (same Bearer +
X-SB-Refresh-Token headers as Bay Buddy). Product setup:
/claim-puck.
| Method | Path | Purpose |
|---|---|---|
POST | /api/pucks/register | Store device id + HMAC secret for the signed-in household |
POST | /api/pucks/claim/start | Issue a nonce for button-confirmed claim |
POST | /api/pucks/claim/finish | Verify HMAC-SHA256(secret, nonce) and bind bay |
GET | /api/bays/<bay_id>/mood | Bay Buddy mood from sensors (or PUT override) |
Firmware + CLI: thermaltrace-claim-puck · Desktop claim UI: Bay Buddy
Common ingest errors
| Status | When | Body shape |
|---|---|---|
400 | Missing key, invalid JSON, empty body, unusable payload | { "error": "…" } |
401 | Unknown / invalid device key | { "error": "Invalid device key" } |
413 | Body larger than ~64KB | { "error": "Payload too large…" } |
429 | More than ~60 POSTs/min for that device key | { "error": "Rate limit exceeded…" } |
500 | Server-side persistence failure | { "error": "…" } |
GET /api/v1/metrics
Prometheus text exposition (Pro). Create an API key under Dashboard → Share.
curl -sS "https://thermaltrace.dev/api/v1/metrics" \
-H "Authorization: Bearer YOUR_API_KEY" | head
Related: GET/POST /api/v1/devices with the same Bearer key (list / create push devices).
Grafana notes:
developer docs ↗
POST /api/inbound/<token>
Pro inbound webhooks for Home Assistant / Zapier / Make: actions such as snooze, vacation, status.
Create the token under Dashboard → Share. Optional HMAC via signing secret
(X-ThermalTrace-Signature; legacy X-GarageTemp-Signature still accepted).
curl -X POST "https://thermaltrace.dev/api/inbound/YOUR_INBOUND_TOKEN" \
-H "Content-Type: application/json" \
-d '{"action":"snooze","hours":4}'
Invalid token → 401; bad signature → 401 { "error": "Invalid signature" }.
Guide:
Ingest & webhooks
·
HACS integration
Session cookie routes (dashboard)
- POST /api/user/pull-setup: save pull feeds and probe labels in one step (Devices → Pull feeds). Not for firmware; requires a signed-in browser session.
- POST /api/devices/reveal-ingest-key: recover a push device’s ingest key when the deployment has
INGEST_KEY_ENCRYPTION_SECRET configured (encrypted vault). Keys are shown briefly at create time; if you dismiss that callout, Reveal can decrypt the stored vault copy for owners/managers.
Rate-limited (~10/hour/user) and written to household activity. Without the vault secret, rotate the key instead.
OpenAPI
Machine-readable contract for all paths above (plus MQTT bridge and devices CRUD). Prefer the downloaded YAML over any truncated preview.
Guides & next steps
- Ingest & webhooks guide
payload shapes, HA blueprints, and alert channels
- JSON probe schema
stable keys for dashboard mapping
- Integrations hub
HACS, MQTT, Grafana, Zapier
- Plans & pricing
Free ingest + family live share JSON; Pro for metrics API keys, expanded share scopes, and inbound webhooks
- Extended developer docs ↗
VitePress on GitHub Pages
Frequently asked questions
Auth, limits, and where the full OpenAPI lives.
Where is the ThermalTrace OpenAPI spec?
Download openapi.yaml from /openapi.yaml on thermaltrace.dev (also mirrored on the GitHub Pages developer docs). The in-app page at /docs/api is the human quick reference with curl examples, rate limits, and error codes.
How do I authenticate to the HTTP API?
Device firmware uses a per-device ingest key in the URL (or X-Ingest-Key for the MQTT bridge). Pro integrations use a Bearer API key from Dashboard → Share. The browser dashboard uses session cookies, not for third-party clients. Share and inbound routes use opaque tokens in the path.
What are the ingest rate limits?
Push ingest accepts about 64KB max body and about 60 requests per minute per device key (per Worker isolate). Oversized bodies return 413; rate limit returns 429. Keep firmware under those ceilings: typical garage probes POST every 1–5 minutes.
What does “encrypted vault” mean for reveal-ingest-key?
When the deployment sets INGEST_KEY_ENCRYPTION_SECRET, new push keys are stored encrypted so owners can Reveal ingest key later from Devices (rate-limited and audited). On thermaltrace.dev this is enabled. If the vault secret is missing, create/rotate a key and copy it from the 30-minute callout, there is nothing to decrypt later.
Where do I find full request/response schemas?
openapi.yaml is the source of truth. Longer walkthroughs live at doodersrage.github.io/thermaltrace (ingest, Grafana, webhooks). Product how-tos: thermaltrace.dev/about/ingest-and-webhooks and /about/adding-devices.
What is the claim puck API?
Companion-session endpoints under /api/pucks (register, claim/start, claim/finish) plus GET/PUT /api/bays/{bay}/mood. Used by the RP2040-Zero claim puck and Bay Buddy. Setup: thermaltrace.dev/claim-puck. Details on /docs/api#claim-puck.