Skip to content

REST API

Everything lives under /v1 on your instance’s own address, for example https://infrainbox.example.com/v1.

The full OpenAPI document is served live, no credential needed, at GET /v1/openapi.json. It’s always in sync with the running server — generate a client from it, or just read it.

Credential Who uses it Form
Session cookie The web dashboard Set by sign-in; sent automatically by the browser.
Device bearer token A paired phone app Authorization: Bearer <token>, opaque, refreshed automatically.
Source key Something sending events Authorization: Bearer iik_src_…, on POST /v1/events, a tool’s own webhook path, and the watcher’s own calls (enrolling its heartbeat, POST /v1/watchers/check-in).
Heartbeat token A heartbeat ping Authorization: Bearer iik_hb_…, or as the ping URL’s path segment.

Signing in through an identity provider uses three endpoints: GET /v1/auth/providers lists the configured providers, POST /v1/auth/oidc/{id}/start begins a sign-in, and GET /v1/auth/oidc/{id}/callback finishes it. The start answers with the provider’s authorization URL (authorizeUrl) and sets a short-lived cookie that ties the sign-in to the browser, which the dashboard then sends to that URL; the callback always answers with a redirect (303) back into the dashboard. They are steps of a browser flow, not calls for a script. {id} is the provider’s id from the configuration.

The account links a server mails out (see Your account) each have a public endpoint that takes the link’s token in the JSON body: POST /v1/auth/password-reset and /password-reset/confirm, POST /v1/auth/email-change/confirm, and on InfraInbox Cloud POST /v1/auth/signup and /verify-email. They exist only where GET /v1/info lists the matching capability (auth.password_reset, mail, auth.signup), and answer 404 elsewhere. A refused link is always the same 403 token_invalid, whatever was wrong with it.

Workspaces and their people sit under /v1/workspaces: POST /v1/workspaces creates one with you as its owner, GET|PATCH /v1/workspaces/{id} reads and changes its settings, GET /v1/workspaces/{id}/members lists who is in it, and PATCH|DELETE /v1/workspaces/{id}/members/{userId} changes a role or removes somebody. Invitations are GET|POST /v1/workspaces/{id}/invites and DELETE /v1/workspaces/{id}/invites/{inviteId}; the reply to a new invitation carries its link once and never again. The invited person accepts with POST /v1/invites/accept, which takes the link’s token in the body. Reading is open to every member of the workspace; everything that changes a membership is for owners only, behind a recent step-up, and a refused invitation is always the same 403 invite_invalid. See Workspaces and members.

Every error response is RFC 9457 problem details, application/problem+json:

{
"type": "https://infrainbox.app/problems/validation_failed",
"title": "Validation failed",
"status": 422,
"detail": "1 field is invalid.",
"code": "validation_failed",
"requestId": "b6f1c2d4e8a94f3b",
"errors": [{"pointer": "/severity", "detail": "must be one of DEBUG, INFO, SUCCESS, WARNING, ERROR, CRITICAL"}]
}

code is the stable, machine-readable reason; type is always https://infrainbox.app/problems/<code>; requestId matches what the server logged, for support. A validation failure (422 validation_failed) lists one errors entry per bad field, each with a JSON pointer and a plain-language reason.

Three operations are destructive or export everything, so they all need a recent re-authentication (a 403 with code: reauth_required says so, and lists the factors the account can prove) and all three are written to the audit log:

Operation What it does
DELETE /v1/me Closes the caller’s own account, and with it every workspace the account owned alone. 409 workspace_needs_owner names a workspace that has other members; 409 last_instance_admin refuses the only administrator.
DELETE /v1/workspaces/{id} Closes one workspace, owners only. 409 last_workspace refuses the account’s only one.
GET /v1/workspaces/{id}/export Streams the workspace as one JSON document (owners only), as a file attachment. It holds no secret — no key, key hash or sealed credential. One export per workspace per minute; a second answers 429.

Both deletions end access with the answer and leave the rows to the background purge (see Security and privacy).

List endpoints support two paging styles:

  • limit (1–200, default 50) with cursor — keyset paging: pass the previous page’s nextCursor to get the next one. Best for a live feed.
  • limit with offset — numbered paging (page n is offset = (n-1) * limit); the response carries total, so a table can say “1–50 of 312”. cursor and offset are mutually exclusive.

Most list endpoints also take sort and order (asc/desc, default desc) — the exact sort columns vary by endpoint (events sort by received, occurred, severity, source, title or disposition, for example).

GET /v1/stream (session cookie or device bearer) is a Server-Sent Events stream of what changed in your workspaces — a data: line per change, so a client refetches the specific resource that changed within about a second instead of polling. Every event carries a reference only, never state. On connect, and whenever the server’s own change feed breaks, it sends resync, meaning “reload everything you show.” A comment heartbeat (: ping) arrives every 25 seconds so an idle connection isn’t mistaken for a dead one; a client that falls behind is disconnected and reconnects to a fresh resync.