Skip to main content
Version: 2.0 prerelease

Waterline Operator API Reference

Waterline is the durable-state operator surface for embedded Laravel deployments. The UI uses the same HTTP+JSON API documented here, so scripts, dashboards, and agents can read durable workflow facts without scraping HTML.

Use this reference when you need typed operator evidence: selected-run detail, history export, actionability, command affordances, saved views, preferences, and schedule visibility. Use Monitoring for the conceptual split between Waterline durable state and worker/runtime telemetry.

Installation

Install Waterline in the same embedded Laravel application that installs the workflow package:

composer require \
durable-workflow/waterline:^2.0@alpha \
durable-workflow/workflow:^2.0@alpha
php artisan waterline:install

The @alpha stability flags are required while Waterline and the workflow package are on the 2.0 prerelease channel. Composer stability flags are root-only, so a default stable Laravel app must either require both prerelease packages explicitly as shown above or set an equivalent root stability policy before installing Waterline. After upgrading Waterline, publish the current assets:

composer update durable-workflow/waterline
php artisan waterline:publish

Deployment Boundary

Waterline runs only inside the embedded Laravel host that installs the durable-workflow/workflow package. It reads the embedded app's durable state directly through the package's data sources; it does not call out to the standalone server, and it does not appear in the standalone-server distribution.

If you operate the standalone server, use the server-side equivalents instead of the routes documented here:

Embedded Waterline routeStandalone-server equivalent
GET /waterline/api/v2/healthGET /api/system/health (admin auth, control-plane v2).
GET /waterline/api/statsGET /api/system/operator-metrics for the namespace-scoped operator metrics snapshot.
GET /waterline/api/flows/{bucket} and selected-run detail routesGET /api/workflows, GET /api/workflows/{workflowId}, and GET /api/workflows/{workflowId}/runs/{runId} from the Server API Reference.
`POST /waterline/api/instances/{instanceId}/{cancelterminate
Waterline schedule routes under /waterline/api/v2/schedules`GET

Deployment Modes freezes the same boundary in product-contract terms: Waterline serves the embedded shape, while the standalone server publishes its own operator surface for service-mode deployments. Do not assume cross-mode visibility — runs stay readable from the runtime that owns them.

Base Path And Scope

Waterline mounts under the Laravel app's configured Waterline base path. The examples below use /waterline; if your app publishes Waterline under a different prefix, keep the /api/... suffixes the same.

When WATERLINE_NAMESPACE is configured, every list, detail, schedule, and operator-action route is namespace scoped. Cross-namespace reads and commands return not-found semantics instead of leaking another namespace's workflow state.

curl -sS "$APP_URL/waterline/api/instances/order-1001" \
-H "Accept: application/json" | jq '.status, .run_id, .actionability'

Authentication And CSRF

Waterline routes are registered under Laravel's web middleware group. GET routes work with any authenticated session. POST routes additionally require a CSRF token, which is how the UI itself authenticates every operator action.

Waterline deliberately delegates the first operator auth boundary to the host Laravel application. The host app decides which guards, route middleware, gates, policies, SSO/OIDC/SAML session middleware, SCIM-backed directory groups, service tokens, and rate limits apply before a Waterline controller runs. Waterline then records durable command context for mutating actions instead of treating UI middleware as the only explanation for access.

