Dynamiq
WorkflowsAdvanced

Human in the Loop

Pause a running workflow for human input — the Human Feedback tool, per-node execution approval, and how replies resume the run.

Some steps shouldn't run unattended: sending an email, executing SQL against production, committing a refund. Dynamiq gives you two human-in-the-loop mechanisms — a Human Feedback tool the agent calls when it needs a person, and execution approval you switch on per node so a person must sign off before the node runs. Both pause the run, surface a prompt over your streaming connection, and resume the moment a reply arrives.

MechanismWho initiatesTypical use
Human Feedback toolThe agent, when it decides it needs inputClarifying questions, confirmations, progress updates
Execution approval on a nodeThe platform, every time the node is about to runApproval gates before irreversible actions

The Human Feedback tool

The Human Feedback tool is a regular agent tool. Once attached, the agent can take two actions with it:

ActionBehavior
askSends a question and waits — the run pauses until the user replies; the reply text becomes the tool's observation
infoSends a status message and continues immediately — no reply expected

This single tool covers both directions of communication: gathering approval, confirmation, clarification, or missing information (ask), and pushing progress notifications to the user (info).

Attach the tool

Select the Agent node, open Tools, click Add tool, and pick Human Feedback.

Tools section of the Agent configuration panel with attached tools, gear and trash icons, and the Add tool link

Configure the message template

Click the gear icon to open the tool's configuration:

TemplateJinja template
How the agent's message is rendered to the user. Defaults to {{input}} — the raw text the agent wrote. Variables you add appear under Input variables for mapping.
Streamingtoggle
The event configuration used to deliver the prompt and receive the reply on deployed Apps.
Input timeoutnumber (seconds)
How long to wait for a reply on the open connection before the run checkpoints and pauses. Must be less than the Error handling timeout.

Tell the agent when to use it

Mention the tool in the agent's role or instructions — for example: "Before sending any email, confirm the draft with the user via the human feedback tool (action='ask'). Notify the user when the task completes (action='info')." The agent's LLM decides when to call it, so explicit instructions make the behavior deterministic.

Execution approval on a node

Approval gates don't rely on the agent choosing to ask — they intercept a specific node every time it is about to execute. They are available on tool nodes (HTTP API Call, SQL Executor, Python Function, web search and scraping tools, and others) via the Human in the loop section of the node's configuration.

Enable approval

Select the node, expand the Human in the loop accordion, and check Enable execution approval. This also enables streaming on the node so the approval request can reach your client.

The Human in the loop accordion on a tool node with Enable execution approval checked, showing the Approval message, Mutable params, and Input timeout fields

Customize the approval message

The Approval message is a Jinja template rendered with the node's input data, so the reviewer sees exactly what is about to run. The default is:

Node {{name}}: Approve or cancel execution. Send nothing for approval; provide feedback to cancel.

For an email-sending node you might use: Email draft: {{input_data.email}}. Send nothing to approve; provide feedback to cancel and regenerate.

Optionally allow edits with Mutable params

Mutable params lists the node input fields the reviewer is allowed to change when approving — for example letting them correct the body of an HTTP call while everything else stays locked. Fields not in the list cannot be modified by the reply.

When the node is reached, the run emits an approval request and waits. The reviewer can:

  • Approve — execution proceeds, optionally with edited values for the mutable params.
  • Reject with feedback — the node does not execute; the feedback text flows back into the run (an agent sees it as the observation and can revise its plan).
Orchestrator nodes have an equivalent gate for plans: plan approval pauses after planning so a person can approve or reject the task breakdown before any task executes (event name plan_approval instead of approval).

How a paused run resumes

