Skip to main content

Use Case: Order Fulfillment

This use case demonstrates an order fulfillment workflow that integrates with external APIs, uses parallel execution, and applies timer-based retry logic.

Scenario

When a customer places an order, the process:

  1. Checks inventory and validates the shipping address in parallel (two service tasks)
  2. Routes to shipping if both checks pass
  3. Waits 5 minutes and retries if stock is unavailable
  4. Notifies the customer once the order ships

Step 1: Store the External API Credential

Keep the inventory API key out of the process definition:

curl -X POST https://api.stateway.io/v1/credentials \
-H "X-API-Key: sw_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"name": "inventory_api_key",
"value": "inv_live_abc123"
}'

Step 2: Create the Process Definition

curl -X POST https://api.stateway.io/v1/definitions \
-H "X-API-Key: sw_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"key": "order-fulfillment",
"name": "Order Fulfillment",
"source_type": "json",
"source": {
"key": "order-fulfillment",
"elements": [
{ "id": "start", "type": "startEvent", "outgoing": ["flow-to-fork"] },
{ "id": "fork", "type": "parallelGateway","outgoing": ["flow-check-stock", "flow-validate-addr"] },
{
"id": "check-stock",
"type": "serviceTask",
"name": "Check Inventory",
"taskDefinition": { "type": "http" },
"taskHeaders": {
"url": "https://api.inventory.com/v1/stock",
"method": "GET",
"executionMode": "sync",
"headers": "{\"Authorization\": \"Bearer {{credentials.inventory_api_key}}\"}"
},
"ioMapping": {
"inputs": [{ "source": "=variables.productId", "target": "product_id" }],
"outputs": [
{ "source": "inStock", "target": "variables.inStock" },
{ "source": "quantity", "target": "variables.availableQuantity" }
]
},
"outgoing": ["flow-stock-to-join"]
},
{
"id": "validate-address",
"type": "serviceTask",
"name": "Validate Address",
"taskDefinition": { "type": "http" },
"taskHeaders": {
"url": "https://api.address.com/v1/validate",
"method": "POST",
"executionMode": "sync"
},
"ioMapping": {
"inputs": [{ "source": "=variables.shippingAddress", "target": "address" }],
"outputs": [{ "source": "valid", "target": "variables.addressValid" }]
},
"outgoing": ["flow-addr-to-join"]
},
{ "id": "join", "type": "parallelGateway","outgoing": ["flow-to-gw"] },
{ "id": "gw", "type": "exclusiveGateway","outgoing": ["flow-ship", "flow-wait"] },
{ "id": "ship-order", "type": "userTask", "name": "Ship Order", "outgoing": ["flow-to-notify"] },
{ "id": "wait-restock", "type": "timerEvent", "timerType": "duration", "timerExpression": "PT5M", "outgoing": ["flow-retry"] },
{
"id": "send-confirmation",
"type": "sendTask",
"name": "Send Shipping Confirmation",
"taskDefinition": { "type": "http" },
"taskHeaders": {
"url": "https://api.notifications.com/v1/send",
"method": "POST",
"executionMode": "sync"
},
"ioMapping": {
"inputs": [
{ "source": "=variables.orderId", "target": "order_id" },
{ "source": "=variables.customerEmail", "target": "email" },
{ "source": "=variables.trackingNumber","target": "tracking_number" }
]
},
"outgoing": ["flow-to-end"]
},
{ "id": "end", "type": "endEvent" }
],
"flows": [
{ "id": "flow-to-fork", "sourceRef": "start", "targetRef": "fork" },
{ "id": "flow-check-stock", "sourceRef": "fork", "targetRef": "check-stock" },
{ "id": "flow-validate-addr", "sourceRef": "fork", "targetRef": "validate-address" },
{ "id": "flow-stock-to-join", "sourceRef": "check-stock", "targetRef": "join" },
{ "id": "flow-addr-to-join", "sourceRef": "validate-address", "targetRef": "join" },
{ "id": "flow-to-gw", "sourceRef": "join", "targetRef": "gw" },
{ "id": "flow-ship", "sourceRef": "gw", "targetRef": "ship-order", "condition": "{{variables.inStock == true && variables.addressValid == true}}" },
{ "id": "flow-wait", "sourceRef": "gw", "targetRef": "wait-restock", "condition": "{{variables.inStock == false || variables.addressValid == false}}" },
{ "id": "flow-to-notify", "sourceRef": "ship-order", "targetRef": "send-confirmation" },
{ "id": "flow-retry", "sourceRef": "wait-restock", "targetRef": "check-stock" },
{ "id": "flow-to-end", "sourceRef": "send-confirmation","targetRef": "end" }
]
}
}'
{
"data": {
"id": "def_01j...",
"key": "order-fulfillment",
"version": 1,
"is_active": true
}
}

Step 3: Start an Instance

curl -X POST https://api.stateway.io/v1/instances \
-H "X-API-Key: sw_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"definition_key": "order-fulfillment",
"correlation_id": "ord-001",
"variables": {
"orderId": "ord-001",
"productId": "prod-001",
"quantity": 2,
"customerEmail": "customer@example.com",
"shippingAddress": "123 Main St, City, State"
}
}'
{
"data": {
"id": "inst_01j...",
"definition_key": "order-fulfillment",
"status": "running"
}
}

Step 4: Monitor Parallel Execution

While both service tasks are running, there are two active tokens:

curl https://api.stateway.io/v1/instances/{instance_id}/tokens \
-H "X-API-Key: sw_live_your_key"
{
"data": [
{ "element_id": "check-stock", "status": "active" },
{ "element_id": "validate-address", "status": "active" }
]
}

Once both complete, the parallel join gate merges them into a single token which proceeds to the exclusive gateway.

Step 5: Complete the Shipping Task

If stock is available and the address is valid, the process pauses at ship-order:

curl -X POST https://api.stateway.io/v1/tasks/{task_id}/complete \
-H "X-API-Key: sw_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"variables": {
"trackingNumber": "TRK123456789",
"shippedAt": "2026-04-26T14:00:00Z"
}
}'

The process then fires the send-confirmation send task (fire-and-forget notification) and completes.

Key Takeaways

  • Parallel gateways run check-stock and validate-address concurrently; the join gate waits for both
  • Credentials keep external API keys out of the process definition — rotate with PATCH /credentials/inventory_api_key
  • Timer events handle retry delays (PT5M) without external scheduling
  • ioMapping outputs use "target": "variables.inStock" to write back into the process
  • correlation_id links the instance to the external order ID for filtering with GET /instances?correlation_id=ord-001