Run it once for each row
"Follow up with everyone who hasn't paid" is not one piece of work. It is forty-seven.
A trigger normally starts one run. Tick Run it once for each row on the trigger and it starts one run per row instead: it asks the business's own database who this firing is about, and gives each of them their own run.
That matters more than it sounds. One run handling forty-seven customers means a single agent working down a list in its own head — it loses its place, it costs a great deal, and it leaves one trace nobody can read. Forty-seven runs means forty-seven traces, each about one customer, and a failure that touches one of them rather than the whole morning's work.
A row is whatever your customer's business is made of:
| The business | One row is | The firing |
|---|---|---|
| A shop | a product | every SKU running low, reordered |
| A clinic | a patient | everyone discharged three weeks ago, followed up |
| A lender | a borrower | every repayment due this week, chased |
| A school | a family | every unpaid balance, reminded |
| An agency | an invoice | everything overdue, followed up |
| A restaurant | an enquiry | every catering request with no reply |
| A wholesaler | a supplier | every order that has missed its date |
Nothing here knows what any of those are. You write the query; the row is whatever it returns.
Setting one up
Open the trigger and tick Run it once for each row.
Where the rows come from — Postgres, or MySQL / MariaDB. It uses the connection you already set up under Tools, so there is nothing new to connect and no second password to keep in step.
Which rows — a query, written by you:
SELECT c.customer_code, c.full_name, c.phone, i.balance
FROM customers c
JOIN invoices i ON i.customer_id = c.id
WHERE i.balance > 0 AND i.due_date < CURRENT_DATE
Or, for a shop watching its shelves:
SELECT p.sku, p.name, p.stock_on_hand, p.reorder_level
FROM products p
WHERE p.stock_on_hand <= p.reorder_level
It is read-only, always — the database itself refuses anything that would change their data, not just our checks. One query at a time; no semicolons.
Their id and their name — which column identifies each subject, and which one names it. Use the business's own identifier: a customer code, an invoice number, a patient number, a SKU. That is what lets an owner check a result against their own records, which is the difference between a report they trust and one they merely hope is right. Leave them blank and RAiN guesses from the column names.
At most — a ceiling on how many rows one firing may cover. It is not a target. It is what stands between a query that matched more than you thought and a very expensive morning.
Preview before you save
Press Preview the rows. It runs the query and shows you the first few rows and — the important part — how many there are.
Do this every time. "47 customers" and "1,400 customers" look identical in a WHERE clause and take one second to tell apart here. The line under the table also shows what each row would be recorded as, so a wrong id column is obvious now rather than in a report three weeks from now.
Using one row's values
Any step below can address the row directly:
{{row:phone}} {{row:full_name}} {{row:balance}} {{row:sku}}
Any column name from your query works. Use it in an Action's arguments, in a Deliver step's To, in a subject line or a message body.
This is what makes fan-out worth having. Without it, a message would have to be addressed by an agent reading a phone number out of a paragraph and copying it correctly — which is exactly the guesswork Action and Deliver steps exist to avoid.
A column that isn't in the row is left showing as {{row:whatever}} rather
than replaced with nothing. A reminder that visibly has a hole in it is better
than one quietly addressed to an empty string.
The whole row is also handed to the first step as its input, in plain words
(customer_code: CUS-2291; full_name: Chidinma Okafor; balance: 45000), so an
agent that just needs to know who it is dealing with already does.
Testing it without waiting for nine o'clock
Press Run it now, for real. It queues the batch exactly as the schedule would, and the runs appear under Runs as they finish.
It uses the query you have saved, not the one currently in the box — so save first if you have just changed it.
How they run
The rows are written down first, then run a few at a time. Two things follow from that, and both are deliberate:
- A big batch doesn't block everything else. Five hundred rows would otherwise hold up every other schedule in your project for the rest of the morning.
- An interrupted batch is not a lost one. Rows still waiting stay waiting, and get picked up when the worker comes back.
Each run is recorded with its subject and its place in the batch — "3 of 47" — so a batch reads as one nine o'clock job rather than forty-seven coincidences.
Questions people ask
Can it change anything in our database? No. The connection is opened read-only, so the database refuses a write even if the query asked for one. RAiN can read the rows and nothing else.
What if the query has a mistake in it?
You are told before anything runs, in the database's own words ("column
i.balnce does not exist"). Nobody is messaged, and the batch is recorded as
failed so it is not mistaken for a quiet morning.
What if it matches nothing? That is recorded too, as an empty batch. "Checked, nobody owed anything" and "the scheduler stopped working" must never look the same.
Does a batch of 500 use 500 runs of my daily allowance? Yes — one row is one run. If a batch is bigger than the allowance left today, it runs as many as it can and says so, rather than failing entirely at nine in the morning.
Can I fan out from a spreadsheet instead of a database? Not yet. Postgres and MySQL / MariaDB today.
Does the customer's database need to be reachable from the internet?
Yes. A connection string pointing at localhost is that server talking to
itself, and will not work from here.
Related: Trigger · Action · Sequences · Running agents

