# Workflows

> A trigger and up to 20 steps on a project's wallets: swaps, orders, waits, perps, bridges, strategy stages, private sends and more, run on ftw.trade's servers with every step's state and outputs kept.

A workflow is **when this happens, do these steps**. It runs on ftw.trade's servers once armed, on paper or live as its project is, and keeps every step's state, outputs and log.

## Every hour, a random wallet makes a random swap

```json
{ "name": "Hourly random swap",
  "trigger": { "kind": "timer", "every": "1h" },
  "steps": [{ "id": "swap", "kind": "swap", "side": "either", "unit": "percent",
              "amount": "${random(2, 10)}", "wallets": { "groups": ["fleet"] }, "pick": 1,
              "pools": ["robinhood:0x…", "robinhood:0x…"] }] }
```

`pick: 1` takes one wallet of the group at random, `side: "either"` flips a coin, `random(2, 10)` draws the percent, and `pools` picks the market.

## Triggers

| Trigger | Fires |
|---|---|
| Manual | When you press Start |
| Price | Once a pool's price or market cap crosses a value after arming |
| Timer | Every period, from a minute to a week, until stopped |
| Signal | On the next event of a source whose fields match (`{ spread: { gt: 20 } }`), or on every one |
| Strategy metric | When a running strategy's metric crosses a value |
| Launch | When one of your project's launches is on chain, or graduates |
| New launches | A **sniper**: every matching new launch on a launchpad gets its own run. See [Snipers and launches](https://docs.ftw.trade/automate/snipers-and-launches) |

## Steps

| Step | Does |
|---|---|
| `swap` | Buys or sells now across wallets, optionally with take-profit, stop and trailing exits resting on the wallets that bought |
| `order` | Rests a limit order, done at its first fill |
| `wait`, `watch` | Waits some minutes, or until a pool crosses a value or a launch graduates |
| `strategy` | Runs a saved strategy on the wallets; with `until` it's a **stage** that ends when the strategy says so and hands on its metrics |
| `flowscript` | Runs a strategy once over the latest bars and outputs its metrics |
| `perp`, `deposit`, `withdraw` | Perps orders (including "hold this position"), and USDC in or out of a venue |
| `bridge` | Moves funds between chains by the best route |
| `husher` | Fans ETH out privately to wallets, or sends it privately home, and waits until it lands |
| `pool`, `index`, `launch` | Makes a Uniswap pool, indexes a new one, or sends one of the project's token launches |
| `group`, `buyWallets` | Rotates wallets between roles, or buys aged wallets from the marketplace into one |
| `remember`, `emit`, `poll`, `action` | Keeps a value in project memory, publishes an event, refreshes a source, or calls a script action (a Telegram post, say) |

Steps run one after another unless `after` says otherwise, so steps after the same one run together.

## Outputs and expressions

Each step's outputs are typed, and later steps read them: `"${entry.mcapUsd * 3}"`, `"${spot.usd / spot.wallets}"`, `"${memory.losses}"`. Expressions are checked when the workflow is saved, so a pool goes where a pool goes and a number where a number goes.

Any step can run only when something holds:

```json
{ "id": "alert", "kind": "action", "name": "telegram", "args": { "text": "BTC under ${pos.price}" },
  "when": { "value": "${pos.price}", "lt": 60000 } }
```

## Arm, run, stop

- A workflow is **armed** when saved: it waits for its trigger. **Start** runs it now.
- It fires once; arm it again to rerun. Repeating workflows (a timer, or a signal with `repeat`) re-arm themselves, run one at a time, keep every run's history, and stop after three failed runs in a row.
- A step that fails ends the run and says why. Steps that wait on something slow (a private send, a wallet purchase) report a status instead (`done`, `partial`, `failed`, `expired`) so later steps can branch on it.
- Orders and strategies a workflow started keep running after it ends.

## A step or a script?

Use a built-in step whenever one says what you need: swaps, orders, perps, bridges, pools and ranges run at once, checked against what was already sent. Write a [TypeScript script](https://docs.ftw.trade/automate/scripts) for the rest: an outside API, data no source carries, or bespoke logic.
