API reference
The API is organised around resources with predictable URLs. Requests and responses are UTF-8; timestamps are RFC 3339 in UTC. Every endpoint on these pages shows cURL, HTTPie and Python examples, example responses, and — where the public sandbox allows it — a Try it button that runs the request live.
The examples are ready to copy and run: they contain this server's address, the public
sandbox account sandbox.example and its public tokens. Each example uses the token it
needs:
| Sandbox token | Used by examples that… | Scopes |
|---|---|---|
| read-only | read data | fw:read logdata:read devices:read tags:read stats:read |
| write | upload, create, tag, update, withdraw | read-only + fw:write logdata:write devices:write tags:write |
In your own account, create tokens with the scopes you need and put them in place of the sandbox tokens. The sandbox is reset every day; see The sandbox for what it contains and its limits.
Immutable resources
Firmware builds and log files are write-once and are never deleted: re-sending
identical content is a harmless 200, while different content under an existing identity
is a 409. Firmware builds and devices are taken out of service by withdrawing them
(status: withdrawn) instead of deleting them. What stays editable is metadata: a build's
tags, general-release flag and status; a device's keys, tags, notes and status.
Base URL
All paths are relative to your server, e.g. https://fw.unitcircle.ca. There is no version prefix.
Authentication
Send an API token as a bearer token: Authorization: Bearer <token>. Tokens are created
by members of your account in the web console (Console → API tokens); each token
belongs to one account and carries scopes that limit what it can do (for example
fw:read, fw:write, devices:write). The token also decides which account a request
works on, so URLs never contain your account's domain — with one exception: the
update check, which devices call without a token, names the
account in its path.
| Scope bundle | Scopes | Typical use |
|---|---|---|
| Read-only | fw:read logdata:read devices:read tags:read stats:read |
dashboards, audits |
| Uploader | read-only + fw:write logdata:write |
CI pipelines, devices uploading logs |
| Full | everything except member management | back-office tools |
Missing or invalid tokens get 401; a token without the needed scope gets 403.
Resources that belong to another account, or don't exist, get 404. See
Authentication for tokens, scopes and console sessions.
Response formats
Most endpoints always answer with JSON. The endpoints devices and build scripts call
directly — uploading firmware and log files, the update check, and fetching a log file —
also support compact plain-text answers, and choose the format from the request's
Accept header:
Accept header |
Successful upload | Errors |
|---|---|---|
absent, */*, or text/plain |
200 OK with the body OK (Content-Type: text/html; charset=utf-8) |
text/plain, e.g. 404 Not Found: no logdata with that name |
contains application/json |
201 Created (new) or 200 OK (identical re-upload) with a JSON object and a Location header |
application/problem+json |
Each of these endpoints lists both forms in its responses. The update check returns the same JSON array either way; only its errors change format.
Errors
JSON errors follow RFC 9457 (application/problem+json):
{"type":"about:blank","title":"Forbidden","status":403,"detail":"missing scope fw:write","instance":"/firmware","request_id":"5b1f…"}
Every response carries an X-Request-Id header; quote it when you contact support.
Common statuses: 400 invalid input, 401 authentication, 403 scope, 404 not found,
409 conflict, 410 expired download link, 413 too large, 422 unprocessable
firmware, 429 rate limited (see Retry-After), 503 maintenance. Details:
Errors, limits & CORS.
Pagination
List endpoints take ?limit= (page size) and ?offset= (number of items to skip, default
0) and return {"items": [...], "next_offset": 100}. next_offset is the integer to pass
as ?offset= for the next page, or null when there are no more items.
Administrators
Administrators of the service can work on any account by adding ?customer=DOMAIN to
any endpoint; for everyone else the parameter must be omitted or name their own account.
The Admin endpoints manage accounts, security and backups.