← All examples
BusinessParallelApproval

Orchestrate high-value order fulfillment

Validate an order, check inventory and fraud concurrently, route only risky orders to a durable human decision, and write one auditable outcome.

This is not a chain of disconnected API boxes. It is one readable program with input validation, bounded concurrency, retries, a conditional human wait, application data, and a final business result.

The workflow at a glance

Text
Order webhook
  → Calculate total
  → [Reserve inventory ∥ Screen fraud]
  → Risky? ─ yes → Human approval
           └ no  → Automatic approval
  → Record outcome in PostgreSQL
  → Publish order.fulfillment-ready

The workflow

WOML
<woml>
  <workflow
    id="order-fulfillment"
    name="Order fulfillment"
    description="Validate, review, and admit customer orders for fulfillment."
    version="1.0.0"
  >
    <config concurrency="20" rate-limit="300/1m" timeout="48h" queue="orders" />

    <triggers>
      <webhook
        id="newOrder"
        path="/webhooks/orders"
        method="POST"
        auth="bearer"
        secret="{{secrets.ORDER_WEBHOOK_TOKEN}}"
      >
        <schema>
          {
            "type": "object",
            "required": ["orderId", "customerId", "items"],
            "properties": {
              "orderId": { "type": "string" },
              "customerId": { "type": "string" },
              "items": {
                "type": "array",
                "minItems": 1,
                "items": {
                  "type": "object",
                  "required": ["sku", "price", "quantity"],
                  "properties": {
                    "sku": { "type": "string" },
                    "price": { "type": "number", "minimum": 0 },
                    "quantity": { "type": "integer", "minimum": 1 }
                  }
                }
              }
            }
          }
        </schema>
      </webhook>
    </triggers>

    <steps>
      <step id="priceOrder" name="Calculate order total">
        <script>
          const total = context.payload.items.reduce(
            (sum, item) => sum + item.price * item.quantity,
            0
          );

          return {
            orderId: context.payload.orderId,
            customerId: context.payload.customerId,
            total: Number(total.toFixed(2)),
            items: context.payload.items
          };
        </script>
      </step>

      <parallel id="checks" name="Run fulfillment checks" concurrency="2" on-error="wait-all">
        <step id="reserveInventory" name="Reserve inventory" retry="3" retry-backoff="exponential">
          <script>
            const response = await services.http.request({
              method: "POST",
              url: "https://inventory.example.com/reservations",
              headers: { authorization: `Bearer ${secrets.INVENTORY_API_TOKEN}` },
              json: {
                orderId: context.steps.priceOrder.orderId,
                items: context.steps.priceOrder.items
              },
              idempotency: {
                header: "Idempotency-Key",
                value: attempt.idempotencyKey
              }
            }, { name: "reserve-order-inventory" });

            return response.data;
          </script>
        </step>

        <step id="screenFraud" name="Screen fraud" retry="3" retry-backoff="exponential">
          <script>
            const response = await services.http.request({
              method: "POST",
              url: "https://risk.example.com/orders/screen",
              headers: { authorization: `Bearer ${secrets.RISK_API_TOKEN}` },
              json: {
                orderId: context.steps.priceOrder.orderId,
                customerId: context.steps.priceOrder.customerId,
                amount: context.steps.priceOrder.total
              }
            }, { name: "screen-order-fraud" });

            return {
              score: response.data.score,
              requiresReview: response.data.score >= 70
            };
          </script>
        </step>
      </parallel>

      <choose id="decision" name="Select approval route">
        <when test="{{context.steps.screenFraud.requiresReview}}">
          <approval
            id="reviewRiskyOrder"
            name="Review high-risk order"
            description="A fraud analyst must approve or reject this order."
            timeout="24h"
            on-timeout="reject"
          >
            <notify>
              <slack
                channels="#order-approvals"
                bot-token="{{secrets.SLACK_BOT_TOKEN}}"
                app-token="{{secrets.SLACK_APP_TOKEN}}"
              />
            </notify>
            <when-approved>
              <step id="approveRiskyOrder"><script>return { decision: "approved", source: "analyst" };</script></step>
            </when-approved>
            <when-rejected>
              <step id="rejectRiskyOrder"><script>return { decision: "rejected", source: "analyst" };</script></step>
            </when-rejected>
          </approval>
          <result value="{{context.steps.reviewRiskyOrder}}" />
        </when>

        <otherwise>
          <step id="approveSafeOrder" name="Approve safe order">
            <script>return { decision: "approved", source: "policy" };</script>
          </step>
          <result value="{{context.steps.approveSafeOrder}}" />
        </otherwise>
      </choose>

      <step id="recordOrder" name="Record fulfillment decision" retry="3">
        <script>
          const db = services.db({
            driver: "postgres",
            connection: secrets.POSTGRES_URL
          });

          await db.insert({
            table: "order_decisions",
            values: {
              order_id: context.steps.priceOrder.orderId,
              customer_id: context.steps.priceOrder.customerId,
              amount: context.steps.priceOrder.total,
              risk_score: context.steps.screenFraud.score,
              decision: context.steps.decision.decision,
              reservation_id: context.steps.reserveInventory.reservationId
            }
          }, { name: "record-order-decision" });

          return {
            orderId: context.steps.priceOrder.orderId,
            decision: context.steps.decision.decision,
            total: context.steps.priceOrder.total
          };
        </script>
      </step>

      <step id="announceOutcome" name="Publish fulfillment outcome">
        <script>
          await services.events.emit(
            "order.fulfillment-decided",
            context.steps.recordOrder,
            { name: "publish-order-outcome" }
          );

          return context.steps.recordOrder;
        </script>
      </step>
    </steps>
  </workflow>
</woml>

Configure and run it

Terminal
woml secrets set ORDER_WEBHOOK_TOKEN
woml secrets set INVENTORY_API_TOKEN
woml secrets set RISK_API_TOKEN
woml secrets set POSTGRES_URL
woml secrets set SLACK_BOT_TOKEN
woml secrets set SLACK_APP_TOKEN
woml check order-fulfillment.woml
woml run order-fulfillment.woml

Replace the example inventory and risk URLs with your APIs, then use the curl command WOML prints at startup.

Why this shows WOML's range

The workflow remains readable while combining code-level pricing logic, concurrent managed effects, external idempotency, a conditional durable approval, SQL persistence, and event fan-out. Every run and retry remains inspectable without spreading the process across a visual canvas and several hidden code nodes.