Skip to main content

Documentation Index

Fetch the complete documentation index at: https://dev.1st.app/llms.txt

Use this file to discover all available pages before exploring further.

Error envelope

Every error response uses the same envelope:
{
  "error": {
    "type": "invalid_request_error",
    "code": "rate_limit_exceeded",
    "message": "Rate limit exceeded: 600 requests per 10 minutes per key. Wait `Retry-After` seconds and retry. For higher throughput, use the bulk export endpoint instead of looping per-device.",
    "param": null,
    "doc_url": "https://dev.1st.app/errors/rate_limit_exceeded",
    "request_id": "req_1a2b3c4d5e6f7a8b"
  }
}
Branch on code, not on message. Messages are tuned over time for clarity; codes are a stable contract. The message field contains the FIX inline, written so an AI agent reading just the response body can self-correct. Lean on this. The request_id is also returned in the X-Request-Id header on every response (including 2xx). Share it with support if you need help.

Status code summary

HTTPWhen
400Validation failed (cursor, time range, body).
401Auth failed (missing / invalid / revoked / expired key).
403Key lacks the required scope (PATCH without read_write).
404Device not in your team or doesn’t exist.
413CSV result exceeds 250,000 rows, narrow the range or device list.
422Idempotency-Key reused with a different request body.
429Rate limit exceeded (see Retry-After).
500Backend hiccup. Retry with backoff.
501Request shape reserved for a future API version. Do not retry.

Code reference

Each entry expands to a short fix; click through for the full page.
No Authorization header on the request. Fix: add Authorization: Bearer YOUR_API_KEY. Full page →
Header malformed, key prefix doesn’t match an active key, or wrong secret. Fix: re-copy the key from /integrations/api-keys. Full page →
The key was revoked. Fix: create a new one. Full page →
The key passed its expires_at date. Fix: create a new key, optionally with a longer expiry preset. Full page →
Read-scope key calling PATCH /v1/devices/{id}. Fix: create a new key with read_write scope. Full page →
600 requests per 10 minutes per key. Fix: wait Retry-After seconds. For bulk reads use GET /v1/readings.csv instead of looping per-device. Full page →
The bookmark you sent is corrupted or too old. Fix: drop the cursor parameter and start over. Full page →
Cursor was issued under a different team’s key. Fix: drop the cursor and start fresh under the current key. Full page →
Cursor was issued for a different device’s pagination loop. Fix: drop the cursor when you switch devices and start fresh. Full page →
Range exceeds 90 days. Fix: split into windows ≤90 days and concatenate. For bulk pulls use /v1/readings.csv. Full page →
from/to not strict ISO 8601 UTC, or to ≤ from. Fix: use 2026-05-11T00:00:00Z format, to after from. Full page →
Device with this id isn’t in your team (or doesn’t exist). Fix: list devices via GET /v1/devices first. Full page →
Assignment id isn’t on the device you asked about, or isn’t visible to your team. Fix: list the device’s assignments via GET /v1/devices/{device_id}/assignments to get valid ids. Full page →
Generic input rejection. Also fires as 422 when an Idempotency-Key is reused with a different request body. Fix: check the param field for what was rejected. Full page →
Endpoint or request shape reserved for v1.1. Today this only fires on PATCH /v1/devices/{id} with an assignment body. Fix: manage assignments via the dashboard for now. Don’t retry — the response won’t change until v1.1 ships. Full page →
Backend hiccup. Fix: retry with exponential backoff. Share the request_id with support if it persists. Full page →