Waits, Signals, And Timeouts
Breyta :wait step guide for human approvals, webhook callbacks, and timeout-safe orchestration.
Goal
Use :wait as the canonical human-in-the-loop and external callback primitive.
Quick Answer
Use flow/step :wait with a stable :key, explicit :timeout, and a clear :on-timeout policy.
Canonical Wait Pattern
{:flow
'(let [input (flow/input)
approval (flow/step :wait :approval
{:key (str "order-approval:" (:order-id input))
:timeout "2h"
:on-timeout :continue
:default-value {:action :reject :reason :timeout}
:notify {:channels {:http {:connection :sendgrid
:path "/v3/mail/send"
:method :post
:json {:personalizations [{:to [{:email "approver@example.com"}]}]
:from {:email "approvals@example.com"}
:subject "Approve order {{order-id}}"
:content [{:type "text/plain"
:value "Approve: {{approval-url}}\nReject: {{rejection-url}}"}]}}}}})]
{:action (:action approval)
:approval approval})}
Wait Modes
| Mode | Required shape | Typical use |
|---|---|---|
| Approval notification | :notify {:channels {:http ...}} | Human approval/review workflows. |
| Webhook callback | :webhook {:path ... :auth ...} | External system callback completion. |
| Internal signal-only | :key + :timeout | Internal orchestration handoff waits. |
Timeout Semantics
| Setting | Behavior |
|---|---|
:timeout | Accepts seconds or duration strings like "10m" / "2h". |
| Default timeout | 24h. |
| Max wait timeout | 30 days. |
:on-timeout :fail | Run fails on timeout. |
:on-timeout :continue | Wait returns :default-value and flow continues. |
Design Rules
| Rule | Why |
|---|---|
Always set explicit :timeout. | Avoids implicit/default timeout surprises. |
Use deterministic, traceable :key values. | Ensures signal path is debuggable and reproducible. |
| Include business identifiers in notifications. | Gives operators enough context to act. |
| Branch approve/reject/timeout outcomes explicitly. | Keeps business behavior predictable. |
Troubleshooting Signals
| Symptom | Check |
|---|---|
| Wait never resolves | Verify wait key/path and confirm completion signal was sent. |
| Timeout fires unexpectedly | Validate timeout unit ("2h" vs 120) and queue delays. |
| Payload missing fields | Enforce required fields with :validation {:required [...]}. |
Frequently Asked Questions
Should I use :timeout-seconds or :kind :signal?
No. The canonical wait config uses :timeout and :key. Legacy fields should be avoided in new flows.
How do I avoid stuck approval runs?
Bound every wait with explicit timeout and a business-safe timeout branch (:fail or :continue + :default-value).