> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ellomas.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Writing Workflows

> Core concepts of the Replay workflow DSL.

# Writing Workflows

A Replay workflow is a YAML file that describes a sequence of steps to execute. Each step performs an action, optionally extracts values into the shared state bag, and asserts outcomes.

## Anatomy of a Workflow

```yaml theme={null}
name: my-workflow                  # Required — workflow name
version: v0.1                      # Optional — DSL version (defaults to v0.1)
config:
  http:
    base_url: https://api.example.com
  vars:
    region: us-east-1              # Convenience variables
steps:
  - name: step-1                   # Required — unique step name
    type: http                     # Required — step type
    request:
      method: GET
      url: /health
    extract:
      status_code: $.status        # Extract value into state bag
    assert:
      - ["$.status", "eq", 200]    # Assert expected outcome
  - name: step-2
    type: shell
    command: "echo {{ status_code }}"
```

## The State Bag

Every workflow has a shared state bag — a key-value store that lives across all steps. Values extracted in one step are available in all later steps via `{{ variable_name }}` syntax.

```yaml theme={null}
steps:
  - name: fetch-token
    type: http
    request:
      method: POST
      url: /auth/login
      body:
        email: test@example.com
    extract:
      token: $.data.token           # ← saved to state bag

  - name: use-token
    type: http
    request:
      method: GET
      url: /users/me
      headers:
        Authorization: Bearer {{ token }}  # ← reused here
```

## Step Types

| Type    | Description                                         |
| ------- | --------------------------------------------------- |
| `http`  | Execute HTTP requests (GET, POST, PUT, DELETE)      |
| `db`    | Run PostgreSQL queries or Redis commands            |
| `shell` | Execute shell commands                              |
| `print` | Print formatted output to the terminal              |
| `loop`  | Iterate over an array and run nested steps          |
| `call`  | Import and execute steps from another workflow file |
| `if`    | Conditionally execute steps based on an assertion   |

## Extract

The `extract` block maps JSONPath expressions to variable names:

```yaml theme={null}
extract:
  user_id: $.data.id
  user_name: $.data.name
  first_tag: data.tags[0]
```

Extracted values are available in all subsequent steps.

## Assert

The `assert` block validates outcomes. Each rule has a path, operator, and expected value:

```yaml theme={null}
assert:
  - ["$.status", "eq", 200]
  - ["$.data.email", "not_null"]
  - ["$.data.count", "gt", 0]
```

You can also use the object form:

```yaml theme={null}
assert:
  - path: $.status
    op: eq
    value: 200
```

## Templates

Any string field in a step can reference state variables with `{{ var }}` syntax:

```yaml theme={null}
- name: dynamic
  type: http
  request:
    url: /users/{{ user_id }}
    headers:
      Authorization: Bearer {{ token }}
    body:
      message: "Hello {{ user_name }}"
```

You can also use built-in functions:

```yaml theme={null}
- name: transform
  type: print
  message: "{{ upper name }} — {{ lower email }}"
```

## Ignoring Errors

Use `ignore_error: true` to continue execution even when a step fails:

```yaml theme={null}
- name: risky-step
  type: shell
  command: "might-fail"
  ignore_error: true

- name: still-runs
  type: print
  message: "This runs regardless"
```

## Cleanup

The `cleanup` field deletes variables from the state bag after a step completes:

```yaml theme={null}
- name: temp-data
  type: shell
  command: 'echo "{\"temp\": \"secret\"}"'
  extract:
    temp_key: $.temp
  cleanup:
    - temp_key
```

This is useful for sensitive data that should not leak to later steps.

## Variable Scope

Variables exist in a three-level scope hierarchy:

1. **Global** — Environment variables loaded at startup
2. **Workflow** — Set via `config.vars` or `extract` at the workflow level
3. **Step** — Set via `extract` within a step, cleaned up when the step exits

Inner scopes inherit from outer scopes. A step can read global and workflow variables.

## What's Next?

* Dive into [HTTP requests](/replay/guides/http-requests) — the most common step type
* Explore [shell commands](/replay/guides/shell-commands) — no external dependencies needed
