The REST API
The :runtime module starts a small HTTP server alongside your topology. It serves operational endpoints — metadata, topology views, live state and progress, health probes, control actions, license state, Prometheus metrics, and freeze diagnostics. Everything below is what an operator reaches for from a shell, a dashboard, or a Kubernetes probe.
Enabling the server
The HTTP server is configured under runtime.http. It's enabled by default; metrics live under runtime.metrics:
runtime:
http:
enabled: true
port: 8080 # default
host: 0.0.0.0 # default — all interfaces
debug:
enabled: true # default — /debug/* endpoints
metrics:
enabled: true
A quick liveness check from the shell:
curl -s localhost:8080/health/ready
/license (license ID, customer name, machine counts), /config, /info, the /topology/* views, and /debug/*. Expose only /health/live and /health/ready through internet-facing ingress; keep the rest on the in-cluster Service. See Kubernetes and Probes.All read endpoints are GET. Calling one with the wrong method returns 405. Before the engine has finished starting, endpoints that depend on it return 503 with a short JSON error.
Metadata
Use these to confirm what's deployed and how it's configured.
| Endpoint | Method | Purpose |
|---|---|---|
/info | GET | Application ID, JVM and Kafka info, uptime, current state, and the list of registered endpoints. |
/config | GET | The merged effective configuration, with secrets masked. |
/info is the fastest way to see what's running and which endpoints this build exposes — the endpoints array is generated from the actual handler registration:
curl -s localhost:8080/info
/config content-negotiates: it returns YAML by default (more readable), or JSON if you ask for it. Passwords, secrets, and tokens are masked in the output.
# YAML (default)
curl -s localhost:8080/config
# JSON
curl -s -H 'Accept: application/json' localhost:8080/config
Topology
Three views of the same topology, for different audiences.
| Endpoint | Method | Purpose |
|---|---|---|
/topology | GET | StoatFlow-native view with rich metadata (scheduled sources, store names, sub-topology structure). |
/topology/ks | GET | Kafka Streams-compatible view, matching Topology#describe() output. |
/topology/compiled | GET | The compiled, runtime-internal representation produced by the topology compiler. |
/topology and /topology/ks return a human-readable text description by default, or JSON when you request it:
# Text (default)
curl -s localhost:8080/topology
# JSON
curl -s -H 'Accept: application/json' localhost:8080/topology
# Kafka Streams-compatible description
curl -s localhost:8080/topology/ks
/topology/compiled reflects how the engine actually compiled and laid out your topology, which can differ from the logical DSL view (for example where key-changing operations introduce sub-topology boundaries). It's primarily a diagnostic view; it returns 503 until the application has started.
curl -s -H 'Accept: application/json' localhost:8080/topology/compiled
State and progress
These show whether the application is making progress and where it's at — lifecycle state, consumer assignment, offset lag, and event-time watermarks.
| Endpoint | Method | Purpose |
|---|---|---|
/state | GET | Current lifecycle state, when it was entered, and the recent state-transition history. |
/consumer | GET | Consumer group metadata, source subscription, last commit time, and per-partition buffer fill + pause state. |
/offsets | GET | Per-partition committed offsets and lag for source topics, plus committed changelog offsets per state store. |
/watermarks | GET | Global and per-partition event-time watermarks, idle flags, and last event times. |
/state is the lifecycle picture — is the app RUNNING, DRAINING, PAUSED, restoring, shutting down?
curl -s localhost:8080/state
/offsets answers "are we falling behind?" — it reports per-partition lag and a totalLag summary, derived from locally cached values (no Kafka RPCs on the request path):
curl -s localhost:8080/offsets
/consumer adds the consumer-group angle (member ID, generation, subscription) and shows per-partition buffer utilisation, so you can see whether any partition is paused under backpressure. /watermarks exposes event-time progress — useful when windowed or timer-driven logic seems to be waiting.
curl -s localhost:8080/consumer
curl -s localhost:8080/watermarks
See Event time and watermarks for how watermarks advance, and Observability for turning these into dashboards.
Health probes
Kubernetes-style liveness and readiness, with separate indicator sets.
| Endpoint | Method | Purpose |
|---|---|---|
/health/live | GET | Liveness — is the process healthy enough to keep alive? |
/health/ready | GET | Readiness — is the app ready to receive/process traffic? |
Both return 200 with {"status":"UP", ...} when all relevant indicators pass, or 503 with status DOWN otherwise. Liveness stays UP through transient states (startup, shutdown, and license issues a restart can't fix) so orchestrators don't needlessly restart the process; readiness goes DOWN during startup, state restoration, and shutdown.
curl -i localhost:8080/health/live
curl -i localhost:8080/health/ready
The response lists each component indicator (engine state, Kafka broker connectivity, Schema Registry if configured, license state) with its own status and details. See Health checks for the indicators and Probes for wiring them to Kubernetes.
Control
Pause and resume processing at runtime — for maintenance, draining, or controlled investigation.
| Endpoint | Method | Purpose |
|---|---|---|
/pause | POST | Pause processing — drains in-flight work, then reaches PAUSED. |
/unpause | POST | Resume processing — returns to RUNNING. |
curl -s -X POST localhost:8080/pause
# -> {"status":"pausing","state":"DRAINING"}
curl -s -X POST localhost:8080/unpause
# -> {"status":"resumed","state":"RUNNING"}
Both are POST. A request that can't be honoured from the current state returns 400 with an explanatory error; if the engine isn't initialised yet you get 503. See Pause and unpause for the state semantics and what "draining" guarantees.
High availability
Registered only when hot standby is enabled (ha.mode != off); otherwise these paths return 404.
| Endpoint | Method | Purpose |
|---|---|---|
/ha/status | GET | This pod's view of the cluster: role, replication lag, ready-standby count, promotion-token epoch, and observed peers. |
/ha/switch | POST | Swap roles — the active drains and the peer promotes. |
/ha/promote | POST | Promote a specific standby (?pod=<id>). |
/ha/demote | POST | Demote a specific active (?pod=<id>). |
curl -s localhost:8080/ha/status
curl -s -X POST localhost:8080/ha/switch
# -> {"command":"SWITCH","target":null,"commandOffset":42}
The write endpoints publish a command and return 202 Accepted with the command's offset as an idempotency token; the targeted pod observes it and acts. If no caught-up (READY_STANDBY) target exists, they reject with 409 Conflict — handing off to a not-ready peer is a self-inflicted outage; ?force=true overrides the gate (a forced promotion still fully restores state before processing). See High availability for the operational guide and REST API reference for full response shapes.
License
| Endpoint | Method | Purpose |
|---|---|---|
/license | GET | Current runtime license-validation state. |
/license reports validation status, tier, expiry and days remaining, machine usage against the allowance, and heartbeat/grace state.
curl -s localhost:8080/license
licenseId, customerName, machine counts). Treat it as in-cluster only — never expose /license through internet ingress.The same state also surfaces through the license health indicator (readiness goes DOWN on expired/revoked/invalid licenses) and as metrics. See License configuration.
Metrics
| Endpoint | Method | Purpose |
|---|---|---|
/metrics | GET | Prometheus text-exposition scrape of JVM and application metrics. |
curl -s localhost:8080/metrics
The endpoint serves the Prometheus text format (text/plain; version=0.0.4). It returns 503 if metrics are disabled (runtime.metrics.enabled: false). The full meter catalogue — commit, changelog, restoration, dispatch, and license meters — is in Metrics.
Debug
Two pull-only diagnostic snapshots for investigating a stalled or frozen commit pipeline.
| Endpoint | Method | Purpose |
|---|---|---|
/debug/threads | GET | Snapshot of engine-owned and platform thread state and stacks, for freeze diagnosis. |
/debug/barriers | GET | Commit-pipeline snapshot: in-flight commit progress, which lanes are outstanding, and a derived phase summary. |
Both are gated by runtime.http.debug.enabled (default true). When disabled, they return 404. They are snapshots of state already kept for other purposes, so there's no cost when nobody is calling them.
# Thread snapshot (optional: depth, filter=all|stuck|known)
curl -s 'localhost:8080/debug/threads?filter=stuck'
# Commit-pipeline snapshot — see the derived phase
curl -s localhost:8080/debug/barriers
/debug/threads accepts optional query params: depth (max stack frames per thread, default 40) and filter (all default, stuck for non-runnable threads, known for engine threads only). /debug/barriers summarises the commit pipeline into a single phase string so you can tell at a glance whether a commit is waiting on lanes, queued, in transaction, or idle.
runtime.http.debug.enabled: false.Full reference
This page is a tour by use case. For the exhaustive list — every response field, status code, and content-negotiation rule — see the REST API reference.
The runtime
The batteries-included :runtime module — StoatFlowRuntime.fromConfig, the lifecycle (start / awaitTermination / stop), and what it adds over the bare :core engine.
Health checks
Liveness and readiness probes on the StoatFlow runtime — the built-in indicators, what flips each, and how they behave during state restoration.