Skip to main content

Start And Observe A Workflow With The Public API

Use the same provider-free fixture as First Workflow. An administrator must first import, bind, review, and enable docs_deterministic_smoke_v1. On 2026-10-05, the exact downloadable fixture completed through the API on a fresh disposable Compact installation at source commit eb1626b05d06ca893a451560df2b5e95b972c47e, returning the declared normalized output. A separate provider-free approval variant also completed with its approved output; disallowed and stale decisions returned HTTP 409. These are local installed checks, not production, MCP, or SSE acceptance.

Credentials and scopes​

Set FLOW_STEWARD_URL, FLOW_STEWARD_TOKEN, PROJECT_ID, and WORKFLOW_ID. Use WORKFLOW_ID=docs_deterministic_smoke_v1 unless import conflict handling renamed it. PROJECT_ID must be the existing project UUID. The workflow must admit its mcp trigger channel. The narrow minimum scope families are:

OperationAny accepted narrow scope
Read workflow contractworkflows.read
Start workflowworkflows.write
Read job, approval, or SSEjobs.read
Submit approval decision or canceljobs.write

Broader wildcard/read/command/admin scopes may also authorize these routes, but do not widen a token merely to make this tutorial pass.

The requests below are written as plain HTTP: method, path relative to FLOW_STEWARD_URL, headers, and JSON body. Send them with your HTTP client or integration code. Every request carries Authorization: Bearer <token>.

An operator on the deployment host can run the same steps with the CLI instead of the API: ./flow-steward workflows contract, ./flow-steward workflows run, ./flow-steward jobs show, and ./flow-steward jobs approve. See CLI Reference.

Confirm the contract​

GET /api/projects/{PROJECT_ID}/workflows/{WORKFLOW_ID}/contract
Authorization: Bearer <FLOW_STEWARD_TOKEN>

Confirm the required JSON input payload and required JSON output normalized.

Submit and capture the job ID​

POST /api/workflow-runs
Authorization: Bearer <FLOW_STEWARD_TOKEN>
Content-Type: application/json
Idempotency-Key: docs-api-smoke-001

{
"project_id": "<PROJECT_ID>",
"workflow_id": "docs_deterministic_smoke_v1",
"test_mode": true,
"inputs": {"payload": {"message": "hello"}}
}

Keep the returned job_id for the next steps.

HTTP 200 accepts this asynchronous launch; it does not mean execution has completed. The response supplies project_id, job_id, status, queue, and deduplicated. Reuse the same idempotency key for an identical retry; use a new key only for a deliberately new run. The API does not accept a task summary, caller task ID, or file uploads in this request.

test_mode is not a general external-effect sandbox. Safety here comes from the reviewed provider-free fixture.

Observe the durable job​

GET /api/jobs/{JOB_ID}
Authorization: Bearer <FLOW_STEWARD_TOKEN>

Wait for a terminal state and inspect the returned outputs. The expected output variable is normalized: {"message":"hello"}. Assert that value as well as completed; a completed job without the declared output does not pass this check.

For durable state events:

GET /api/events/stream?job_id={JOB_ID}
Authorization: Bearer <FLOW_STEWARD_TOKEN>
Accept: text/event-stream

Keep the connection open and read frames as they arrive.

Representative frames include:

id: 1842
event: job.state
data: {"schema_version":"1","job_id":"job-987","project_id":"proj_42","workflow_id":"docs_deterministic_smoke_v1","status":"completed","terminal":true,"updated_at":"2026-09-17T12:00:00Z","result":{"outputs":{"normalized":{"message":"hello"}},"artifacts":{}}}

event: stream.error
data: {"schema_version":"1","code":"stream_unavailable","message":"Public job stream is temporarily unavailable","retryable":true}

The job.state line above is a complete minimal terminal WorkflowRunStateV1 frame; optional step, approval, error, and lifecycle timestamps are omitted. The stream.error line is the exact retryable error shape emitted when the public job stream is temporarily unavailable.

Reconnect with the last marker in Last-Event-ID. On 429, wait the Retry-After seconds. A 410 means the marker can no longer be resumed: reload job detail and open a fresh stream from current state.

Approval discovery and decision​

The smoke fixture has no gate. For a different reviewed workflow that reaches an approval gate, discover the current gate before deciding:

GET /api/jobs/{JOB_ID}/approval
Authorization: Bearer <FLOW_STEWARD_TOKEN>

From the response, take the current gate's thread_id and approval_id, and check that allowed_actions contains approve. Never substitute the job ID for the thread ID or reuse an approval ID from an older gate.

Continue only if both identifiers were returned and approve is offered. For a reviewed human-approval gate offering approve, send this explicit decision with a stable retry key:

POST /api/approvals/{THREAD_ID}
Authorization: Bearer <FLOW_STEWARD_TOKEN>
Content-Type: application/json
Idempotency-Key: docs-approval-{APPROVAL_ID}

{
"approval_id": "<APPROVAL_ID>",
"approved": true,
"action": "approve",
"comment": "Reviewed for the documentation smoke run."
}

HTTP 202 means the decision and resume intent are durable; execution remains asynchronous. Repeating the same body and key may return a deduplicated result. Changing the body for the same key is a conflict. A stale or already-resolved gate returns 409; an action outside allowed_actions also returns 409 (approval_action_not_allowed). An expired replay record returns 410. Custom gates may offer resume or another action: use only an action offered by that current gate, with the decision body appropriate to it. Do not treat resume as a universal synonym for approving a human-review gate.

Cleanup and failures​

Cancel only a non-terminal disposable job with POST /api/jobs/{job_id}/cancel. Flow Steward does not expose project creation or deletion through the public allowlist; fixture lifecycle remains an administrator responsibility.

Common failures are 403 scope/allowlist mismatch, 404 unknown or unauthorized project/workflow/job, 409 approval/idempotency conflict, 422 disabled or contract-invalid workflow, 429 admission pressure, and 503 unavailable runtime.