A unique signal identifier shared by the sender and receiver.
OptionaltimeoutOrConfig: string | ConditionQueueConfigOptional timeout string (e.g. '30s', '24h') OR a
ConditionQueueConfig that writes one row to public.hmsh_escalations
atomically at suspension time. For an escalation-bearing wait with an SLA,
set the config's timeout field — the wait arms the same resume timer as
the string form, and when the timer wins the escalation row transitions
pending → expired so a late resolve fails as already-expired. (expiresAt
is display metadata on the row only; it arms nothing.)
The signal payload, false when a timeout (string form or
config.timeout) expired first, or null if the escalation was cancelled
via client.escalations.cancel().
Pauses the workflow until a signal with the given
signalIdis received. The workflow suspends durably — it survives process restarts and will resume exactly once when the matchingsignal()call delivers data.conditionis the receive side of the signal coordination pair. The send side issignal(), which can be called from another workflow, a hook function, or externally viaclient.workflow.signal().On replay,
conditionreturns the previously stored signal payload immediately — no actual suspension occurs.Basic usage
With timeout
Pass a duration string as the second argument to set a deadline.
conditionreturnsfalseif the timeout fires before a signal arrives.With escalation queue config
Pass a ConditionQueueConfig as the second argument to surface the pause as a claimable row in
public.hmsh_escalations. The INSERT — every field of the config, includingmetadatafacets — is committed atomically with the workflow checkpoint: one write, one commit, crash-safe. From the row's first visible moment it carries its complete routing context and metadata, so claim-by-metadata routing and version-pinned facets (e.g. aschema_versionthe resolver UI renders) can trust every row they read.Placement: call escalation-bearing waits from main workflow code
The resolve/signal delivery pipeline routes to the main flow's waiter. Inside a hook function (
execHook), an escalation-bearing wait writes its row and honorstimeout(the row expires and the hook resumes withfalse), while resolution delivery targets the main flow — so structure SLA-gated human waits in the workflow body and let hook functions report back viasignal().Early signals are buffered
A signal delivered before its
condition()registers — a fast signaler, or a payload deposited before the workflow starts — is buffered as a pending signal and delivered when the wait registers. The buffer holds a signal for 10 minutes by default; passexpiretosignal()(e.g.'1h','30d') to hold it longer when signaling early on purpose.Fan-in: wait for multiple signals in parallel
Harvest fan-out scales the same way: open N waits with
Promise.alland signal all of them at once. Buffering covers every signal that races ahead of registration, so size fan-out by the pending-signal TTL — how long a racing signal may wait for its condition to register — with no separate bound on the number of concurrent waits.Paired with hook: spawn work, wait for its signal