Quickstart
From nothing to a firing workflow in five commands.
1. Install
curl -L https://rflow.xyz/install.sh | bash2. Scaffold a project
mkdir transfer-alert && cd transfer-alert
rflow newrflow new scaffolds from the template registry: a picker
lists 17 production-shaped recipes (id + risk + summary — browse them any
time with rflow templates ls), then asks one typed prompt per template
input. --template <id> pre-selects one; --yes skips the prompts entirely
(the large-transfer-alert template with its defaults — what this page
follows). Grow the project later with
rflow add workflow <template-id>
or rflow add network|contract|relayer|notification,
which append to rflow.yaml without disturbing your comments or formatting
(only a chain + address? rflow contract add fetches the
verified ABI for you).
It writes:
transfer-alert/
├── rflow.yaml # the project definition
├── abis/erc20.json # ERC20 ABI used by the workflow
├── docker-compose.yml # postgres:16 on localhost:5448
├── .env # DATABASE_URL, RPC + notification credentials
├── .env.example # the same keys, empty — safe to commit
├── .rflow/template-lock.yaml # which template + answers generated this
└── .gitignore # ignores .env and .rflow/The scaffolded rflow.yaml is a real, runnable workflow — alert on large USDC
transfers on Ethereum mainnet:
rflow_version: 1
name: transfer-alert
config:
port: 3940
db_connection: ${DATABASE_URL}
max_concurrent_runs: 64
networks:
- name: ethereum
chain_id: 1
rpc: ${ETH_RPC}
contracts:
USDC:
abi: ./abis/erc20.json
addresses:
ethereum: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
notifications:
channels:
ops:
telegram:
bot_token: ${TG_BOT_TOKEN}
chat_id: ${TG_CHAT_ID}
workflows:
large-transfer-alert:
trigger:
event:
contract: USDC
name: Transfer
network: ethereum
# fires only above 100000 whole tokens
where: "${{ trigger.args.value > wei('100000', 6) }}"
confirmations: 0
start_block: latest
end_block: live
steps:
- id: alert
notify:
channel: ops
message: "large transfer: ${{ format_units(trigger.args.value, 6) }} USDC in ${{ trigger.tx_hash }}"
on_failure: dead_letterThe scaffolded .env points at the compose Postgres and a public RPC:
DATABASE_URL=postgresql://postgres:rflow@localhost:5448/postgres
POSTGRES_PASSWORD=rflow
ETH_RPC=https://eth.merkle.io
TG_BOT_TOKEN=
TG_CHAT_ID=Fill in TG_BOT_TOKEN/TG_CHAT_ID (any Telegram bot token + chat), or swap the
channel for slack: { webhook_url: ... } / discord: { webhook_url: ... }.
3. Start Postgres (and anvil, for a local chain)
docker compose up -dRunning against mainnet works as-is. To trigger events yourself, run a local anvil chain instead:
anviland point the network at it in rflow.yaml:
networks:
- name: ethereum
chain_id: 31337
rpc: http://localhost:8545Then deploy any ERC20 to anvil and put its address under
contracts.USDC.addresses.ethereum.
Check your local dependencies are ready before booting:
rflow doctor # Docker, Foundry, Postgres, Node + the rflow.yaml summarydoctor is informational (it always exits 0) and prints a one-line fix hint for
anything that is missing. See Scaffolding.
4. Start rflow
rflow startrflow validates the config (strict — unknown keys are hard errors, and it collects
all errors before reporting), applies the rflow schema to Postgres, boots the
engines lazily (no signer here, so no relayer boots), and tails the chain from
start_block: latest.
5. Trigger it
On anvil, fire a qualifying transfer with cast:
cast send <token-address> "transfer(address,uint256)" \
0x70997970C51812dc3A010C7d01b50e0d17dc79C8 200000000000 \
--private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80Watch the run land:
rflow ls # workflows: state, trigger, cursor, runs today
rflow runs list # the journal: per-step status, outputs, attemptsSend the same event twice (a reorged duplicate, a replayed block range) and rflow skips it — the trigger key is claimed exactly once. That's the point.
Next steps
- Add a signer and relayers to send transactions
- Rehearse a workflow against history with
rflow replay— or a single fixture withrflow test - Learn the expression language
- Understand the exactly-once guarantees
- Migrating?
rflow import defender ./serverless.yml/rflow import gelato ./tasks.jsonscaffold a project from what you have — see Migrate - Let your editor or AI agent operate the project —
rflow mcpandrflow schema