Files
app/docs/code/api.md
T
stroblmeandClaude Opus 5 37a7df9d24
Docs / docs (push) Successful in 29s
Playwright Tests / test-playwright (1, 2) (push) Successful in 3m33s
Playwright Tests / test-playwright (2, 2) (push) Successful in 2m3s
pre-commit / pre-commit (push) Failing after 3m9s
Test Backend / test-backend (push) Successful in 2m46s
Compose Smoke Test / test-compose (push) Successful in 39s
Playwright Tests / merge-reports (push) Successful in 1m47s
Let a sweep run more than four at a time, and name the run a failure was in
Concurrent runs sat at 4 whatever FLOW_MAX_CASCADES said: that setting bounds
cascades, and the run drivers read a hardcoded MAX_PARALLEL nobody could reach.
FLOW_MAX_RUNS is the knob they read now, --max-runs/--max-cascades/--max-workers
are the same three as flags on serve, and the engine says which numbers it
started with — which is the only way to tell that a settings file was read.

Events keep the run they happened in. The payload always carried it and the
persist path dropped it, so reading one run's failures meant filtering the
engine-wide list; a batch run's id reaches those events now too, since a run
has no journaled item to name itself by.

Also: a provisioner's 0 means "no deadline" rather than "cancel on the next
reconcile", and a command that reaches no engine says how to start one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015sbYeYaVgYQqm1sbx7wPdL
2026-08-27 14:17:51 +02:00

227 lines
9.7 KiB
Markdown

