Christian Heyden (Alex)
03/11/2026, 5:37 PMMarvin
03/11/2026, 5:37 PMMarvin
03/11/2026, 5:41 PMpause_flow_run(...)
- Long waits (hours/days) and you want to tear down infrastructure: use suspend_flow_run(...) with persist_result=True on the flow so state/outputs are durable
- Ongoing, low‑latency exchanges (chat-like): use streaming inputs via `receive_input`/`send_input`
- Only pause/suspend at the flow level (not inside tasks)
2) Use structured forms for great UX and safety
- Define forms with RunInput (Pydantic) for type-checked inputs, defaults, descriptions, and dropdowns (via Literals/Enums)
- Include clear, concise context (e.g., summaries, links, previews) in the description
- Never include secrets or sensitive payloads in form fields/descriptions
3) Always set explicit timeouts and define the fallback
- Set timeout on `pause_flow_run`/`suspend_flow_run` and handle timeouts (e.g., fail fast, auto-rollback, or skip)
- Use the key parameter to make pauses idempotent and prevent re-prompting on retries
4) Automate notifications and resumption
- Use Automations to notify the right channel (Slack/email) when a run enters Paused state
- Prefer Automations over client-side hooks for reliability
- For external approvals, emit custom events and have an Automation resume the run when your event arrives
5) Make approvals auditable and observable
- Log what is being approved, by whom, and why; persist decisions as artifacts/variables if needed
- Keep the human step a clear gate with explicit branching (approved vs. rejected paths)
- Use states and events to trace the full decision path
6) Operate at scale cleanly
- Cap how many runs can be paused concurrently (via work pool sizing or concurrency limits elsewhere in your system)
- Favor suspend_flow_run for long queues of approvals to avoid idle infra costs
- Avoid pausing inside retry loops; add key= to dedupe pauses if retries occur
7) Secure programmatic control
- If resuming programmatically (resume_flow_run), scope credentials tightly and use service accounts
- Validate and sanitize any input received from external systems before resuming
Minimal reference pattern
from prefect import flow
from prefect.flow_runs import pause_flow_run
from prefect.input import RunInput
from pydantic import Field
class Approval(RunInput):
approved: bool = Field(description="Approve to proceed?")
notes: str = Field(default="")
@flow(persist_result=True) # recommended if you might suspend later
def pipeline():
decision = pause_flow_run(
wait_for_input=Approval.with_initial_data(
description="Review sample: <s3://bucket/key> (100 rows previewed)"
),
timeout=3600,
key="gate-approve-v1", # idempotent pause
)
if not decision.approved:
return "stopped_by_human"
# continue...
Suspend when waits are long to free infra:
from prefect.flow_runs import suspend_flow_run
# use when waits are hours+ and you want to free infra
suspend_flow_run(timeout=86400, key="long-wait-approval")
Event-driven resumption (via Automations)
- External system or service:
from prefect.events import emit_event
emit_event(event="approval.granted", resource={"prefect.resource.id": "your-resource-or-run"})
- Automation: trigger on custom event “approval.granted” and action “Resume flow run”
Useful references
- Interactive workflows: Interactive workflows
- Form building with RunInput: Form building
- Automations: Automations
- Custom events: Custom events
- States overview: StatesMarvin
03/11/2026, 5:41 PM