MotorPulse AI exposes a local-only synthetic simulation API from the standard-library HTTP server in motorpulse/__main__.py. It binds to 127.0.0.1; it is not an authenticated production service, a remote-control interface, or a physical motor diagnostic API. Do not port-forward it or connect it to machinery.
The default base URL is http://127.0.0.1:8765. Use --port to select another loopback port. All JSON responses set Content-Type: application/json; charset=utf-8, Cache-Control: no-store, and X-Content-Type-Options: nosniff.
| Method | Path | Purpose | Persistence required? |
|---|---|---|---|
| GET | /api/health |
Process/API liveness for the synthetic app | No |
| GET | /api/snapshot |
Read the cached current telemetry snapshot without stepping | No |
| GET | /api/status |
Read controller state, seed, clocks, tick and optional storage/session status | No |
| GET | /api/stream |
Stream read-only latest synthetic snapshot/status state with Server-Sent Events | No |
| GET | /api/config |
Read the complete active per-asset synthetic scenario configuration | No |
| PUT | /api/config |
Atomically validate and replace all asset scenarios, pause and reset | No |
| PUT | /api/runtime |
Atomically replace deterministic seed and clock settings, pause and reset | No |
| POST | /api/control |
start, pause, step, or reset the synthetic controller |
No |
| GET | /api/history/sessions |
Read newest-first recorded SQLite sessions | Yes |
| GET | /api/history/snapshots |
Read recorded snapshot rows | Yes |
| GET | /api/history/events |
Read recorded per-asset telemetry/event rows | Yes |
| GET | /api/history/alerts |
Read original per-tick synthetic rule-alert rows | Yes |
| GET | /api/history/incidents |
Read deduplicated synthetic rule incidents | Yes |
| GET | /api/history/transitions |
Read incident lifecycle/review transitions | Yes |
| POST | /api/incidents/acknowledge |
Add a once-only local reviewer annotation to one recorded incident | Yes |
There is no GET /api/runtime. Current runtime settings are part of GET /api/status.
Returns HTTP 200:
{"status":"ok","source":"simulation"}This only proves that the local demo HTTP process is responding. It is not a health assessment of physical equipment.
Returns the cached v1 synthetic telemetry snapshot. Reads are side-effect free: polling does not advance virtual time or write a new tick. The authoritative field contract is TELEMETRY.md, with machine-readable structure in snapshot.schema.json.
Returns controller state and timing metadata, including state, tick, seed, start_time_utc, simulated_at_utc, step_seconds, and interval_seconds. When SQLite persistence is enabled, status also exposes local storage/session metadata. See RUNTIME_CONTROLS.md.
Returns a long-lived text/event-stream response for the local dashboard. Each named state event contains one atomic read-only object with source="simulation", the current cached snapshot, and matching controller status. Opening or reading the stream never advances virtual time, persists a tick, changes a control state, or invokes a simulation action.
The server emits an event immediately and then approximately every 1.5 wall-clock seconds. Frames include retry: 1500, event: state, and an id equal to the current synthetic tick. The id is convenience metadata, not an event-log cursor: the server does not buffer or replay missed frames. A reconnect reads the latest current state.
The same loopback bind and local Host validation apply to the stream. There is no CORS/public-network mode or authentication layer. The packaged browser prefers EventSource when available; if EventSource is unavailable, parsing fails, or the connection errors while the browser reconnects, the existing 1.5-second /api/snapshot + /api/status polling loop remains the fallback.
Returns the same complete { "assets": [...] } configuration shape accepted by PUT /api/config. Scenario fields and ranges are documented in SCENARIOS.md, PROGRESSION.md, and OPERATIONS.md.
All request bodies below must use Content-Type: application/json and a valid positive Content-Length.
The body must contain exactly one string field:
{"action":"pause"}Accepted actions are start, pause, step, and reset. step is valid only while paused. Configuration/runtime changes and reset produce deterministic replay semantics described in SIMULATION_CONTROL.md.
Common failures:
| HTTP | code |
Meaning |
|---|---|---|
| 400 | invalid_control_request |
Wrong body shape, type, or action |
| 409 | control_conflict |
State transition is not allowed, such as stepping while running |
| 503 | persistence_unavailable |
Required SQLite write failed; live state is not advanced/claimed committed |
Accepts exactly the validated asset configuration used by local --config files:
{
"assets": [
{"asset_id":"MTR-01","name":"Conveyor","scenario":"healthy","severity":0}
]
}The full replacement is validated before mutation. A successful change pauses the simulation and resets to tick 1 while preserving the active seed/clock settings. With SQLite enabled, the new initial snapshot is persisted as a distinct recording session before the live configuration is published.
Common failures use invalid_configuration (400), control_conflict (409), or persistence_unavailable (503). See CONFIG_FILE.md for the important difference between the trusted-local startup file boundary and this HTTP boundary.
Requires exactly all three fields:
{
"seed": 1357,
"step_seconds": 0.5,
"interval_seconds": 1.5
}This replaces the deterministic PRNG seed, virtual seconds-per-tick, and real worker cadence, then pauses/resets to tick 1. It preserves the current asset scenarios and virtual UTC epoch. Detailed ranges and atomicity guarantees are in RUNTIME_CONTROLS.md.
Common failures use invalid_runtime_settings (400), control_conflict (409), or persistence_unavailable (503).
Requires exactly:
{"incident_id":1,"actor":"local-demo-reviewer"}The reviewer label is not an authenticated identity. The action only adds a once-only local review annotation; it does not resolve an incident, change severity, authorize maintenance, or control equipment. See ALERT_LIFECYCLE.md.
Specific codes are invalid_acknowledgement (400), incident_not_found (404), incident_already_acknowledged (409), history_unavailable (409), and history_storage_error (503).
History is opt-in. Start with a local database path:
python -m motorpulse --paused --database ./motorpulse-local.sqlite3The six GET /api/history/... routes use API envelope version 1.0.0, source="synthetic", newest-first row-ID pagination, default limit 50, maximum limit 100, and a maximum raw query-string size of 2048 bytes.
| Resource | Extra filters in addition to limit / before_id |
|---|---|
| sessions | none |
| snapshots | session_id, start_utc, end_utc |
| events | session_id, asset_id, start_utc, end_utc |
| alerts | session_id, asset_id, start_utc, end_utc |
| incidents | session_id, asset_id, `state=active |
| transitions | session_id, asset_id, incident_id |
Unknown, duplicate, empty/padded, malformed-percent-encoded, or resource-inapplicable query fields are rejected rather than ignored. UTC range semantics, cursor examples, and response fields are detailed in HISTORY_API.md; incident lifecycle semantics are in ALERT_LIFECYCLE.md.
History failures use:
- 400
invalid_history_queryfor query validation errors; - 409
history_unavailablewhen the app was not started with--database; - 503
history_storage_errorwhen SQLite access fails without disclosing SQL or local filesystem paths.
POST/PUT API bodies are deliberately small and strict:
- maximum body size: 16384 bytes;
Content-Typemust beapplication/json(parameters such as charset are tolerated after the media type);Content-Lengthis required, decimal, positive, and bounded before integer conversion;- UTF-8 decoding must succeed;
- duplicate object keys are rejected at every level;
- non-standard JSON constants
NaN,Infinity, and-Infinityare rejected; - maximum parsed JSON nesting depth: 64;
- invalid input is rejected before controller mutation.
Framing/parser codes are stable:
| HTTP | code |
Condition |
|---|---|---|
| 415 | unsupported_media_type |
Body media type is not JSON |
| 411 | length_required |
Missing/malformed Content-Length |
| 413 | invalid_request_size |
Empty or larger-than-allowed body |
| 400 | invalid_json |
Bad UTF-8/JSON, duplicate keys, non-standard constants, or excessive nesting |
For an unknown /api/... path—or unsupported DELETE, PATCH, or OPTIONS against an API path—the server returns 404 JSON with code="unknown_api_endpoint". HEAD on an API path returns the same 404 JSON headers with no body. Static-file unsupported methods keep the standard HTTP-server behavior. No CORS preflight permission is granted.
API errors are JSON objects with a human-readable error string and stable machine-readable code where the current contract defines one:
{"error":"Unknown API endpoint","code":"unknown_api_endpoint"}Do not parse the human text as a stable protocol. Branch on documented code values and HTTP status instead.
- The server binds to 127.0.0.1 only; there is no supported public-network mode.
- There is no authentication/authorization layer. Incident actor labels are not identities.
- There is no PLC, actuator, sensor gateway, work-order system, or physical-control integration.
- All live measurements, faults, rule alerts, and trained-baseline outputs in this API are synthetic/research artifacts, not validated industrial diagnoses, failure probabilities, or RUL predictions.
- IMS support is offline and local-file-only; no IMS data is served by this API.