Both mechanisms follow the same lifecycle on a deployed App:

  1. The run pauses at the Human Feedback ask call or the approval gate.
  2. An event is emitted on your open connection. Over a WebSocket, approval requests arrive with "event": "approval" and the rendered message in data.template; over the Runs API event stream you receive agent.human_feedback.requested or approval_request.created with a request id.
  3. You reply on the same channel — send the event back with your feedback over the WebSocket, or POST /v1/runs/{run_id}/input with the request id. The run resumes immediately and the stream continues.
  4. If no reply arrives within the node's Input timeout, the run checkpoints and pauses (run.paused). It is listed under GET /v1/runs?status=awaiting_input with its pending input_requests, and resumes whenever the input POST finally arrives — minutes or days later.

The Runs API input payload distinguishes the three reply kinds: human_feedback (with feedback), approval_request.confirmed (optionally with edited data), and approval_request.rejected (with feedback).

Full transport details — WebSocket client code, the typed event stream, reconnecting, and the input endpoint contract — are covered in Streaming & Async Jobs and The Runs API. The Integration tab of your App page generates ready-to-run human-feedback snippets prefilled with your hostname.

Patterns

  • Approval gate before irreversible actions. Enable execution approval on the one node that touches the outside world (send email, write to DB, call a payment API). The agent plans and drafts freely; nothing ships without sign-off. Put the draft into the approval message template so the reviewer sees the exact payload.
  • Clarifying questions. Attach Human Feedback and instruct the agent to ask whenever the request is ambiguous instead of guessing. This converts hallucinated assumptions into one extra round trip.
  • Confirm-then-execute. Combine both: the agent uses Human Feedback ask to confirm intent early ("You want me to cancel all 3 subscriptions — correct?"), and an approval gate still protects the final action.
  • Progress updates on long runs. Have the agent send info messages at milestones so users watching a chat UI see movement during multi-minute runs — no pause involved.
  • Escalation from guardrails. Route inputs flagged by a detector to a human review branch rather than a flat refusal.

SDK example

With the Python SDK you can run the whole loop locally using the console as the feedback channel — the same workflow deployed as an App switches to streaming methods without code changes to the agent logic:

import os

from dynamiq.connections import OpenAI as OpenAIConnection
from dynamiq.nodes.agents import Agent
from dynamiq.nodes.llms import OpenAI
from dynamiq.nodes.tools.human_feedback import HumanFeedbackTool
from dynamiq.nodes.tools.python import Python
from dynamiq.types.feedback import ApprovalConfig, FeedbackMethod

SEND_EMAIL_CODE = """
def run(inputs):
    return {"content": "Email was sent."}
"""

# A tool guarded by an approval gate: the user must approve before it executes.
email_sender = Python(
    name="EmailSenderTool",
    description="Sends an email. Put the full email text under the 'email' key.",
    code=SEND_EMAIL_CODE,
    approval=ApprovalConfig(
        enabled=True,
        feedback_method=FeedbackMethod.CONSOLE,
        msg_template=(
            "Email draft: {{input_data.email}}\n"
            "Send nothing to approve; provide feedback to cancel and regenerate."
        ),
    ),
)

# A tool the agent can use to ask questions or send status updates.
human_feedback = HumanFeedbackTool(
    name="human-feedback",
    description="Tool for human interaction. Use action='ask' to request clarifications, "
    "action='info' to notify the user.",
    input_method=FeedbackMethod.CONSOLE,
    output_method=FeedbackMethod.CONSOLE,
)

agent = Agent(
    name="email-agent",
    role=(
        "You write and send emails. Ask clarifying questions with the human-feedback tool "
        "(action='ask') when the request is ambiguous, and notify the user about the result "
        "(action='info')."
    ),
    llm=OpenAI(
        connection=OpenAIConnection(api_key=os.getenv("OPENAI_API_KEY")),
        model="gpt-4o",
    ),
    tools=[email_sender, human_feedback],
)

result = agent.run(
    input_data={"input": "Write and send a short email to the team about Friday's release."}
)
print(result.output.get("content"))

Running this, the agent drafts the email, the console shows the approval prompt rendered from msg_template, and pressing Enter (sending nothing) approves execution. On the platform the equivalent configuration uses FeedbackMethod.STREAM, and the prompts travel over the WebSocket or Runs API connections described above.

Next steps

On this page