Your First Flow

Build a three-step Flow where each Step needs an output from the one before it. See how the planner walks backward from a single goal to assemble the execution plan automatically.

What You’ll Build

A simple order processing Flow with three Steps:

  • lookup-customer: fetch customer data by ID
  • validate-payment: validate customer has credit available
  • send-confirmation: send an order confirmation email

These Steps form a dependency chain: send-confirmation needs validate-payment to complete, which needs customer data from lookup-customer.

Step 1: Register the lookup-customer Step

This is a resolver Step that provides customer data:

curl -X POST http://localhost:8080/engine/step \
  -H "Content-Type: application/json" \
  -d '{
    "id": "lookup-customer",
    "name": "Lookup Customer",
    "type": "sync",
    "http": {
      "method": "GET",
      "endpoint": "https://api.example.com/customers/{customer_id}",
      "timeout": 5000
    },
    "attributes": {
      "customer_id": {"role": "required", "type": "string"},
      "customer": {"role": "output", "type": "object"}
    }
  }'

What this Step declares:

  • It needs a customer_id (required input)
  • It produces a customer object (output)
  • It’s an HTTP GET request that needs the customer_id in the URL

Step 2: Register the validate-payment Step

This Step takes customer data and validates it:

curl -X POST http://localhost:8080/engine/step \
  -H "Content-Type: application/json" \
  -d '{
    "id": "validate-payment",
    "name": "Validate Payment",
    "type": "sync",
    "http": {
      "method": "POST",
      "endpoint": "https://api.example.com/validate-payment",
      "timeout": 5000
    },
    "attributes": {
      "customer": {"role": "required", "type": "object"},
      "order_amount": {"role": "required", "type": "number"},
      "valid": {"role": "output", "type": "boolean"}
    }
  }'

Key point: This Step requires customer, which is the output of lookup-customer. Argyll will automatically wire them together.

Step 3: Register the send-confirmation Step

This Step sends a confirmation to the customer:

curl -X POST http://localhost:8080/engine/step \
  -H "Content-Type: application/json" \
  -d '{
    "id": "send-confirmation",
    "name": "Send Confirmation",
    "type": "sync",
    "http": {
      "method": "POST",
      "endpoint": "https://api.example.com/send-email",
      "timeout": 5000
    },
    "attributes": {
      "customer": {"role": "required", "type": "object"},
      "valid": {"role": "required", "type": "boolean"},
      "confirmation_sent": {"role": "output", "type": "boolean"}
    }
  }'

Step 4: Start the Flow

Now start a Flow with send-confirmation as the Goal:

curl -X POST http://localhost:8080/engine/flow \
  -H "Content-Type: application/json" \
  -d '{
    "id": "order-123",
    "goals": ["send-confirmation"],
    "init": {
      "customer_id": ["cust-456"],
      "order_amount": [99.99]
    }
  }'

What Argyll Does

Planning Phase

  1. Goal is send-confirmation. What does it need?
  2. It needs customer and valid
  3. Argyll looks for a Step that produces customer. Found: lookup-customer
  4. Argyll looks for a Step that produces valid. Found: validate-payment
  5. validate-payment needs customer (from lookup-customer) and order_amount (from init)
  6. lookup-customer needs customer_id (from init)
  7. All dependencies satisfied. Plan is complete.

Execution Phase

  1. Run lookup-customer with customer_id=“cust-456”. Output: customer={…}
  2. Run validate-payment with customer={…} and order_amount=99.99. Output: valid=true
  3. Run send-confirmation with customer={…} and valid=true. Output: confirmation_sent=true
  4. All Goals satisfied. Flow completes.

Inspect the Result

curl http://localhost:8080/engine/flow/order-123

The response shows the complete Flow state:

{
  "id": "order-123",
  "status": "completed",
  "attributes": {
    "customer_id": [{ "value": "cust-456" }],
    "order_amount": [{ "value": 99.99 }],
    "customer": [
      {
        "value": { "id": "cust-456", "name": "Alice" },
        "step": "lookup-customer"
      }
    ],
    "valid": [{ "value": true, "step": "validate-payment" }],
    "confirmation_sent": [{ "value": true, "step": "send-confirmation" }]
  },
  "executions": {
    "lookup-customer": { "status": "completed" },
    "validate-payment": { "status": "completed" },
    "send-confirmation": { "status": "completed" }
  }
}

Each Attribute is an array of value records since multiple upstream Steps can produce the same Attribute. The step field shows which Step produced each value (omitted for init values).

Key Insights

No explicit wiring: You didn’t write code to “pass customer from Step A to Step B”. Argyll matched the output name to the input name automatically.

Dependency discovery: You didn’t write “execute lookup-customer, then validate-payment, then send-confirmation”. Argyll walked backward from the Goal and figured out the order.

Minimal work: If you started a Flow with a different Goal (e.g., just “validate-payment”), it would still run lookup-customer (because validate-payment needs it) but skip send-confirmation.

Next Steps

  • Read Steps to understand all Step types (sync, async, script, sub-flows)
  • Read Flows to understand Flow lifecycle and terminal states
  • Read Attributes & Roles to understand input/output/const/meta attributes and collection policies
  • Explore HTTP Steps to learn about request/response handling