Endpoints
Health (Available)
| Method | Path | Gate | Description |
|---|---|---|---|
GET | /healthz | public | Liveness. { "status": "ok", "version": "0.1.0", "env": "prod" } |
GET | /readyz | public | Readiness (checks the database). { "status": "ok" | "degraded", "database": bool, "jobs": "postgres" | "disabled" } |
Every route lists its gate (admin / tenant / user / public); see
Authentication → gates.
Keys (Available)
| Method | Path | Gate | Description |
|---|---|---|---|
GET | /v1/keys/scopes | tenant · keys:manage | Valid scope names |
POST | /v1/keys | tenant · keys:manage | Issue a key (name, scopes default all, expiresAt) → plaintext once |
GET | /v1/keys?q=&cursor=&limit=&includeRevoked= | tenant · keys:manage | List (search name/prefix) |
GET / PATCH / DELETE | /v1/keys/{id} | tenant · keys:manage | Read / update name-scopes-expiry / revoke |
Users (Available)
| Method | Path | Gate | Description |
|---|---|---|---|
POST | /v1/users | tenant · users:write | Create (tenantKey, displayName, email?, phone?, locale?, timezone?, units?); 409 on duplicate tenantKey |
GET | /v1/users?q=&cursor=&limit=&includeDisabled= | tenant · users:read | List (search tenantKey / name / email) |
GET | /v1/users/by-key/{tenantKey} | tenant · users:read | Lookup by your id |
GET / PATCH / DELETE | /v1/users/{id} | tenant · users:read / users:write | Read / update (changing email or phone resets its verification) / hard delete |
POST | /v1/users/{id}/disable · /enable | tenant · users:write | Disable (revokes sessions, blocks minting) / re-enable |
POST / GET | /v1/users/{id}/session-tokens | tenant · users:write | Mint (token once) / list |
DELETE | /v1/session-tokens/{id} | tenant · users:write | Revoke a pending token |
Auth (Available)
| Method | Path | Gate | Description |
|---|---|---|---|
POST | /v1/auth/session | public | Exchange session token → cookie + Me |
GET | /v1/auth/me | user (exact) | Current user / tenant / session |
POST | /v1/auth/logout | user (exact) | Revoke session, clear cookie |
GET / DELETE | /v1/auth/sessions[/{id}] | user (exact) | Own sessions |
Tiles (Available)
| Method | Path | Gate | Description |
|---|---|---|---|
GET | /v1/tiles | user | Latest tile versions visible to the tenant (platform + own); ?allVersions=true |
GET | /v1/tiles/{id} | user | Tile definition (no script) |
GET | /v1/tiles/{id}/spec | user | Self-describing spec: flattened params[] + ui + paramsSchema; see Tiles |
Sources (Available)
| Method | Path | Gate | Description |
|---|---|---|---|
POST | /v1/sources | user · keys sources:write | Register a source: displayName, hint (open lower-kebab string: area, sensor, weather-station, or your own), optional tenantKey (your stable id, unique per owner), boundary (closed [lng,lat] ring), location ({lat,lng}; derived centroid when only a boundary is sent), timezone, metadata |
GET | /v1/sources | user · keys sources:read | List (paginated, q over displayName/tenantKey/hint, ?hint=, ?status=, tenant callers ?userKey=); a user session sees only its own |
GET / PATCH / DELETE | /v1/sources/{id} | user · keys sources:read / sources:write | Read / update (incl. clearBoundary/clearLocation/clearTenantKey) / delete |
Bind a flow with targets: [{ "sourceId": "…" }]. The Engine resolves the name, location (or
boundary centroid) and timezone at run time; deleting a bound source makes later runs fail with
a clear error until the flow is re-saved.
Flows & runs (Available)
| Method | Path | Gate | Description |
|---|---|---|---|
POST | /v1/flows | user · keys flows:write | Create (tiles validated against their schemas; tileKey or pinned id/version; tenant callers may pass ownerUserKey; sources and contact lists must belong to the owner) |
GET | /v1/flows | user · keys flows:read | List (paginated, q, ?status=, tenant callers ?userKey=); a user session sees only its own |
GET / PATCH / DELETE | /v1/flows/{id} | user · keys flows:read / flows:write | Read / update (tiles or targets bump version) / delete |
POST | /v1/flows/{id}/run | user · keys flows:run | Run now: 202 with the queued run (targets run in parallel; poll /v1/runs/{id}; 409 while a run is unfinished); body { "now": ISO } optional |
GET | /v1/flows/{id}/runs | user · keys flows:read | Run history (summaries; ?includeResults=true) |
GET | /v1/runs/{id} | user · keys results:read | One run with per-target, per-tile results |
Adapters (Available)
| Method | Path | Gate | Description |
|---|---|---|---|
POST | /v1/adapters | tenant · adapters:manage | Register an adapter: facet (series | weather | events | targets | comms, one each), displayName (+ optional tenantKey), TS/JS script, write-only env, allowedHosts |
GET | /v1/adapters[/{id}] | tenant · adapters:manage | List/read (env shown as envKeys only) |
PATCH / DELETE | /v1/adapters/{id} | tenant · adapters:manage | Update script/env/hosts/status / remove |
POST | /v1/adapters/{id}/test | tenant · adapters:manage | Run once with a sample request |
POST | /v1/adapters/{id}/sync | tenant · adapters:manage | targets adapters only. 202: queue a source-catalog sync now (the hourly scheduler runs the same job); optional ?userKey= |
Contacts & alerts
| Method | Path | Gate · scope | Purpose |
|---|---|---|---|
POST / GET | /v1/contacts | user · keys contacts:write / contacts:read | Create / list (cursor + q search, tenant callers ?userKey=); a contact needs email and/or phone; user-owned |
GET / PATCH / DELETE | /v1/contacts/{id} | user · keys contacts:read / contacts:write | Read / update (address change resets its verification) / delete |
POST / GET | /v1/contact-lists | user · keys contacts:write / contacts:read | Create / list (tenant callers ?userKey=); members must belong to the list’s owner |
GET / PATCH / DELETE | /v1/contact-lists/{id} | user · keys | Read / rename + replace membership (contactIds) / delete |
POST | /v1/users/{id}/verifications | tenant · users:write | Start a user’s address verification ({"channel": "email"|"sms"}) |
POST | /v1/contacts/{id}/verifications | user · keys contacts:write | Start a contact’s address verification; a user session verifies its own contacts |
POST | …/verifications/confirm | same as the start route | Confirm the 6-digit SMS code |
GET | /verify/{token} | public | Consume the one-time email link |
Sessions
| Method | Path | Gate · scope | Purpose |
|---|---|---|---|
POST | /v1/session-tokens | tenant (any key; the session is frozen to ⊆ its scopes) | Mint a tenant session token: the GUI logs in as the tenant itself (Me.kind: "tenant") |
Alert config rides the flow (settings.alerts, validated); see Alerts & contacts. Manual runs pass {"suppressAlerts": false} to send alerts.
Reports (Available)
| Method | Path | Gate · scope | Purpose |
|---|---|---|---|
GET | /v1/flows/{id}/reports | user · keys results:read | The flow’s report history (one row per send attempt, per-recipient outcomes inside) |
POST | /v1/flows/{id}/reports/send-now | user · keys reports:send | Freeze a report from an existing succeeded run (runId optional, defaults to the latest) and email its link to contactListIds: 202 {reportId, token, link, expiresAt}; delivery runs on a worker, poll the history |
GET | /reports/{token} | public | The frozen report payload behind an emailed link (7-day expiry; 404 after) |
Report schedules ride the flow (settings.reports, validated); see Reports.
Branding
| Method | Path | Gate · scope | Purpose |
|---|---|---|---|
GET / PUT / DELETE | /v1/branding | tenant · branding:manage | The tenant brand {name?, logo?, skin?} (PUT replaces whole) |
GET / PUT / DELETE | /v1/branding/me | user session only | A permitted user’s personal brand (canOverrideBranding); overrides their own account per-field |
PUT | /v1/branding/skin | tenant · branding:manage | Replace the tenant skin (compat view over branding.skin) |
GET | /v1/branding/skin | tenant · branding:manage | Read back the stored skin ({"skin": null} when unset) |
DELETE | /v1/branding/skin | tenant · branding:manage | Clear (back to the platform default) |
GET | /v1/branding/theme | user | The caller-resolved brand: {name, logo, colorSchemes, shape}; see Branding |
See Adapters for the script contract. The Engine keeps no copy of the data.
Pagination & search (Available)
Every list endpoint accepts limit (1–200, default 50), cursor (opaque, from the previous
response’s nextCursor) and q (case-insensitive substring over the fields named above), and
returns { "items": [...], "nextCursor": "…" | null }. Ordering is newest-first and stable.
Webhooks (Available)
| Method | Path | Gate | What |
|---|---|---|---|
GET | /v1/webhooks/events | tenant · webhooks:manage | Event catalog |
POST / GET | /v1/webhooks | tenant · webhooks:manage | Register (secret returned once) / list |
GET / PATCH / DELETE | /v1/webhooks/{id} | tenant · webhooks:manage | Read / update url, events, status / remove |
POST | /v1/webhooks/{id}/ping · /rotate-secret | tenant · webhooks:manage | Test event (202) · new secret (shown once) |
GET | /v1/webhooks/{id}/deliveries[/{deliveryId}] | tenant · webhooks:manage | Delivery log (?status=), one delivery with attempts + envelope |
POST | /v1/webhooks/{id}/deliveries/{deliveryId}/redeliver | tenant · webhooks:manage | New attempt chain (202) |
See Webhooks for the event catalog and signature verification.
Everything else (Planned)
The remaining resource families (per-source status, tenant tile authoring) are documented here as they ship. Until then the API overview table is the roadmap.