Some things only your code knows: a supplier feed that arrived empty, a reconciliation that came
out short, an order that needs a person to look at it. this.notify raises a notification for
those.
async reconcile() {
const gap = await this.compare();
if (gap.length) {
await this.notify.warn(
"Reconciliation found a gap",
`${gap.length} orders in the feed have no matching payment.`,
);
}
}
Two levels, and no third:
await this.notify.info("Nightly import finished", "4,812 rows, 0 rejected");
await this.notify.warn("Supplier feed was empty", "Ran at the usual time and got nothing back.");
warn is the ceiling. The level above it is what wakes people through their quiet hours, and
only the platform raises that — a flow cannot page anybody at 3am.
Everyone who could already read this deployment's runs, in the workspace the deployment belongs to. You do not address it and cannot: a flow has no way to name a person, and picking recipients is what preferences are for.
Every one of these arrives as message from a flow, so a reader who finds your flow too talkative can silence all of them with one switch, or mute this deployment specifically. That is a feature — a notification channel nobody can quiet gets ignored wholesale, including the parts that mattered.
notify is for people; log is for youthis.log() writes to the run's output, where it is read while debugging one execution.
this.notify interrupts a human who is not looking at this run at all. If nobody would act on
it, log it.
Pass a third argument to fold repeats into a single row with a count instead of a new row each time:
await this.notify.warn(
"Supplier feed was empty",
"Ran at the usual time and got nothing back.",
`feed-empty-${supplier.id}`,
);
The key is scoped to your deployment, so it cannot collide with another flow's. The row keeps counting while it is unread and open; once its reader has dealt with it, the next occurrence starts a fresh one.
this carries.