# The HTTP API
Everything the browser does, the API does first. The dashboard is a generated
client of this schema, not a privileged path into the engine — so anything you
can click, you can script.
Base URL: `https://api.${DOMAIN}/api/v1`, or `http://127.0.0.1:8000/api/v1` for
a `fluksio serve` installation.
```sh
export FLUKSIO=http://127.0.0.1:8000/api/v1
```
## Authenticating
```sh
export TOKEN=$(curl -s -X POST $FLUKSIO/login/access-token \
-d "username=admin@example.com&password=..." | jq -r .access_token)
curl -s $FLUKSIO/users/me -H "Authorization: Bearer $TOKEN"
```
A bearer token, valid for eight days. `POST /login/test-token` checks one.
Password recovery and reset are `POST /password-recovery/{email}` and
`POST /reset-password/`.
Agents authenticate differently — see [Agents over MCP](agents.md).
!!! tip "The interactive schema"
When `ENVIRONMENT` is not `production`, the full OpenAPI schema is at
`/docs` (Swagger) and `/redoc`, and the raw document at
`/api/v1/openapi.json`. That is the authoritative reference; this page is
the tour.
It is closed in production on purpose: the schema enumerates every endpoint
the installation serves, including the paths webhook nodes mounted at
runtime.
## Flows
| Method | Path | What |
|---|---|---|
| `GET` | `/flows/` | every flow, with node counts, draft and running state |
| `GET` | `/flows/{name}` | one flow — the draft if there is one — with node status and issues |
| `PUT` | `/flows/{name}` | save a draft (`version` must match, or 409) |
| `POST` | `/flows/{name}/publish` | put the draft live |
| `POST` | `/flows/{name}/discard-draft` | throw the draft away |
| `POST` | `/flows/{name}/rename` | rename it |
| `DELETE` | `/flows/{name}` | delete it |
| `GET` | `/flows/node-types` | every node type and its parameter schema |
| `GET` | `/flows/graph` | every flow as one graph — what Home draws |
Reading a flow returns the *draft* when one exists, along with a preview of how
it would run: node statuses and validation issues for the version you are
editing, not the one running underneath it.
### Node source
```sh
curl -s $FLUKSIO/flows/house/nodes/decide/source -H "Authorization: Bearer $TOKEN"
curl -X PUT $FLUKSIO/flows/house/nodes/decide/source -H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' -d '{"code": "def process(x):\n return {\"y\": x * 2}\n"}'
```
`POST .../share` promotes a node's source to the shared library;
`POST .../unshare` gives it a private copy back. `GET /flows/library` lists the
shared sources and which flows use each.
### Running and controlling
| Method | Path | What |
|---|---|---|
| `POST` | `/flows/{name}/start` · `/stop` | activate or tear down its subscriptions and schedules |
| `POST` | `/flows/{name}/pause` · `/resume` | hold messages instead of running them |
| `POST` | `/flows/{name}/step` | release exactly one held message |
| `POST` | `/flows/{name}/validate` | the issues, without saving |
| `POST` | `/flows/{name}/run` | run every node once, from the values you pass |
| `POST` | `/flows/{name}/nodes/{id}/trigger` | feed values into one node |
| `POST` | `/flows/{name}/nodes/{id}/cancel` | kill the worker running it right now |
| `POST` | `/flows/{name}/nodes/{id}/acknowledge` | clear the failure the canvas is marking |
`POST /flows/{name}/run` on a **batch** flow submits a run instead, because
that is what running one means — the parameters, the series and the result are
the point, and a call that quietly did something else would be a trap.
## Messages
```sh
curl -s $FLUKSIO/messages/ -H "Authorization: Bearer $TOKEN"
curl -X POST $FLUKSIO/messages/house.setpoint -H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' -d '{"value": 22.5}'
curl -s $FLUKSIO/messages/house.temperature/history -H "Authorization: Bearer $TOKEN"
```
`GET /messages/` is the catalogue: every message any published flow declares,
with its last value. Publishing puts a value into the graph exactly as a
dashboard control does — which means only a message some flow *declares* can be
published to. Flows own the namespace; everything else is a client of it.
## Runs
| Method | Path | What |
|---|---|---|
| `POST` | `/runs/flows/{name}` | queue one run — `{"params": {...}, "seed": 7, "draft": false, "no_cache": false}`. `"cause"` says where it came from — `api` (the default), `cli` or `sdk` |
| `POST` | `/runs/flows/{name}/sweep` | queue many, sharing a `group_id` |
| `GET` | `/runs` | the queryable history: `?flow=`, `?status=`, `?group=`, `?digest=`, `?limit=`, `?offset=` |
| `GET` | `/runs/overview` | one row per flow that has runs, with how many are running or queued |
| `GET` | `/runs/{id}` | one run in full: params, result, per-node record, artifacts |
| `POST` | `/runs/{id}/cancel` | stop it |
| `GET` | `/runs/{id}/metrics?name=&stride=` | one metric's series, in step order; every series of the run without `name` |
| `GET` | `/runs/series/compare?ids=a,b,c&metric=&x=` | that metric across several runs. `x` is what to plot against: nothing or `step`, `time` (seconds since each run's own first reading), or another metric's name, joined on the step the two share |
Submitting answers immediately with a `queued` run. Wrong parameters — an
undeclared name, a value of the wrong type — come back as a 422 naming the
problem, before anything executes.
`?digest=` filters by the hash of the parameters, which is how you find "every
run that used exactly this configuration".
`compare` answers in the same shape a chart widget draws, so three training
curves side by side is a widget binding rather than a screen of its own.
See [Runs: pipelines that finish](../concepts/runs.md).
## Artifacts
```sh
curl -X PUT $FLUKSIO/artifacts -H "Authorization: Bearer $TOKEN" \
--data-binary @model.pt
curl -s $FLUKSIO/artifacts/sha256:abc... -H "Authorization: Bearer $TOKEN" -o model.pt
```
Content-addressed, so uploading the same bytes twice stores them once. Node
code normally reaches these through `fluksio.save_artifact` /
`fluksio.load_artifact` rather than here.
## Dashboards and panels
| Method | Path | What |
|---|---|---|
| `GET` | `/dashboards/` | every dashboard, without its contents |
| `GET` | `/dashboards/{name}?draft=true` | the published document, or the editor's copy |
| `POST` | `/dashboards/{name}` | create one |
| `POST` | `/dashboards/from-flow/{flow}` | build `{flow}_results` from a batch flow's declared ports — a chart per streamed metric, a stat per output. 409 if it already exists |
| `PUT` | `/dashboards/{name}` | save a draft |
| `POST` | `/dashboards/{name}/publish` · `/discard` · `/rename` | |
| `GET` `PUT` | `/panels/` | which device shows which dashboards |
| `POST` | `/panels/pair` | start a pairing |
| `GET` | `/panels/pair/{code}` | what is holding a code |
| `POST` | `/panels/{id}/unpair` | drop this panel's credential, keep the panel |
## Secrets, modules, alerts
```sh
curl -s $FLUKSIO/secrets/ -H "Authorization: Bearer $TOKEN" # names only
curl -X PUT $FLUKSIO/secrets/influx-token -H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' -d '{"value": "..."}'
curl -s $FLUKSIO/modules/ -H "Authorization: Bearer $TOKEN"
curl -X POST $FLUKSIO/modules/apply -H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' -d '{"requirements": "numpy>=2\n"}'
curl -s $FLUKSIO/alerts/config -H "Authorization: Bearer $TOKEN"
curl -X POST $FLUKSIO/alerts/test/my-phone -H "Authorization: Bearer $TOKEN"
```
Secrets are write-only over the API: you can list names and set values, never
read one back.
## Observability
| Path | What |
|---|---|
| `/observability/summary` | engine health — always 200, degraded or not |
| `/observability/timeseries` | executions and failures over a window |
| `/observability/flows` | per-flow rollups with a 60-slice trend |
| `/observability/runs` | recent cascades, with `?flow=`, `?since=`, `?until=` |
| `/observability/events?kind=failure\|audit` | what went wrong, or who changed what; narrows by `?flow=`, `?run=`, `?since=`, `?until=` |
| `/observability/dead-letter` | work the engine gave up on |
`GET /utils/health/` is the deep health check the container probe uses: it
fails when the event loop is wedged or the state backend is gone, not just when
the process is dead.
## Workers
| Method | Path | What |
|---|---|---|
| `GET` | `/workers` | what is attached, its labels and how busy it is |
| `POST` | `/workers/tokens` | mint a worker credential (superuser; shown once) |
| `GET` | `/workers/runtime` | the node runner's source, for a host without pip |
| `WS` | `/workers/attach` | where a worker dials in |
See [Remote workers](workers.md).
## Live events
```text
ws://127.0.0.1:8000/api/v1/flows/ws?token=<access token>
```
The websocket authenticates from its query string. It sends a snapshot on
connect and then every engine event: node executions, values published, health
changes, run started and finished. This is what the canvas and the dashboards
draw from.
## Errors
| Code | Means |
|---|---|
| `400` | the request was malformed, or the node refused it with a message |
| `401` / `403` | not signed in, or not allowed |
| `404` | no such flow, dashboard, run or message |
| `409` | someone else saved first — the body carries `current_version` |
| `422` | a parameter, port or binding did not typecheck |
| `503` | that subsystem is not available on this installation |
A 409 on a save or a publish is not an error to retry blindly: it means the
stored version moved past the one you were editing. Re-read, merge, save again.
## Generating a client
The frontend's TypeScript client is generated from the OpenAPI schema
(`make generate-client`). Any OpenAPI generator will do the same for your
language — point it at `/api/v1/openapi.json` on a non-production installation.