For scripted operators the token requirement still applies. Two patterns work:

  • Session + XSRF cookie. Make one GET to a Waterline route with the session cookie set. Laravel returns an XSRF-TOKEN cookie on that response. Include its value in the X-XSRF-TOKEN header on every subsequent POST. Laravel accepts that header as CSRF proof for JSON requests under the default middleware.
  • Host-app exemption. If your operator automation owns its own identity (service token, machine user), exempt Waterline's API POST routes from CSRF in the host app's App\Http\Middleware\VerifyCsrfToken by adding the paths under $except, for example waterline/api/instances/*/archive. Exempted routes are then reachable with any authentication layer your app enforces.

A POST without a valid CSRF token returns 419 CSRF token mismatch before any Waterline controller runs, regardless of the target action.

Host apps that expose Waterline on the public internet should document TLS, trusted proxy headers, session lifetime, CSRF exceptions, service-token rotation, and any private-network or mTLS assumptions alongside the deployment runbook.

Dashboard And Health

MethodPathPurpose
GET/waterline/api/statsDashboard totals, backlog counters, repair policy, operator metrics, and fleet trend data.
GET/waterline/api/v2/healthWaterline package/runtime health and v2 data-source readiness.

/waterline/api/stats is the dashboard's summary surface. Treat operator_metrics as Waterline-owned JSON diagnostics, not as a Prometheus scrape surface. Use worker SDK metrics for runtime latency and custom application telemetry.

The Operator Operating Envelope defines how to interpret those diagnostics during rollouts and incident response. In particular:

Field familyMeaning
operator_metrics.backlog.*Durable runnable, delayed, leased, unhealthy, repair-needed, claim-failed, and compatibility-blocked work counts, plus the fleet-level tasks_added_last_minute and tasks_dispatched_last_minute queue-flow facts.
operator_metrics.matching_role.*The node-local matching/dispatch contract for the process serving the request: queue_wake_enabled, deployment shape, task_dispatch_mode, frozen partition_primitives, and current backpressure_model.
operator_metrics.repair.*Repair-loop sweep footprint, including selected candidates, candidate age, and scan pressure.
operator_metrics.projections.*Projection-drift counts for run summaries, waits, timelines, timers, and lineage.
operator_metrics.command_contracts.*Legacy WorkflowStarted contract snapshots that still need backfill.
operator_metrics.history.*History-size and event-count pressure plus continue-as-new recommendations.
queue_visibility.available, queue_visibility.reasonWhether namespace-scoped queue visibility is available on this host app, and the reason when it is not.
queue_visibility.task_queues[].stats.*Queue-local backlog, backlog age, poller counts, workflow/activity ready-versus-leased counts, and per-queue tasks_added_last_minute / tasks_dispatched_last_minute flow facts.
queue_visibility.task_queues[].repair.*Queue-local repair pressure, including candidates, dispatch failures, expired leases, dispatch-overdue counts, and the oldest age for each condition.
coordination_alerts[]Roll-up warnings and errors derived from the health checks plus queue-visibility risks such as backlog without pollers, stale pollers, or aged repair candidates.
checks[], categories.*Blocking versus advisory v2 health checks, with per-check `category = correctness
engine_source, readiness_contractWhether Waterline is actively using the v2 operator bridge and which readiness contract governs that state.

The queue-flow fields answer a specific operator question: is durable queue input arriving faster than the system is dispatching it? tasks_added_last_minute counts distinct durable task rows created in the trailing 60 seconds, and tasks_dispatched_last_minute counts distinct durable task rows whose latest successful dispatch landed in that same window.

Use the queue-flow totals with the server task-queue visibility routes rather than by themselves. Waterline tells you whether durable inflow is outrunning dispatch, while /api/task-queues and /api/task-queues/{taskQueue} tell you whether the hot queue is saturated, intentionally throttled, no_active_workers, or otherwise short on healthy pollers or slots.

GET /waterline/api/v2/health exposes the same queue-local evidence without leaving the Waterline surface. Scripts and dashboards that need one namespace's current queue posture should read queue_visibility.task_queues[] for per-queue stats and coordination_alerts[] for the summary of which queue or health-check condition currently needs operator attention.

The matching_role fields answer a different question: which matching shape is this node currently serving? shape distinguishes in_worker from dedicated, partition_primitives freezes the routing axes as connection/queue/compatibility/namespace, and backpressure_model currently reports lease_ownership. Treat that block as process-local. During mixed-shape rollouts, compare it across nodes before assuming backlog or poll differences mean worker trouble.

GET /waterline/api/v2/health uses the same distinction: error is blocking, warning is advisory, and ok means the current v2 operator bridge is ready.

List Views

List views are bucketed by durable status:

MethodPathBucket
GET/waterline/api/flows/runningOpen or actively blocked runs.
GET/waterline/api/flows/completedRuns closed successfully, including continued runs with closed_reason = continued.
GET/waterline/api/flows/failedFailed runs.
GET/waterline/api/flows/cancelledCancelled runs.
GET/waterline/api/flows/terminatedTerminated runs.

List rows are compact operator summaries. Stable fields include workflow identity (id, instance_id, run_id, workflow_type), state (status, closed_reason, archived_at), timing, history_event_count, history_size_bytes, continue_as_new_recommended, and repair/actionability badges such as repair_blocked.

Selected-Run Detail

Waterline has two addressing modes:

MethodPathPurpose
GET/waterline/api/instances/{instanceId}Current selected run for a workflow instance.
GET/waterline/api/instances/{instanceId}/runs/{runId}Explicit selected run.
GET/waterline/api/flows/{id}Legacy/detail lookup by Waterline row or run id.

Selected-run detail is the authoritative JSON contract for operator screens. It contains durable state and derived diagnostics for:

Field familyMeaning
activities, timers, waits, childrenCurrent and historical wait state rebuilt from typed history first.
signals, updates, declared_signals, declared_updates, declared_queriesCommand lifecycle rows plus loadable workflow contract targets.
timelineOrdered durable events and diagnostic entries.
exceptions, logsFailure facts and replay/debug context.
can_signal, can_update, can_query, can_cancel, can_terminate, can_archiveUI and automation affordances for the selected run.
actionabilityVersioned repair and evidence contract described below.

Automation should prefer selected-run detail over visual screenshots. A screenshot can show what an operator saw; selected-run detail explains why a workflow can or cannot be repaired, queried, cancelled, archived, or replayed.

History Export

History exports are replay/debug bundles. They intentionally include stored workflow, command, activity, update, task, failure, and history payloads so a developer can reproduce or archive the selected run.

MethodPathPurpose
GET/waterline/api/instances/{instanceId}/history-exportExport the current selected run for an instance.
GET/waterline/api/instances/{instanceId}/runs/{runId}/history-exportExport an explicit run.
GET/waterline/api/flows/{id}/history-exportLegacy/detail export by Waterline row or run id.

If an export can leave a protected environment, configure the workflow history-export redactor first. Downstream tools should preserve the export's redaction metadata so reviewers can tell which policy shaped the artifact.

Actionability Contract

Waterline annotates list rows, selected-run detail responses, timeline entries, and history exports with the versioned actionability contract:

{
"actionability_contract": {
"schema": "waterline.actionability",
"version": 1
}
}

The contract identifier is actionability_contract.schema = waterline.actionability with actionability_contract.version = 1.

Run-level actionability answers whether the selected run can be repaired:

FieldMeaning
repair_stateOne of repairable, blocked, not_needed, or unknown.
repairableBoolean shorthand for repair_state = repairable.
blocked_reasonStable reason code when repair_state = blocked.
status_bucketThe Waterline bucket that shaped the run-level decision.
closed_reasonDurable close reason when the run is closed.
task_problemWhether Waterline saw a task-level problem on the run.
diagnostic_only_evidenceTrue when at least one row is informative but not a resume source.

Evidence rows under activities, waits, timers, exceptions, logs, and timeline/export entries can include their own actionability block:

FieldMeaning
stateactionable when the row is a valid repair source, otherwise diagnostic_only.
repair_sourceTrue only for rows backed by a repairable source authority.
diagnostic_onlyTrue when the row must not be used as a resume source.
history_authoritySource authority such as typed_history, mutable_open_fallback, failure_row_fallback, or unsupported_terminal_without_history.
history_unsupported_reasonStable reason code for unsupported fallback history.

Agents and scripts must gate repair/resume decisions on actionability.repair_state, actionability.repairable, and row-level actionability.repair_source. A row with diagnostic_only = true is evidence, not permission to replay or resume.

Operator Actions

Waterline actions are durable commands issued through the selected-run contract. Instance routes target the current run; run routes reject stale or wrong-run targets explicitly.

MethodPathPurpose
POST/waterline/api/instances/{instanceId}/queries/{query}Execute a declared query against the current run.
POST/waterline/api/instances/{instanceId}/signals/{signal}Send a signal to the current run.
POST/waterline/api/instances/{instanceId}/updates/{update}Submit an update to the current run.
POST/waterline/api/instances/{instanceId}/repairDispatch a repair pass for the current run when actionability allows it.
POST/waterline/api/instances/{instanceId}/cancelRequest cancellation for the current run.
POST/waterline/api/instances/{instanceId}/terminateTerminate the current run.
POST/waterline/api/instances/{instanceId}/archiveArchive the selected closed run.
POST/waterline/api/instances/{instanceId}/runs/{runId}/queries/{query}Execute a query against an explicit selected run.
POST/waterline/api/instances/{instanceId}/runs/{runId}/signals/{signal}Send a signal only if the selected run is current.
POST/waterline/api/instances/{instanceId}/runs/{runId}/updates/{update}Submit an update only if the selected run is current.
POST/waterline/api/instances/{instanceId}/runs/{runId}/repairRepair an explicit selected run.
POST/waterline/api/instances/{instanceId}/runs/{runId}/cancelCancel only if the selected run is current.
POST/waterline/api/instances/{instanceId}/runs/{runId}/terminateTerminate only if the selected run is current.
POST/waterline/api/instances/{instanceId}/runs/{runId}/archiveArchive the selected closed run.

Signals, updates, and queries accept JSON arguments. Invalid arguments return field-level validation failures. Replay blocks return 409 Conflict with a stable reason such as workflow_definition_unavailable instead of attempting best-effort execution.

Update Inspection

MethodPathPurpose
GET/waterline/api/instances/{instanceId}/updates/{updateId}Inspect an update lifecycle row for the current run.
GET/waterline/api/instances/{instanceId}/runs/{runId}/updates/{updateId}Inspect an update lifecycle row for an explicit run.
GET/waterline/api/flows/{id}/updates/{updateId}Legacy/detail update lookup.

Use update inspection when a UI or agent needs to explain whether an update is accepted, applied, completed, rejected, timed out while waiting, or blocked by replay compatibility.

Schedules

Waterline schedule routes are v2 visibility and operator-action routes:

MethodPathPurpose
GET/waterline/api/v2/schedulesList schedules visible in the current namespace.
GET/waterline/api/v2/schedules/{scheduleId}Describe one schedule.
POST/waterline/api/v2/schedules/{scheduleId}/pausePause future fires.
POST/waterline/api/v2/schedules/{scheduleId}/resumeResume a paused schedule.
POST/waterline/api/v2/schedules/{scheduleId}/triggerTrigger an immediate fire.
POST/waterline/api/v2/schedules/{scheduleId}/backfillBackfill a time window.
DELETE/waterline/api/v2/schedules/{scheduleId}Delete a schedule.

Schedule responses expose action, status, next fire, recent fire history, overlap policy, note, memo, and search attributes for operator review.

Saved Views And Preferences

Saved views and preferences are UI-owned operator configuration, not workflow history. They are still useful for stable runbooks because they let teams share the exact filters and table layout used during an incident.

MethodPathPurpose
GET/waterline/api/saved-viewsList saved operator views.
POST/waterline/api/saved-viewsCreate a saved view.
GET/waterline/api/saved-views/{view}Read one saved view.
PUT/waterline/api/saved-views/{view}Update a saved view.
DELETE/waterline/api/saved-views/{view}Delete a saved view.
GET/waterline/api/preferences/{surface}Read UI preferences for one surface.
PUT/waterline/api/preferences/{surface}Update UI preferences for one surface.

Do not treat saved-view names or preference payloads as durable workflow facts. For evidence, cite selected-run detail or history export.

Error Contract

Waterline uses ordinary HTTP status codes and JSON error details:

StatusMeaning
400Malformed request or unsupported action payload.
404Workflow instance, run, update, schedule, saved view, or preference surface was not found in the current namespace.
409The selected run cannot execute the action, often because it is historical, closed, replay-blocked, or definition-unavailable.
422Validation failed; response includes field-level errors.
500Unexpected application failure.

Automation should branch on status plus stable fields such as blocked_reason, query_blocked_reason, outcome, reason, and actionability values. Do not parse toast text or button labels as the contract.

See Also