Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Your first workflow

A workflow orchestrates a process by chaining steps: decisions, gateways, human tasks, and service tasks. This continues the loan example: score risk, price the loan, branch on approval, get underwriter sign-off, disburse funds.

The model

{
  "id": "loan_application",
  "name": "Loan application",
  "start": "score",
  "steps": [
    { "type": "decision_task", "id": "score", "decision": "loan_risk",    "next": "price" },
    { "type": "decision_task", "id": "price", "decision": "loan_pricing", "next": "gate" },
    {
      "type": "gateway",
      "id": "gate",
      "branches": [{ "variable": "approved", "op": "eq", "value": true, "next": "review" }],
      "default": "reject"
    },
    { "type": "human_task", "id": "review", "assignee": "underwriter", "next": "disburse" },
    {
      "type": "service_task",
      "id": "disburse",
      "action": "disburse-funds",
      "next": "done",
      "retry": [{ "errors": ["States.TaskFailed"], "max_attempts": 3, "interval_seconds": 5, "backoff_rate": 2.0 }],
      "catch": [{ "errors": ["States.ALL"], "next": "reject" }]
    },
    { "type": "end", "id": "done" },
    { "type": "end", "id": "reject" }
  ]
}

Step types used here:

  • decision_task — calls a named decision (must exist in the same project/release) with the whole workflow state as input; its outputs land under $.decisions.<stepId>.
  • gateway — branches on a prior step’s output. gate reads $.decisions.price.approved (the loan_pricing decision’s approved output) via the variable field.
  • human_task — pauses the execution until a person completes it via the API (see Executions); assignee is stored but authorization also accepts an approver/admin role.
  • service_task — calls out to an external system through a connector; retry/catch follow Step Functions’ Retry/Catch shape.
  • end — a terminal state; a workflow can have several (here: done, reject).

How state flows

Each decision_task/service_task/human_task result is written under a per-step namespace instead of replacing the state:

$.decisions.<stepId>   # decision_task output
$.tasks.<stepId>       # service_task / human_task result

So after score runs, state looks like:

{ "credit_score": 780, "debt_to_income": 0.2,
  "decisions": { "score": { "risk_tier": "LOW" } } }

price still receives the entire state, so it can read risk_tier from $.decisions.score even though its own declared inputs are named differently at the top level — the compiled ASL passes the whole object through. Because results accumulate rather than replace, a decision downstream never clobbers an upstream one, and a gateway can always branch on $.decisions.<step>.<field>.

decisions and tasks are reserved top-level keys in workflow/decision state. Don’t name an input schema field decisions or tasks — see Workflows § reserved namespaces.

Compile it

Compiling turns the model into an AWS Step Functions definition. This is a read-only, non-persisting operation useful to sanity-check a workflow before saving it:

curl -sS -X POST "$RULEFLOW_API/api/engine/compile-workflow" \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d @loan_application.json

The response is Amazon States Language JSON — an internal detail; authors never hand-write or hand-edit it. It still contains unresolved placeholders (like ${RuleFlowEngineArn}) that only get filled in at deploy time (see Release, deploy, execute) — compiling alone does not produce a runnable machine.

Next

Save the workflow, cut a release, deploy it, and run it: Release, deploy, execute.