Signals and timers
402 pro_required and the console's Pro routes are not served. Get a licence · Core and Pro · Pro features indexA worker never holds a lease while a run waits: the engine keeps the wait, and the step task comes back only when it can go on.
Signals
wait_for(name, signal:, timeout_ms:) waits durably for a signal (timeout_ms defaults to 600,000, ten minutes); its output is the signal's payload, so later steps read it with output("approval", "by").
workflow "conf-signal-#{LANG}" do
step "before", Ok
wait_for "approval", signal: "approve", timeout: 60.seconds
step "after", Approved, input: {by: ref("steps.approval.output.by")}
end,wf("signal").step("before", before).wait_for("approval", signal="approve", timeout_ms=60000)
.step("after", approved, input={"by": output("approval", "by")}),wf('signal')
.step('before', async () => ({ ok: true }))
.waitFor('approval', { signal: 'approve', timeoutMs: 60000 })
.step('after', async (i: any) => ({ approved_by: i.by }), { input: { by: output('approval', 'by') } }),wf("signal")
.step("before", ->(_i, _ctx) { { ok: true } })
.wait_for("approval", signal: "approve", timeout_ms: 60_000)
.step("after", ->(i, _ctx) { { approved_by: i["by"] } }, input: { by: output("approval", "by") }),wf("signal").
Step("before", func(map[string]any, *EC) (any, error) { return map[string]any{"ok": true}, nil }).
WaitFor("approval", "approve", 60000).
Step("after", func(in map[string]any, ec *EC) (any, error) {
return map[string]any{"approved_by": in["by"]}, nil
}, joobq.WithInput[*App](map[string]any{"by": joobq.Output("approval", "by")})),wf("signal")
.step("before", |_i: Value, _c: &Ctx| Ok(json!({"ok": true})))
.wait_for("approval", "approve", 60_000)
.step_with("after", |i: Value, _c: &Ctx| Ok(json!({"approved_by": i["by"]})), StepOptions { input: json!({"by": output("approval", Some("by"))}), ..Default::default() }),wf("signal")
.step("before", (i, ctx) -> map("ok", true))
.waitFor("approval", "approve", 60_000)
.step("after", (i, ctx) -> map("approved_by", m(i).get("by")), StepOptions.input(map("by", DSL.output("approval", "by")))),wf('signal')
->step('before', fn () => ['ok' => true])
->waitFor('approval', 'approve', 60000)
->step('after', fn ($i, $ctx) => ['approved_by' => $i['by']], input: ['by' => DSL::output('approval', 'by')]),Wf("signal")
.Step("before", (i, c) => Task.FromResult<object?>(new { ok = true }))
.WaitFor("approval", "approve", 60000)
.Step("after", (i, c) => Task.FromResult<object?>(new { approved_by = i?["by"]?.GetValue<string>() }), input: new { by = Dsl.Output("approval", "by") }),Tested against a real server in every language shown.
Send a signal from code with signal_workflow(id, name, payload) (POST /api/v1/sdk/workflows/:id/signal, answers {id, accepted}), or from the console (POST /api/v1/workflows/:id/signal, operator). A signal sent before the run reaches the wait is kept by the engine and consumed when the wait starts.
When timeout_ms passes with no signal, the engine times the step out (the history records StepTimedOut with kind signal_wait).
Timers
timer(name, for_ms:) waits durably for a time: no worker is busy while it waits.
workflow "conf-timer-#{LANG}" do
timer "pause", for: 3.seconds
step "after", Now
end,wf("timer").timer("pause", for_ms=3000).step("after", after_timer),wf('timer').timer('pause', { forMs: 3000 }).step('after', async () => ({ now_ms: Date.now() })),wf("timer").timer("pause", for_ms: 3000).step("after", ->(_i, _ctx) { { now_ms: now_ms } }),wf("timer").Timer("pause", 3000).Step("after", func(map[string]any, *EC) (any, error) {
return map[string]any{"now_ms": nowMs()}, nil
}),wf("timer").timer("pause", 3000).step("after", |_i: Value, _c: &Ctx| Ok(json!({"now_ms": now_ms()}))),wf("timer").timer("pause", 3000).step("after", (i, ctx) -> map("now_ms", System.currentTimeMillis())),wf('timer')->timer('pause', 3000)->step('after', fn () => ['now_ms' => now_ms()]),Wf("timer").Timer("pause", 3000).Step("after", (i, c) => Task.FromResult<object?>(new { now_ms = NowMs() })),Tested against a real server in every language shown.