Most of my flows end the same way. Something happened, so post a notification and append a line to a log file. After a year that tail had been copied into twelve flows: the Downloads sorter, the backup check, the build watcher, the invoice filer. Then I wanted urgent messages to land in Reminders as well, so they would still be on the phone after the notification was dismissed. That meant twelve canvases, and the two I missed kept the old behavior for a month.
The fix is the one you would reach for in code: write it once and call it. In Watchflows the function is an ordinary flow, and calling one workflow from another takes a Run Watchflow node.
What it does
Tell Me takes three values, title, body and level. It appends the message to ~/logs/tell-me.jsonl, files a High priority reminder when the level is urgent, and posts a notification either way. Every other flow ends by handing it those three values. The download on this page is one caller, File Invoices and Receipts. Tell Me is a second download, tell-me.watchflow, and it goes in first.
Tell Me, the flow being called
- Input is the door. Its Custom Arguments hold a test message (
levelisinfo), so pressing Run on Tell Me itself shows you what a caller's message looks like. - Log to File, named Log it, writes JSON Lines with Include Timestamp on. It comes first so every message is on disk even if a later step fails.
- Condition Urgent? asks whether
levelequalsurgent, case-insensitive, soUrgentfrom a sloppy caller still counts. - Yes goes to Apple Reminders, Remind me: Create Reminder, Title
{{title}}, Notes{{body}}, ListTell Me, Priority High. The list is created on first use, and if Reminders syncs through iCloud, the reminder is on your phone too. - Both branches meet at Notification, Notify, which shows
{{title}}and{{body}}. Only one branch is ever taken, so it posts once.
A caller that files invoices from Downloads
File Invoices and Receipts watches ~/Downloads with Directory Changed, Event Types set to Created. Invoice? checks that fileExtension equals pdf and fileName contains invoice; Receipt? does the same for receipt. Both ignore case. Move File puts the file in ~/Documents/Invoices or ~/Documents/Receipts with Overwrite Existing off, so a second invoice.pdf is saved as invoice-1.pdf rather than replacing the first.
The last two columns are what every caller now ends with: a Map Keys node that writes the message, and a Run Watchflow node that sends it.
An invoice is something to pay, so Invoice message sets level to urgent and body to {{fileName}} is in Documents/Invoices. Move File updates fileName after it moves, so the body names invoice-1.pdf when that is what was saved. Receipt message sends info. Include Unmapped Keys is off on both, so Tell Me receives three keys instead of the whole file event.
On both Run Watchflow nodes, Input passes this payload down, which is what hands the three keys over. The colored bands are Sections: Notes with the backdrop switched on. They change nothing about a run. On a canvas this wide they are how you find the part you came to edit. Drag one by its label band and every card whose centre sits inside moves with it. Select some cards and press ⌘G to wrap them in a new one.
My backup check and build watcher end with the same pair of nodes, each with its own title and level. Changing what "urgent" means now happens in Tell Me alone.
When a PDF with "invoice" in its name lands in ~/Downloads, move it to ~/Documents/Invoices without overwriting, then run my Tell Me flow with title "Invoice to pay", body "{{fileName}} is in Documents/Invoices." and level urgent.
What comes back, and what goes wrong
The trigger does not wait. A called flow's trigger is only the door. The caller's payload is dropped in at its outlet and Tell Me runs at once, and the Input's own settings, Custom Arguments included, are ignored for the call.
Done carries Tell Me's final payload. With one flow picked and a run that completes, Done hands over what Tell Me's last node emitted, with no wrapper. Notification replaces the payload instead of adding to it, so a caller reads {{notified}}, {{title}} and {{body}}. If notifications are off for Watchflows, Notify does not fail: notified is false and error reads permission_denied: notifications, which a caller can branch on. Pass-through, the third outlet, forwards the caller's own input unchanged.
Expect a warning key. Passing the payload down fills in the fields Tell Me's Input would normally produce (text, contentType, data, filePath, timestamp) as empty strings, and Done carries a warning naming them because the caller did not supply them. Tell Me ignores them.
A failed Tell Me does not undo the caller's work. The first urgent message raises a macOS prompt asking to let Watchflows control Reminders. Decline it and Remind me fails, so Tell Me's run fails and Notify never posts. The invoice has already moved and the log line is already written, because both happened earlier. What the caller does next is its On fail setting. Set it to Continue and Done still fires, carrying the result under the flow's namespace instead: {{tell_me.status}} reads failed. Branch on that when a caller needs to know. Each call is saved in Tell Me's own run history, which is where you find why it failed.
Loops and depth are refused. A flow cannot appear twice on one call stack, so if Tell Me ever called a flow that calls Tell Me, the call is refused with "Run Watchflow would create a loop" and the path it found. Nesting stops at four levels, counting the first caller as one. Every call made under one root run shares a budget of 64, so a For Each that calls Tell Me once per file is refused at the 65th.
Setting it up
- Import tell-me.watchflow, then file-invoices.watchflow.
- The download cannot point at your copy of Tell Me, so both Run Watchflow nodes arrive empty. Select each one and pick Tell Me in the inspector.
- Arm File Invoices and Receipts. Tell Me needs no arming, since an Input flow has no watcher.
Variations
- Add
url={{destPath}}to each message and set Notify's Action URL to{{url}}, so clicking the notification opens it in Finder. - Turn on Lock this flow in Tell Me's flow inspector. Its preview inside every caller becomes view-only, so a stray click cannot edit the shared flow.
- Add a
quietlevel: a second Condition in front of Notify that ends the run forquietmessages, so they reach the log file and nothing else. - One Run Watchflow node can call up to three flows. Pick Tell Me and an archive flow together and Done carries each result under its own namespace, plus a
ranFlowslist.