Backend API guide¶
This is the contract-oriented reference for the currently wired backend surfaces.
When to use this page¶
Use this page when integrating a client, validating endpoint contracts, or triaging Gateway-to-TACTIC errors.
Base URLs¶
| Service | Local base URL | Interactive schema |
|---|---|---|
| API Gateway | http://localhost:8000/v1 |
http://localhost:8000/docs |
| TACTIC | http://localhost:8001/api/v1 |
http://localhost:8001/docs |
Deployed installations serve the gateway under /api on the dashboard host and
start it with --root-path /api. Its schema at /api/openapi.json therefore
names /api under servers, and the interactive schema opens at /api/docs.
The gateway's OpenAPI document is written for clients that read it instead of
this page, AI agents included. Every operation is named after its handler, has
a description, and protected operations declare the bearerAuth scheme rather
than an Authorization parameter. The document's introduction maps the web
app's words (sources, readings, monitors, alarms) to the API's. Contract tests
in backend/tests/contracts/test_gateway_openapi.py keep it that way.
Authentication expectations¶
| Endpoint group | Client behaviour | Current enforcement note |
|---|---|---|
| Authentication | Public calls without a bearer token | Public by design |
| Devices and measurements | Send the login bearer token | Active, verified membership required |
| Monitors and alarms | Send the login bearer token | Members read; writes need manage_monitors or manage_alarms |
| Favourites | Send the login bearer token | Server selects the current member as owner |
| Members and roles | Send the login bearer token | Profile and roles for every member; manage_members changes them |
| Health and readiness | No token | Public operational probes in local deployments |
Important
Always send Authorization: Bearer <token> for non-authentication user workflows, and supply the private service credential only for trusted gateway-to-TACTIC calls.
Gateway API — /v1¶
Authentication¶
| Method | Path | Purpose | Key inputs |
|---|---|---|---|
POST |
/auth/accept-invitation |
Activate an invited account | Body: token, password |
POST |
/auth/login |
Authenticate and mint a JWT | Body: email, password |
POST |
/auth/password-reset |
Send reset mail when the account exists; the answer is the same either way | Body: email |
POST |
/auth/password-reset/confirm |
Set a new password with the token from the reset mail | Body: token, new_password |
POST |
/auth/refresh |
Rotate the remembered session cookie and mint a new JWT; 401 clears the cookie | Session cookie |
POST |
/auth/logout |
Revoke the supplied bearer token and remembered session, then clear its cookie | Current bearer token and session cookie, when present |
POST /v1/auth/login
Content-Type: application/json
{"email":"[email protected]","password":"<user-password>"}
Membership¶
| Method | Path | Purpose | Access |
|---|---|---|---|
GET |
/me |
Current profile | Active member |
GET |
/members |
List organization members | manage_members |
POST |
/invitations |
Invite or resend; body email, role |
manage_members |
PATCH |
/members/{member_id} |
Change role, is_active or both; fields left out keep their value |
manage_members |
DELETE |
/members/{member_id} |
Permanently remove a disabled member and personal favorites/sessions; 204 on success, 409 if active, 404 if missing | manage_members |
GET |
/permissions |
Every permission a role can grant, with a description | Active member |
GET |
/roles |
Roles with their permissions and how many members hold each | Active member |
POST |
/roles |
Create a role; body name, permissions; the id is derived from the name |
manage_members |
PATCH |
/roles/{role_id} |
Rename a role or replace its permissions | manage_members |
DELETE |
/roles/{role_id} |
Delete a role no member holds; 204 | manage_members |
Every active member can read everything outside the Dojo. A role grants the
permissions:
manage_sources (catalogue, collection and live preview), manage_monitors
(monitors and pipelines), manage_alarms (creating and deleting alarms),
use_dojo (the Dojo, reads included) and manage_members (members and roles). GET /me returns the member's
role and the permissions it grants. Two roles are built in: admin
(Administrator) always holds every permission and cannot be changed, and user
(Member) is what an invitation gives unless it names another role. Neither can be
deleted. A change that would leave no active member able to manage members is
refused with 409. Account and token lifecycle details are in
Single-organization operation.
Devices and measurements¶
Devices and readings are named by their catalogue ids, devices[].id and
readings[].id of GET /v1/catalogue. The installation resolves where their
values are stored.
| Method | Path | Purpose | Key inputs |
|---|---|---|---|
GET |
/devices |
Catalogue devices with their reading_ids, online and last_seen |
Query: optional active |
GET |
/devices/{device_id}/readings |
The catalogue readings of one device | Path: device_id |
GET |
/readings/{reading_id}/measurements |
Recorded values of one reading | Query: limit, cursor fields, paginated |
GET |
/readings/{reading_id}/measurements/buckets |
A downsampled range: each time bucket's lowest and highest value | Query: start (omit for all stored history), end (default now), buckets (10–2000, default 600) |
Measurement cursors:
- Use
after_timestampfor historical event-time pagination. - Use
after_createdandafter_event_timestamptogether for live ingestion-order pagination. - Do not mix historical and live cursor forms.
Catalogue, recording and live views¶
The catalogue document holds brokers, devices, readings and groups;
devices and readings name their broker as broker_id. A broker's protocol is
mqtt or http. On an HTTP broker a reading's topic is its request path,
and the reading may carry a timestamp_pointer; the field is absent from
readings that have none. Changes need
manage_sources, which TACTIC checks.
| Method | Path | Purpose | Access |
|---|---|---|---|
GET |
/catalogue |
Saved document, revision, how each reading is recorded (runtime.bindings) and can_edit |
Member |
PUT |
/catalogue |
Save structural changes at expected_revision |
manage_sources |
GET |
/catalogue/revisions |
Saved catalogue revisions | Member |
POST |
/brokers/check |
One MQTT CONNECT to a saved broker (broker_id), then disconnect; never subscribes. For an HTTP broker, one GET for the path of its first reading |
manage_sources |
POST |
/brokers/{broker_id}/reconnect |
Restart recording from a broker now, resetting its retry backoff | manage_sources |
PUT |
/recording |
Apply confirmed recording policies for reading_ids, or rule_changes for a broker, topic or reading scope |
manage_sources |
POST |
/recording/preview |
Review a recording change and the monitors it affects | manage_sources |
POST |
/recording/status |
Recording state and latest values of the given reading_ids |
Member |
GET |
/recording/health |
Recording diagnosis (first failing check, with code and explanation) plus the raw ingress/collector reports |
Member |
GET |
/storage |
Storage size and managed retention status | Member |
GET |
/live/measurements |
Open live views: brokers, readings and samples after after_sequence |
Member |
POST |
/live/views |
Open a bounded live view of reading_ids; answers its view_id and first measurements |
manage_sources |
POST |
/live/views/{view_id}/renew |
Keep a live view open, optionally with other reading_ids |
manage_sources |
DELETE |
/live/views/{view_id} |
Close a live view; 204 | manage_sources |
Catalogue documents use only aberration-system schema version 1. New inputs
remain Off regardless of policy metadata in an import. The UI reviews structural
replacement before saving; existing selections survive by stable reading ID.
Files exported before the rename, with connections, streams and
connection_id, still import.
/recording/health decides the recording diagnosis on the server. The checks run
in a fixed order (catalogue saved, collector report younger than 15 seconds,
local MQTT connected, revisions applied, broker routes, broker connections,
database, traffic) and the first that fails is returned as diagnosis.code with
a title, tone and explanation. The web app shows that verdict unchanged; it only
adds "Status not refreshing" when the endpoint itself stops answering.
Live views are explicitly bounded in-memory previews. Opening one requires
manage_sources; members can read the measurements of an open view. A live view
does not record anything. Accepted detector topics use only
telemetry/<entity>/<reading>; upstream topics can use arbitrary concrete MQTT
paths and JSON Pointer payload bindings.
Monitors, pipelines and detectors¶
A monitor is named by its monitor_id everywhere; container names, container
ids and Docker states stay inside the installation. Members read everything
here; changes need manage_monitors.
| Method | Path | Purpose | Key inputs |
|---|---|---|---|
GET |
/monitors |
List monitors with their state, strategy and watched readings | Optional active |
POST |
/monitors |
Start a form-configured monitor as a pipeline (HTTP 202) | Monitoring configuration body |
GET |
/monitors/overview |
What the Monitoring page loads: every monitor, whether designs may be edited, and the selected monitor's pipeline state | Optional selected_monitor_id |
GET |
/monitors/{monitor_id} |
One monitor with the configuration it was started with | Path: monitor_id |
PATCH |
/monitors/{monitor_id} |
Rename a monitor | Body: display_name |
POST |
/monitors/{monitor_id}/stop |
Stop a monitor and remove its workload; it stays listed as stopped | Path: monitor_id |
DELETE |
/monitors/{monitor_id} |
Delete a monitor and its workload; 204, its alarms stay | Path: monitor_id |
GET |
/monitors/{monitor_id}/pipeline |
The design a monitor runs, its current run, recent history and validation | Optional selected_node_id |
GET |
/monitors/{monitor_id}/pipeline/history |
Earlier runs it replaced, newest first; the next page is the history of next_monitor_id |
limit |
GET |
/monitors/{monitor_id}/pipeline/timeline |
The run's lifecycle events, oldest first, and how it ended | Path: monitor_id |
GET |
/monitors/{monitor_id}/pipeline/metadata |
Validation with the fields available at a node | selected_node_id |
GET |
/monitors/{monitor_id}/pipeline/state |
The diagnostic snapshot the running monitor last reported | Path: monitor_id |
PUT |
/monitors/{monitor_id}/pipeline/layout |
Store where its nodes sit on the canvas; the graph is unchanged | Body: layout |
GET |
/pipelines |
Saved designs, paged | limit, offset, query, archived |
POST |
/pipelines |
Save a new design as a draft | Body: name, definition |
POST |
/pipelines/apply |
Save, publish and activate a design in one step (HTTP 202) | Body: definition, bindings, display_name, idempotency_key, optional replace_monitor_id |
POST |
/pipelines/validate |
Check an unsaved design; with selected_node_id also its fields |
Body: definition |
GET |
/pipelines/{pipeline_id} |
One design with its draft, version and revisions |
Path: pipeline_id |
PATCH |
/pipelines/{pipeline_id} |
Rename, describe or archive a design | Body: expected_version and changes |
PUT |
/pipelines/{pipeline_id}/draft |
Replace a design's draft graph and layout | Body: expected_version, definition |
POST |
/pipelines/{pipeline_id}/validate |
Check a graph against a saved design | Body: definition |
GET |
/pipelines/{pipeline_id}/revisions |
Published revisions, newest first | limit, before |
POST |
/pipelines/{pipeline_id}/revisions |
Freeze the draft as a revision monitors can run | Body: expected_version |
GET |
/pipelines/{pipeline_id}/revisions/{revision_id} |
One revision with its graph and fingerprint | Path ids |
GET |
/pipelines/{pipeline_id}/revisions/{revision_id}/metadata |
Validation of a revision with the fields at a node | selected_node_id |
GET |
/pipeline-components |
Components a design may use, with parameter schemas, and the starting templates | None |
GET |
/pipeline-blocks |
Every saved block, by name: items, each with id, name, description, steps and created_at |
None |
POST |
/pipeline-blocks |
Save a chain of steps as a block (HTTP 201). HTTP 409 when the name is taken or the library holds 100 blocks; 422 for a step a block cannot hold, such as a detector | Body: name, optional description, steps (1 to 16, each with component and optional contract_version, params, label) |
DELETE |
/pipeline-blocks/{block_id} |
Delete a block (HTTP 204); designs keep the steps they copied from it | Path: block_id |
GET |
/forecast-models |
Models of the forecast service that pretrained forecaster steps use, each with its download status, progress and error; service is not_deployed or unreachable when there is none to list |
None |
POST |
/forecast-models/downloads |
Have the forecast service download a model's weights (HTTP 202); poll the list for progress | Body: model |
POST |
/pipeline-activations |
Run a published revision as a monitor (HTTP 202) | Body: revision_id, bindings, display_name, idempotency_key, optional replace_monitor_id |
GET |
/pipeline-activations/{activation_id} |
Progress of an activation: phase, monitor_id, issues |
Path: activation_id |
POST |
/pipeline-activations/{activation_id}/cancel |
Cancel an activation that has not finished | Path: activation_id |
GET |
/detectors |
Detector models with family, cost and parameter schema | Optional strategy |
GET |
/evidence |
Inference evidence of the observations that covered one reading | reading_id, limit |
GET |
/evidence/chart |
Incrementally load chart evidence | device_id, or reading_id repeated for up to 200 readings, optional monitor_id, optional complete cursor, limit |
Evidence rows name their monitor_id and the reading_ids they covered;
input_timestamps is keyed by reading id. The chart cursor is the complete
tuple after_created, after_timestamp, after_monitor_id, and
after_sequence_number; if one is supplied, all four are required.
Every listed monitor, here and in /monitors/overview, carries reading_ids:
the catalogue readings it watches. Use it to answer "which monitors watch this
reading"; a reading removed from the catalogue drops out. A monitor's
operational_status names its inputs by reading id as well. How a reading is
recorded (its topic and ingress key) stays inside the backend. active is true
while the monitor should run and false once it is stopped.
A monitor start body contains:
- required
display_nameandreading_ids: up to 100 distinct numeric readings that are recorded, all of one device or all of one broker without a device monitoring: the strategy configuration, includingstrategy(static_baselineoradaptive_stream),model_type,model_params, and the strategy's training, calibration, and alarm settings- optional
performance_config:sensor_freshness_seconds,observation_max_age_seconds,observation_future_tolerance_secondsandalignment_max_skew_seconds
Omitting monitoring selects the default static baseline. Other fields,
including the former top-level mqtt_topics, strategy, model_type and
model_params, and malformed configuration return HTTP 422.
TACTIC converts the configuration into its equivalent graph, binds one input
per reading and submits a pipeline activation, even while the visual workbench
is disabled. The graph's inputs carry the readings' catalogue labels. The HTTP
202 response carries monitor_id, activation_id, phase and reading_ids.
Poll /pipeline-activations/{activation_id} until its phase is succeeded,
failed or cancelled. A reading that is unknown, not numeric, not recorded or
of another device returns HTTP 422.
A run view (/monitors/{monitor_id}/pipeline) names its bound inputs by
slot_id, reading_id and unit. Its binding_metadata keeps the
catalogue_revision and collection_revision the run was bound against and,
per reading, the catalogue label, unit, device_id, device_label,
broker_id, broker_label and collection_policy at that time.
Use /detectors as the source for valid model names and parameter schemas.
bindings map each input slot of a design to a catalogue reading id. Repeating
an apply or activation with the same idempotency_key returns the first
activation instead of starting another; replace_monitor_id swaps out a running
monitor. Direct public graph launches are rejected so they cannot bypass
preflight, durable input reservations or run history.
Draft/layout conflicts return HTTP 409. Publishing does not activate a revision.
Run views survive deletion of the live monitor and expose retained input metadata,
terminal errors and compiler feature lineage. /monitors/{monitor_id}/pipeline
accepts an optional selected_node_id for member inspection even when authoring
is disabled. Each compiler report is
bounded to 512 KiB, omitting provenance and then whole unselected node tables
with a metadata_budget notice. Complete ordered names for the selected node
and its immediate parents remain available; inspect features_complete and
provenance_complete before interpreting missing metadata. Invalid oversized
definition echoes use definition: null and definition_omitted: true; bounded
errors expose issues_truncated. Internal MQTT overrides and runtime image pins
are rejected by public monitor creation.
See pipeline architecture and rollout.
Favourites and alarms¶
| Method | Path | Purpose | Key inputs |
|---|---|---|---|
GET |
/favorites |
The current member's favourites, oldest first | Optional query: kind |
POST |
/favorites |
Star a device, reading or monitor; an existing favourite is returned unchanged | Body: kind, id |
DELETE |
/favorites |
Remove a favourite; 204 whether or not it existed | Query: kind, id |
GET |
/alarms/count |
Count alarms | Optional query: since |
POST |
/alarms/query |
Reporting: alarms of catalogue readings with catalogue names, a summary and per-reading counts; every member | Body: reading_ids, optional reading_id, monitor_id, since, until, max_p_value, limit, before |
GET |
/alarms |
The newest alarms of a device, of readings, or of every reading | Query: device_id or reading_id (repeated, up to 200), optional monitor_id, limit |
POST |
/alarms |
Record an alarm by hand and email the alert recipients; 201 with the alarm | Body: reading_ids, timestamp, alarm_type, value, optional value_type |
DELETE |
/alarms/{alarm_id} |
Delete one alarm; 204, or 404 when there is none | Path: alarm_id |
A favourite's kind is device (a catalogue device id), reading (a catalogue
reading id) or monitor (a monitor_id). Ids are not checked against the
catalogue, so a favourite stays until the member removes it. A member keeps at
most 256; adding another answers 409.
An alarm names its catalogue device_id and reading_ids with their names in
device_label and reading_labels. A reading without a device has a null
device_id, and device_label names its broker. Readings the catalogue no
longer has are left out. multivariate marks an alarm a monitor raised over
several readings together. A manual alarm names readings of one device, or of
one broker without a device; several make it multivariate.
POST /alarms/query is a read, so members may call it although other POST
routes under /alarms need manage_alarms. An alarm is in the reading_ids
scope when one of its readings is. A multivariate alarm recorded without its
readings is only in scope when every reading of its device is. summary and
readings cover every match rather than the returned page; readings ignores
the narrowing filters so it can feed a reading picker. Pages are newest first;
pass next as before.
Dojo¶
Datasets and runs for comparing detection approaches offline, apart from live
monitoring. A dataset is a frozen copy: retention and catalogue edits never
change it. A run replays one design over one dataset. Every route needs
use_dojo, reads included, because datasets are copies made for experiments.
| Method | Path | Purpose | Key inputs |
|---|---|---|---|
GET |
/dojo/datasets |
List datasets, newest first | limit, offset |
POST |
/dojo/datasets/csv |
Create a dataset from a CSV request body (text/csv, at most 100 MB) |
Query: name, layout (wide or long), optional timestamp_column, series_column, value_column, delimiter, filename, description |
POST |
/dojo/datasets/history |
Copy recorded readings for one window into a dataset | Body: name, reading_ids, end, optional start (omitted: from the first stored value) |
GET |
/dojo/datasets/{dataset_id} |
One dataset with its series | Path: dataset_id |
PATCH |
/dojo/datasets/{dataset_id} |
Rename or describe a dataset | Body: name, description |
DELETE |
/dojo/datasets/{dataset_id} |
Delete a dataset and its values; 204 | Path: dataset_id |
GET |
/dojo/datasets/{dataset_id}/series/{series_id}/points |
Values of one series in time order | after, limit (at most 10,000); continue with next_after |
GET |
/dojo/datasets/{dataset_id}/labels |
The dataset's labels, ordered by start | Path: dataset_id |
GET |
/dojo/datasets/{dataset_id}/label-suggestions |
Alarms live monitors recorded on the readings the dataset was copied from, inside its span, grouped into episodes as candidate labels; nothing is stored, and an upload gets none | Optional gap_seconds (default 600): alarms closer together form one suggestion |
POST |
/dojo/datasets/{dataset_id}/labels |
Mark a known problem as an interval | Body: start_at, end_at, optional note |
POST |
/dojo/datasets/{dataset_id}/labels/batch |
Add up to 200 labels in one write: all of them, or none when one overlaps or the dataset would hold more than 500; 201 with the new labels | Body: labels, each with start_at, end_at, optional note |
PATCH |
/dojo/datasets/{dataset_id}/labels/{label_id} |
Move a label or change its note | Body: start_at, end_at, note |
DELETE |
/dojo/datasets/{dataset_id}/labels/{label_id} |
Delete a label; 204 | Path: dataset_id, label_id |
GET |
/dojo/datasets/{dataset_id}/comparison |
Up to six finished runs of the dataset, scored against its labels | run_ids, repeated once per run |
GET |
/dojo/runs |
List runs, newest first | Optional dataset_id, status; limit, offset |
POST |
/dojo/runs |
Queue a replay of one design over one dataset | Body: dataset_id, definition, bindings (slot id to series id), optional name, alignment, compare_calibration |
GET |
/dojo/runs/{run_id} |
One run with its design, progress and counts | Path: run_id |
POST |
/dojo/runs/{run_id}/cancel |
Cancel a waiting or running run | Path: run_id |
POST |
/dojo/runs/{run_id}/promote |
Save the run's design as a library template; 201. Also needs manage_monitors |
Body: name, optional description |
DELETE |
/dojo/runs/{run_id} |
Delete a finished run and its results; 204 | Path: run_id |
GET |
/dojo/runs/{run_id}/metrics |
Event metrics of a finished run against the dataset's labels | Path: run_id |
GET |
/dojo/runs/{run_id}/calibration |
The calibration methods a finished run also tried on its scores, each scored against the labels; 409 for a run started without compare_calibration |
Path: run_id |
GET |
/dojo/runs/{run_id}/results |
Detector results in order | after, limit (at most 10,000), optional alarm; continue with next_after |
A wide CSV has one timestamp column (the first unless timestamp_column
names another) and one series per remaining column; a long CSV has one value
per row in series, timestamp and value columns. Timestamps are ISO 8601
or Unix epoch seconds or milliseconds, UTC without a zone. The delimiter is
detected from the header; with ; or tab a decimal comma is accepted. A
dataset holds at most 64 series and 5,000,000 values, and two values for the
same series and instant are rejected.
A run is compiled and bound when it is created, so a design error is refused
at once. The Dojo worker, a service on the RADAR image, then replays the bound
series in time order through the same alignment and detector code as a live
monitor. The clock is the data's own time, so alignment.freshness_seconds
and alignment.max_skew_seconds mean what the live monitor settings mean. A
static baseline's fitting is awaited instead of discarding observations, so
the same run gives the same results again. Each worker replica executes one
run at a time; at most 20 runs wait or run at once. A run's stats count
observations that produced no result, by reason.
A label marks a known problem as the interval from start_at to end_at.
Labels of one dataset may touch but not overlap and must overlap the dataset's
time span; a dataset holds at most 500. Metrics treat each label as one event
and are computed when read, from the stored results and the current labels,
so editing a label re-scores every finished run without a replay. A label is
detected when the run alarmed inside it, ends included, with
delay_seconds from its start to the first alarm; missed when the run
scored it without alarming; and unscored when the run had no scores during
it, for example while the detector was still learning. detection_rate
divides detected labels by scored ones. Each alarm onset outside every label
is one false alarm, so an alarm held over many results counts once, and one
that starts inside a label and outlasts it does not count.
false_alarms_per_day divides them by evaluated_seconds, the scored time
outside the labels. Only runs with status succeeded have metrics.
A run created with compare_calibration also tries the other calibration
methods on its own detector scores: the fixed split and the 15 methods that
adapt, without the run's own. Each gets the calibration window and then every
scored observation, as the run's own calibration step does, and nothing a
method decides reaches the run. All aim at one alarm rate, alpha, and alarm
after consecutive scores in a row above their boundary. A run whose step
adapts gives its own alpha and the consecutive of its detection rule; a
fixed split gives the share its quantile leaves above the threshold, at least
one in n + 1 for a calibration window of n, and consecutive 1. The other
methods run with their default settings. A design that alarms on a conformal
martingale has no boundary on scores and is refused with 422.
GET /dojo/runs/{run_id}/calibration lists the run's own step first
(own), then the others, each with the event metrics above counted from the
first result the methods judged, and alarm_share, the share of judged
results it would have alarmed on. A method that raised an error on the scores
stops there and says why in failed; the run itself carries on. Its counts
are of what it found until then, and alarm_share, detection_rate and
false_alarms_per_day are null, because they would be over results it never
judged.
Promoting a run copies its compiled design into the pipeline library as a new
template, the same as POST /pipelines, so pipeline authoring must
be enabled and the member needs manage_monitors. The answer names the new
pipeline_id and lists each input slot with the series it was bound to. For a
series copied from recorded history, reading_id is the catalogue reading it
came from, the live input to bind when the template starts a monitor; an
uploaded series has none. The template has no published revision yet.
Import and metrics bounds¶
CSV imports accept up to 65 columns per record (a timestamp and 64 series), 64 Ki characters per logical record, and 256 characters per series name. These limits also apply to quoted multiline records and unused columns in long CSVs. Ordinary wide and long CSV uploads retain their existing overall upload limits.
Run, run-comparison, and calibration-comparison metrics cache their bounded aggregates until label intervals change. An uncached calculation uses a shared database slot and returns a retryable conflict when another calculation holds it. Calculations have a 30-second request deadline and a 25-second database statement deadline. These protections use built-in defaults and need no new environment variables.
Gateway health¶
| Method | Path | Purpose |
|---|---|---|
GET |
/health |
Gateway liveness; this route is outside the /v1 prefix |
TACTIC API — /api/v1¶
All business routes require X-Off-Key-Service-Key; collection, membership and
role routes additionally validate the end-user bearer token and its permissions. TACTIC is private.
Health and readiness remain available without this service credential.
The gateway is the only caller. Its routes above forward to these, under the
names the installation uses internally: a catalogue is the
system, recording is collection, a broker is a connection, a live view is
a lease and a monitor is a service.
Members, sessions and roles¶
| Method | Path | Purpose |
|---|---|---|
GET |
/members/me |
Profile of the signed-in member with role and permissions |
GET |
/members |
List organization members |
POST |
/members/invitations |
Invite a member or resend the invitation |
PATCH |
/members/{user_id} |
Change a member's role, active state or both |
DELETE |
/members/{user_id} |
Permanently remove a disabled member; 204 |
POST |
/members/accept-invitation |
Activate an invited account with its token and a password |
POST |
/members/request-password-reset |
Send reset mail when the account exists |
POST |
/members/reset-password |
Set a new password with the token from the reset mail |
POST |
/members/sessions |
Create the remembered session behind the gateway's session cookie |
POST |
/members/sessions/refresh |
Rotate a remembered session's token |
POST |
/members/sessions/revoke |
Revoke a remembered session |
POST |
/members/sessions/revoke-access |
Revoke the supplied bearer token |
GET |
/roles |
Roles with their permissions and member counts |
POST |
/roles |
Create a role |
PATCH |
/roles/{role_id} |
Rename a role or replace its permissions |
DELETE |
/roles/{role_id} |
Delete a role no member holds; 204 |
Catalogue, recording and live views¶
| Method | Path | Purpose |
|---|---|---|
GET |
/system |
Saved catalogue document, revision and how each reading is recorded |
PUT |
/system |
Save structural catalogue changes at an expected revision |
GET |
/system/revisions |
Saved catalogue revisions |
POST |
/system/connections/check |
One MQTT CONNECT to a saved broker, then disconnect; never subscribes. For an HTTP broker, one GET |
POST |
/system/connections/reconnect |
Retry a recorded broker now instead of waiting for its backoff |
PUT |
/system/collection |
Apply confirmed recording policies |
POST |
/system/collection/preview |
Review a recording change and the monitors it affects |
POST |
/system/collection/status |
Recording state and latest values of the given readings |
GET |
/system/health |
Recording diagnosis with the raw ingress and collector reports |
GET |
/system/storage |
Storage size and managed retention status |
GET |
/system/live |
Open live views: brokers, readings and samples after a sequence number |
POST |
/system/live/leases |
Open a bounded live view of readings |
PUT |
/system/live/leases/{lease_id} |
Keep a live view open, optionally with other readings |
DELETE |
/system/live/leases/{lease_id} |
Close a live view; 204 |
Pipelines and monitor runs¶
| Method | Path | Purpose |
|---|---|---|
GET |
/monitors/workspace |
What the Monitoring page loads: every monitor, whether designs may be edited, and the selected monitor's pipeline state |
GET |
/monitors/{service_id}/pipeline |
The design a monitor runs, its current run, recent history and validation |
GET |
/monitors/{service_id}/pipeline-history |
Earlier runs the monitor replaced, newest first |
GET |
/monitors/{service_id}/pipeline-timeline |
The run's lifecycle events, oldest first, and how it ended |
GET |
/monitors/{service_id}/pipeline-metadata |
Validation with the fields available at a node |
GET |
/monitors/{service_id}/pipeline-state |
The diagnostic snapshot the running monitor last reported |
PUT |
/monitors/{service_id}/pipeline-layout |
Store where the nodes sit on the canvas; the graph is unchanged |
GET |
/monitors/pipelines |
Saved designs, paged |
POST |
/monitors/pipelines |
Save a new design as a draft |
POST |
/monitors/pipelines/apply |
Save, publish and activate a design in one step |
POST |
/monitors/pipelines/validate |
Check an unsaved design without creating a library record |
GET |
/monitors/pipelines/catalog |
Components a design may use, with parameter schemas, and the starting templates |
GET |
/monitors/pipelines/forecast-models |
The forecast service's models and their download state, asked with TACTIC's token |
POST |
/monitors/pipelines/forecast-models/downloads |
Have the forecast service download a model's weights |
GET |
/monitors/pipelines/{pipeline_id} |
One design with its draft, version and revisions |
PATCH |
/monitors/pipelines/{pipeline_id} |
Rename, describe or archive a design |
PUT |
/monitors/pipelines/{pipeline_id}/draft |
Replace a design's draft graph and layout |
POST |
/monitors/pipelines/{pipeline_id}/validate |
Check a graph against a saved design |
GET |
/monitors/pipelines/{pipeline_id}/revisions |
Published revisions, newest first |
POST |
/monitors/pipelines/{pipeline_id}/revisions |
Freeze the draft as a revision monitors can run |
GET |
/monitors/pipelines/{pipeline_id}/revisions/{revision_id} |
One revision with its graph and fingerprint |
GET |
/monitors/pipelines/{pipeline_id}/revisions/{revision_id}/metadata |
Validation of a revision with the fields at a node |
GET |
/monitors/pipeline-blocks |
Every saved block, by name |
POST |
/monitors/pipeline-blocks |
Save a chain of steps as a block |
DELETE |
/monitors/pipeline-blocks/{block_id} |
Delete a block; designs keep the steps they copied from it |
POST |
/monitors/pipeline-activations |
Run a published revision as a monitor |
GET |
/monitors/pipeline-activations/{activation_id} |
Progress of an activation: phase, monitor and issues |
DELETE |
/monitors/pipeline-activations/{activation_id} |
Cancel an activation that has not finished |
Data adapter¶
| Method | Path | Purpose |
|---|---|---|
GET |
/data/devices |
Catalogue devices with recording status |
GET |
/data/devices/{device_id}/readings |
Catalogue readings of one device |
GET |
/data/readings/{stream_id}/measurements |
Measurements of a catalogue reading |
GET |
/data/readings/{stream_id}/measurements/buckets |
Downsampled measurements of a catalogue reading |
GET |
/data/evidence |
Evidence of a catalogue reading |
GET |
/data/evidence/chart |
Chart evidence of a catalogue device or of catalogue readings |
POST |
/data/auth/login |
Internal authentication check |
GET |
/data/users/{user_id}/favorites |
List favourites |
POST |
/data/users/{user_id}/favorites |
Add a favourite |
DELETE |
/data/users/{user_id}/favorites |
Remove a favourite named by query kind and id |
GET |
/data/alarms |
Alarms of a catalogue device or of catalogue readings |
POST |
/data/alarms/query |
Reporting query by catalogue reading |
GET |
/data/alarms/count |
Count alarms |
POST |
/data/alarms |
Record an alarm about catalogue readings |
DELETE |
/data/alarms/{alarm_id} |
Delete an alarm |
Dojo¶
| Method | Path | Purpose |
|---|---|---|
GET |
/dojo/datasets |
List datasets |
POST |
/dojo/datasets/csv |
Import a CSV body |
POST |
/dojo/datasets/history |
Copy recorded readings |
GET |
/dojo/datasets/{dataset_id} |
Read a dataset |
PATCH |
/dojo/datasets/{dataset_id} |
Rename or describe a dataset |
DELETE |
/dojo/datasets/{dataset_id} |
Delete a dataset |
GET |
/dojo/datasets/{dataset_id}/series/{series_id}/points |
Page through one series |
GET |
/dojo/datasets/{dataset_id}/labels |
List a dataset's labels |
GET |
/dojo/datasets/{dataset_id}/label-suggestions |
Suggest labels from recorded alarms |
POST |
/dojo/datasets/{dataset_id}/labels |
Add a label, refusing overlaps |
POST |
/dojo/datasets/{dataset_id}/labels/batch |
Add several labels as one write |
PATCH |
/dojo/datasets/{dataset_id}/labels/{label_id} |
Move or annotate a label |
DELETE |
/dojo/datasets/{dataset_id}/labels/{label_id} |
Delete a label |
GET |
/dojo/datasets/{dataset_id}/comparison |
Score runs of one dataset side by side |
GET |
/dojo/runs |
List runs |
POST |
/dojo/runs |
Compile, bind and queue a run |
GET |
/dojo/runs/{run_id} |
Read a run |
POST |
/dojo/runs/{run_id}/cancel |
Cancel a run |
POST |
/dojo/runs/{run_id}/promote |
Save a run's design as a template |
DELETE |
/dojo/runs/{run_id} |
Delete a finished run |
GET |
/dojo/runs/{run_id}/metrics |
Score a finished run against the labels |
GET |
/dojo/runs/{run_id}/calibration |
Score the calibration methods a run also tried |
GET |
/dojo/runs/{run_id}/results |
Page through a run's results |
RADAR orchestration¶
| Method | Path | Purpose |
|---|---|---|
GET |
/orchestration/radar/services/ |
List services |
POST |
/orchestration/radar/services/start/ |
Start a form-configured monitor as a pipeline activation |
GET |
/orchestration/radar/services/{service_id} |
Get one service with its configuration |
POST |
/orchestration/radar/services/{service_id}/stop |
Stop a service and remove its workload |
PATCH |
/orchestration/radar/services/{service_id}/label |
Rename a service |
DELETE |
/orchestration/radar/services/{service_id} |
Delete by service identifier |
Model catalogue¶
| Method | Path | Purpose |
|---|---|---|
GET |
/models/ |
List the detectors shipped in RADAR, optionally filtered by strategy |
The catalogue is served from code (off_key_core.models). Adding a model means
shipping its implementation and catalogue entry.
Middleware health¶
| Method | Path | Purpose |
|---|---|---|
GET |
/health |
Liveness; outside /api/v1 |
GET |
/ready |
Docker API readiness; outside /api/v1 |
Error model¶
| Status | Typical meaning |
|---|---|
200 / 201 |
Successful read or mutation |
400 |
Malformed request or unsupported transition |
401 / 403 |
Authentication or authorization failure |
404 |
Target not found |
409 |
Duplicate or conflicting state |
422 |
Semantic/input validation failure |
500 / 502 / 503 |
Application, upstream, or infrastructure failure |
FastAPI validation errors and Gateway-propagated TACTIC errors use a detail field; exact payload shape varies by layer.
Compatibility notes¶
- The Gateway API family remains
/favorites. The dashboard's Favorites filter uses/?state=favorites; the old/favouritesfrontend route redirects there. - Removed sync and preprocessor-discovery routes must not be used by new clients.