Operate

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

ModeRequired shapeTypical use
Approval notification:notify {:channels {:http ...}}Human approval/review workflows.
Webhook callback:webhook {:path ... :auth ...}External system callback completion.
Internal signal-only:key + :timeoutInternal orchestration handoff waits.

Timeout Semantics

SettingBehavior
:timeoutAccepts seconds or duration strings like "10m" / "2h".
Default timeout24h.
Max wait timeout30 days.
:on-timeout :failRun fails on timeout.
:on-timeout :continueWait returns :default-value and flow continues.

Design Rules

RuleWhy
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

SymptomCheck
Wait never resolvesVerify wait key/path and confirm completion signal was sent.
Timeout fires unexpectedlyValidate timeout unit ("2h" vs 120) and queue delays.
Payload missing fieldsEnforce 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).

Related

As of Feb 13, 2026