Delays and waits
Every way a flow waits: the fixed delay, the window delay in the contact's local time, the smart delay configured by name with a preset, an urgency and legal hours, and the two waits that pause until the contact acts or a message is delivered.
Timing decides more of a flow's results than its copy does. A reminder that lands at 3 a.m. annoys; one that lands the instant a checkout starts looks like surveillance; one that lands days late misses the moment. Five nodes control when things happen: three wait for a time, two wait for something to happen.
Every wait counts towards the limit on a flow's longest path: the waits along any path may add up to at most 180 days, and validation reports a flow that exceeds it.
| Node | Waits for | Paths |
|---|---|---|
ACTION_SEQUENTIAL_DELAY | An exact amount of time. | nextStep |
ACTION_BRANCH_TIMEZONE_DELAY | The first allowed moment in the contact's local time. | waitComplete, cannotSatisfy |
ACTION_BRANCH_SMART_DELAY | The best allowed moment for that contact. | waitComplete, cannotSatisfy |
ACTION_BRANCH_CONTACT_EVENT_WAIT | The contact to do something, or a timeout. | One per matcher group, plus the timeout |
ACTION_BRANCH_MESSAGE_DELIVERY_WAIT | The result of the previous send. | By delivery outcome |
Which one to use
- Before a marketing message, a smart delay. Including before the first one: it waits at least its minimum, picks each contact's best moment, and with
legalHoursnever lands at an hour when marketing is not allowed. - For an exact gap, a fixed delay. Two messages ten minutes apart, for example. It does not look at the clock, so do not put one directly before a marketing send.
- For a wait longer than 14 days, a fixed delay followed by a smart delay. A winback that waits 30 days after the last order uses a 30-day fixed delay, then a smart delay to pick the moment.
- Only a message that confirms what the contact just did — an order confirmation — goes out with no delay at all.
Fixed delay
ACTION_SEQUENTIAL_DELAY counts down from the moment the contact reaches it, then continues on nextStep. No time zone, no allowed hours, no paths.
delaySecondsintegerrequiredFrom 10 seconds to 90 days.
Window delay
ACTION_BRANCH_TIMEZONE_DELAY waits for the first allowed moment in the contact's local time zone, inside a window.
minDelaySecondsintegerThe earliest it may resolve. 0 means as soon as an allowed moment comes.
maxDelaySecondsintegerrequiredThe latest it may resolve. Greater than the minimum, at least 8 hours and at most 14 days.
allowedHoursinteger[]Hours from 0 to 23, local time. Empty means every hour, not none.
allowedDaysOfWeekinteger[]Days from 1 (Monday) to 7 (Sunday). Empty means every day.
pathsobject[]requiredExactly two, fixed: { "outcome": "waitComplete" }, taken once the allowed moment arrives, and { "outcome": "cannotSatisfy" }, taken at once when no moment in the window fits the hours and days. The run is not aborted on cannotSatisfy: you decide what that path does. Paths cannot be added, removed or renamed.
Allowed hours and days are the brand's own courtesy calendar. The platform adds no legal restriction here; that is the smart delay's legalHours.
Smart delay
ACTION_BRANCH_SMART_DELAY has the same window, allowed hours and days, and the same two paths as the window delay. Instead of taking the first allowed moment, it scores every allowed moment and waits for the best one for that contact. Configure it by name, and always send all three names:
presetstringWhat ranks the moments. recommended, a balanced mix; purchase, where the project's and the contact's purchases and checkouts weigh most, for carts and upsells; engagement, where the contact's own replies and clicks weigh most. advanced is for hand-written signals, below.
urgencystringHow strongly sooner moments are preferred, from least to most: bestTime, relaxed, balanced, soon, asap. A cart reminder wants soon or asap; a winback, balanced or relaxed.
legalHoursbooleantrue before a marketing message, false before a transactional one. See Legal hours.
minDelaySecondsintegerdefault: 300The minimum wait. Keep it at five minutes or more, even before the first send, so a marketing message never lands in the same instant as the event that triggered it. 0 is accepted when that is really what you want.
maxDelaySecondsintegerrequiredThe end of the window: at least 8 hours, at most 14 days.
allowedHoursinteger[]As on the window delay.
allowedDaysOfWeekinteger[]As on the window delay.
{
"stepClass": "ACTION_BRANCH_SMART_DELAY",
"minDelaySeconds": 1800,
"maxDelaySeconds": 28800,
"preset": "purchase",
"urgency": "soon",
"legalHours": true,
"paths": [
{ "outcome": "waitComplete", "nextStep": { "stepClass": "ACTION_SEQUENTIAL_SEND", "...": "..." } },
{ "outcome": "cannotSatisfy" }
]
}Put the send inside the waitComplete path. cannotSatisfy means no allowed — or legal — moment fitted the window, so nothing should send there. A send placed after the two paths rejoin goes out anyway, at a possibly illegal hour. A narrow window with tight hours or days sends more contacts to cannotSatisfy; widen maxDelaySeconds rather than drop the hours the brand asked for.
What happens when a name is missing:
- No
preset(and no hand-written signals): the node is saved with nothing to rank by and no legal filter. Nothing fills it in for you. - No
urgency: a balanced default applies. legalHoursabsent ornull: on, with any named preset.- A name wins over hand-written values. With
presetset, the signals are rebuilt from it on every save; withurgencyset, a raw urgency value is ignored. To change the behaviour, change the name.
Technical details: the advanced form
With preset: "advanced", the node carries contextProviders, a list of { "contextClass", "relevance" } with relevance from 0 to 1000 and contextClass one of SEND_TIME_MARKETING_BEST_PRACTICE, EVENTS_HOURLY_SALES, EVENTS_HOURLY_CHECKOUTS, CONTACT_SALES, CONTACT_CHECKOUTS and CONTACT_INTERACTIONS. The legal filter is the entry { "contextClass": "SEND_TIME_MARKETING_COMPLIANCE" }, with no relevance. decayFactor is the raw urgency, a number below 1, where lower prefers sooner. A named preset is almost always enough.
Legal hours
legalHours removes the moments at which sending marketing is not allowed in the contact's country. It acts only where a country restricts marketing hours or days; elsewhere, and when the contact's country cannot be determined, every moment passes. It does not replace the brand's allowedHours, and it applies to marketing only: a transactional message is not held back by it.
Waiting for an event
ACTION_BRANCH_CONTACT_EVENT_WAIT pauses until the contact does something, and branches by what they did first.
maxWaitSecondsintegerrequiredFrom 10 seconds to 90 days. When it runs out, the contact takes defaultPath.
pathsobject[]requiredFrom 1 to 20, evaluated top to bottom. Each has matchers (any one of them matches the path) and an optional name.
defaultPathobjectrequiredThe timeout path, with "matchers": [].
Matchers, by their by field:
by | Matches | Fields |
|---|---|---|
event | An event received after the contact reached the node. | eventType, datasource (required), optional filter |
inboundText | A reply matching a pattern. | patterns, unaccent |
interactionToken | A specific interaction with the message. | tokens |
anyClick, anyTap, anyInboundText | Any click, tap or reply. | — |
unsubscribed | The contact unsubscribed. | kind: optOut, suppression or any (default) |
subscribed | The contact subscribed. | — |
- Put catch-all matchers last. A path with
anyClick,anyTaporanyInboundTextmust come after every path with a specific matcher, or it would take their contacts. - Click, tap, token and reply matchers listen to the message the flow just sent. Without a send before the node on that path, they can only time out.
- For "they left", use
kind: any. A contact already suppressed who unsubscribes again arrives only assuppression.
Waiting for delivery
ACTION_BRANCH_MESSAGE_DELIVERY_WAIT binds itself to the send just before it and branches on its result. There is no timeout to configure: a fixed platform window applies, and a message with no result by then goes to notDelivered. An interaction with the message proves delivery early.
outcomeModestringdefault: deliverydelivery: one delivered and one notDelivered path. channel: delivered paths split by channel (sms or rcs, at most one without a channel), plus one notDelivered. There is no defaultPath.
An optional unableToSend path is taken at once when the send was never attempted — no phone, no consent, a blocked number. Without it, those contacts go to notDelivered, mixed with real delivery failures.
With no send before it, delivered can never be reached: every contact goes straight to unableToSend or notDelivered.