ORD-10042 for a brand called acme-outdoors — through the
Temporal Engine’s order-fulfillment workflow: reserve inventory → NSR gate → (human review) →
dispatch. You create the brand and its API key, connect a connector, check readiness, stream
the workflow’s history as server-sent events, and signal the review decision. The last section
deliberately breaks things so you can see what durability buys you.
Base URL https://api.workstream.stateset.com; auth x-api-key: <key> on every request; the
workflow id is always order-fulfillment-{brand_uuid}-{order_id}.
Steps 1–2 need a tenant-scoped or global key (creating a brand checks tenant access, and a
minted key is never broader than its minter). From step 3 on, the brand key from step 2 is enough.
The overview explains the two scopes.
1
Create the brand
A brand is the unit of configuration — connectors, policies and workflow bindings hang off it.
Keep
tenant_id, slug and display_name are required; status defaults to draft and
routing_mode to legacy. Send an Idempotency-Key header so a retried create returns the
same brand (idempotent_replay: true) instead of a slug conflict.brand.id — it is $BRAND_ID below. The reference for
POST /v1/brands lists the optional
config fields (region, default_locale, policy_set_key, quotas, …);
Control plane covers draft → active.2
Mint a brand-scoped API key
The body is optional —
label defaults to "<slug> key".3
Connect a connector and check readiness
/connect stores the credentials encrypted, re-points the brand’s connectors, and with
activate: true attempts activation in the same call. It accepts gorgias, shopify and
recharge blocks; the same body sent to
/onboarding/{brand_id}/test
first is a read-only probe that stores nothing and returns { "<type>": { "healthy", "error", "latency_ms" } }.activated is false and readiness is { "blocked": "<error>" };
the credentials are still stored. The go-live gate live-probes every connector and is ready
only when all are healthy and there is at least one:4
Start the order-fulfillment workflow
fulfillment_tool is the sync-server tool that dispatches to your 3PL (it varies per connector);
reservation_params and fulfillment_params pass through to create_inventory_reservation and
to fulfillment_tool. review_cap_cents overrides the default autonomy cap of **50,000 cents
(689 order will.(brand, order_id). POST the same payload again
while the run is open and you get the same workflow_id and the existing run_id back — a
duplicate checkout webhook cannot start a second fulfillment. POST a different payload
under the same order id and the engine answers 409 IDEMPOTENCY_CONFLICT
(workflow_id already exists with a different start payload): either the order changed
after checkout (cancel and restart) or two producers disagree about it.5
Watch the status and the review queue
The status route runs the workflow’s Poll the review queue rather than remembered workflow ids — it lists everything parked at
current_status query and returns a JSON string
"<status>:<phase>". Phases are init, reserve_inventory, nsr_gate, review_gate,
dispatch; status stays running until a terminal one lands.running:review_gate for the brand.6
Stream the event history
GET /v1/workflows/{workflow_id}/events tails Temporal history as server-sent events. event:
is the kind, id: the history event id, data: a JSON WorkflowEvent (event_id, kind,
ts, plus activity, attempt, signal, preview or error when relevant). A keepalive
arrives every 15 seconds and the stream closes after a terminal kind.workflow_started; after 15 comes silence: the NSR verdict was execute, but
68900 >= 50000 promoted it to an escalation and the workflow is sleeping on a durable timer.
If the connection drops, reconnect with Last-Event-ID: 15 and the engine replays from 16.7
Approve at the review gate
The body is a On the stream the signal lands as
ReviewDecision: approved is required; reason, feedback and
edited_text are optional. Signal routes answer 202 Accepted with an empty body — the
decision is delivered to the workflow, not applied synchronously.signal_received with "signal":"set_review_decision",
dispatch runs, and the workflow completes with the OrderFulfillmentResult in preview:GET …/status now returns "fulfilled:dispatch" and the order has left the review queue;
decision_id is the NSR decision that authorised the dispatch (policy engine).Failure, cancellation and replay
A connector blips mid-run. Every activity runs under the engine’s API retry policy: up to 8 attempts, exponential backoff from 1 s capped at 30 s (roughly a 90-second window). On the stream that isactivity_failed with the upstream error, then activity_started with
attempt: 2, 3, … — the workflow’s own state is untouched between attempts. Non-retryable
errors (4xx from the tool) fail fast and the run ends with workflow_failed and error set.
Someone cancels while it waits for review.
cancel signal, status becomes "cancelled:review_gate", and the result
carries the compensation:
blocked (NSR said no, or the reviewer denied),
escalated_expired (nobody answered within the 7-day review timeout), cancelled — goes
through the same chokepoint and releases the reservation; if the release itself fails the run
keeps its original status and reports { "released": false, "error": … } in
detail.compensation. The whole run has a 14-day execution timeout.
The worker restarts. Temporal rebuilds the workflow’s state by replaying its history:
completed activities are not re-executed, and a run parked at the review gate is still parked
afterwards. The activities that could run twice carry deterministic idempotency keys
({brand}-{order}-reserve, -dispatch, -release) so the sync server deduplicates them — the
same contract the Order Operations guide asks of your own integrations.
What you built
Troubleshooting
400 'workflow_id prefix does not grant streaming access' on /events
400 'workflow_id prefix does not grant streaming access' on /events
Expected with a brand key on an
order-fulfillment- id — see the warning in step 6. Use a
global key for the stream; status, review and cancel work with the brand key.Readiness says ready: false
Readiness says ready: false
Read
connectors[].error — each entry is a live probe, and a brand with zero connectors is
never ready. Re-probe with /onboarding/{brand_id}/test until healthy: true, then /connect.Workflow is stuck at running:review_gate, or 404 WORKFLOW_NOT_FOUND
Workflow is stuck at running:review_gate, or 404 WORKFLOW_NOT_FOUND
Stuck at the gate is the design: it waits up to 7 days for a
review signal and never times
out into dispatching, so alert on the length of GET …/reviews/pending rather than on
failures. Smaller orders escalate only when the NSR gate says so or is unreachable (it fails
closed). A 404 means no run with that id exists — check the brand uuid is the compact 32-hex
form and order_id matches; /start again is safe because the id is deterministic.Next steps
Workflow anatomy
The primary
response-automation-v2 workflow — phases, signals, and the review gate pattern this guide reused.Return workflow
The same start / status / review / cancel shape for returns, plus receipt and inspection signals.
Control plane
Multi-tenancy, the outbox and DLQ, and how brand config is versioned and activated.
Order to cash
Where fulfillment sits in the wider commerce flow from order to settlement.