Writing node source, installing modules and any flow save that adds, retypes or repoints a code node (Function, Command), edits a Command node's settings or changes a node's resources.env now answer 403 for everyone but a superuser. MCP tools and the assistant reach the same routes, so they inherit the gate. Node types report runs_code. The editor shows code read-only with a note, disables a Command node's settings, leaves code nodes and reusable nodes out of the palette and paste, and the Modules page offers no install.
264 lines
13 KiB
Markdown
264 lines
13 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` instance.
|
|
|
|
```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: the schema enumerates every endpoint the
|
|
instance serves, including 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. Its runs stay — a run is the record of what ran, and only `DELETE /runs` clears one. Refused with 409 while a run of it is `running` or `queued` |
|
|
| `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"}'
|
|
```
|
|
|
|
Writing a source is a superuser's, and so is any `PUT /flows/{name}` that
|
|
adds a Function or Command node, retypes or repoints one, changes a Command
|
|
node's settings, or changes any node's `resources.env`: each of them is code
|
|
the engine runs. Anyone else gets a 403 and keeps the rest — wiring, titles,
|
|
a Function node's settings and every other node.
|
|
|
|
`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.
|
|
|
|
## 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` |
|
|
| `POST` | `/runs/import` | record runs executed somewhere else, up to 200 per request. Answers `[{"external_id", "id", "created"}]` |
|
|
| `GET` | `/runs` | the queryable history: `?flow=`, `?status=`, `?group=`, `?digest=`, `?since=`, `?before=`, `?limit=`, `?offset=` |
|
|
| `DELETE` | `/runs?flow=` | every run of one flow, and everything hanging off them. `flow` is required. Refused with 409 while one of them is still going |
|
|
| `GET` | `/runs/overview` | one row per flow that has runs, with how many are running or queued |
|
|
| `GET` | `/runs/export/metrics?…&name=&stride=&format=` | every selected run's series as one long table: `run, name, step, ts, value` |
|
|
| `GET` | `/runs/export/runs?…¶ms=&metrics=&format=` | one row per run: its inputs as columns, its final numbers, its status and provenance |
|
|
| `GET` | `/runs/metrics/names?…` | every metric name the selected runs recorded, distinct; takes the export's own filters |
|
|
| `GET` | `/runs/{id}` | one run in full: params, result, per-node record, artifacts |
|
|
| `POST` | `/runs/{id}/cancel` | stop it |
|
|
| `DELETE` | `/runs/{id}` | forget it, with its nodes, series and artifact rows. Cancel a live one first |
|
|
| `POST` | `/runs/{id}/retry` | run the same thing again, as a new run naming this one |
|
|
| `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.
|
|
|
|
`/runs/import` takes `{"runs": [...]}`. Each entry carries `flow`,
|
|
`external_id`, `params`, `seed`, `status` (`ok`, `error` or `cancelled`),
|
|
`created_at`, `started_at`, `finished_at`, `result`, `metrics` (`name`,
|
|
`step`, `ts`, `value`), `artifacts` (`name`, `filename`, `media_type`,
|
|
`content_base64`), `labels` and `group_id`. The parameters are completed and
|
|
checked as a submit's are, the run is stored with the cause `import` and an
|
|
empty commit and code digest, and nothing is queued. The request is checked
|
|
whole before anything is written, and an `external_id` already imported
|
|
answers with that run and `"created": false`.
|
|
|
|
`?digest=` filters by the hash of the parameters, which is how you find "every
|
|
run that used exactly this configuration".
|
|
|
|
`?before=` is how a long history is paged: rows come newest first, so handing
|
|
back the last row's `created_at` reads the next page whatever landed
|
|
meanwhile, which `?offset=` cannot, since a run submitted between two pages
|
|
shifts every row down one. `?since=` is inclusive and bounds the other end.
|
|
|
|
Both exports stream `csv` (the default) or `jsonl`, and take the selection the
|
|
history takes plus `?ids=a,b,c`, `?since=` and `?until=`. `export/runs` puts
|
|
the inputs that *vary* across the selection in `param.` columns (the sweep
|
|
axis) unless `?params=` names them, and the run's numbers in `metric.`
|
|
columns. Both are dotted paths into whatever a node returned, so
|
|
`?metrics=final_metrics.train_loss` selects a field of a record and the
|
|
defaults reach every number inside one. A key with a dot in its own name, such
|
|
as `-1.0`, cannot be addressed that way and gets no column. The run id is on every row of both, which is what makes an exported
|
|
file a join back to the run rather than a loose table.
|
|
|
|
`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. Secrets and `POST /modules/apply` are a superuser's.
|
|
|
|
## Observability
|
|
|
|
| Path | What |
|
|
|---|---|
|
|
| `/observability/summary` | engine health — always 200, degraded or not — and the `version` it is running |
|
|
| `/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 instance |
|
|
|
|
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 instance.
|