Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 24 additions & 3 deletions docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,14 @@
"en/use-dify/nodes/agent",
"en/use-dify/nodes/question-classifier",
"en/use-dify/nodes/ifelse",
"en/use-dify/nodes/human-input",
{
"group": "Human Input",
"expanded": false,
"root": "en/use-dify/nodes/human-input",
"pages": [
"en/use-dify/nodes/hitl-api-integration-flow"
]
},
"en/use-dify/nodes/iteration",
"en/use-dify/nodes/loop",
"en/use-dify/nodes/code",
Expand Down Expand Up @@ -498,7 +505,14 @@
"zh/use-dify/nodes/agent",
"zh/use-dify/nodes/question-classifier",
"zh/use-dify/nodes/ifelse",
"zh/use-dify/nodes/human-input",
{
"group": "人工介入",
"expanded": false,
"root": "zh/use-dify/nodes/human-input",
"pages": [
"zh/use-dify/nodes/hitl-api-integration-flow"
]
},
"zh/use-dify/nodes/iteration",
"zh/use-dify/nodes/loop",
"zh/use-dify/nodes/code",
Expand Down Expand Up @@ -931,7 +945,14 @@
"ja/use-dify/nodes/agent",
"ja/use-dify/nodes/question-classifier",
"ja/use-dify/nodes/ifelse",
"ja/use-dify/nodes/human-input",
{
"group": "人間の入力",
"expanded": false,
"root": "ja/use-dify/nodes/human-input",
"pages": [
"ja/use-dify/nodes/hitl-api-integration-flow"
]
},
"ja/use-dify/nodes/iteration",
"ja/use-dify/nodes/loop",
"ja/use-dify/nodes/code",
Expand Down
805 changes: 788 additions & 17 deletions en/api-reference/openapi_chatflow.json

Large diffs are not rendered by default.

805 changes: 788 additions & 17 deletions en/api-reference/openapi_workflow.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion en/self-host/configuration/environments.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -1153,7 +1153,7 @@ The sandbox is a separate service that runs Python, JavaScript, and Jinja2 code
| `WORKFLOW_MAX_EXECUTION_TIME` | `1200` | Maximum wall-clock time in seconds per workflow run. Exceeding this terminates the workflow. |
| `WORKFLOW_CALL_MAX_DEPTH` | `5` | Maximum depth for nested workflow-calls-workflow. Prevents infinite recursion. |
| `MAX_VARIABLE_SIZE` | `204800` | Maximum size in bytes (200 KB) for a single workflow variable. |
| `WORKFLOW_FILE_UPLOAD_LIMIT` | `10` | Maximum number of files that can be uploaded in a single workflow execution. |
| `WORKFLOW_FILE_UPLOAD_LIMIT` | `10` | Upper bound for the max-files setting on a single file-upload field (e.g., a User Input File List). The node panel's max-files slider is capped at this value; raise to allow larger per-field configurations. |
| `WORKFLOW_NODE_EXECUTION_STORAGE` | `rdbms` | Where workflow node execution records are stored. `rdbms` stores everything in the database. `hybrid` stores new data in object storage and reads from both. |
| `DSL_EXPORT_ENCRYPT_DATASET_ID` | `true` | Encrypt dataset IDs when exporting DSL files. Set to `false` to export plain IDs for easier cross-environment import. |

Expand Down
123 changes: 123 additions & 0 deletions en/use-dify/nodes/hitl-api-integration-flow.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
---
title: Human Input API Integration Flow
sidebarTitle: API Integration Flow
description: End-to-end sequence for handling a paused Human Input form via the API
---

When a workflow reaches a Human Input node, it pauses and emits a `human_input_required` event in the streaming response. The event carries a `form_token` that your integration uses to drive the form lifecycle until the workflow resumes.

For per-endpoint reference, see the [Human Input API](/api-reference/human-input/get-human-input-form).

## Sequence

The sequence below applies to both Workflow and Chatflow apps. Only the entry endpoint in Step 1 and the resume endpoint in Step 6 differ between the two.

<Steps>
<Step title="Start the app in streaming mode">

1. Call [Run Workflow](/api-reference/workflows/run-workflow) (Workflow apps) or [Send Chat Message](/api-reference/chatflows/send-chat-message) (Chatflow apps).
2. Watch for the `human_input_required` event in the SSE stream and capture `form_token`, `form_id`, and `task_id` from the payload.

You'll use `task_id` in Step 6 if the stream closes before the workflow resumes.
</Step>
<Step title="Get the form definition">
Call [Get Human Input Form](/api-reference/human-input/get-human-input-form) with `form_token`. The response includes the rendered Markdown, input field definitions, available actions, and any pre-filled default values. Render the form for the recipient.
</Step>
<Step title="(File inputs only) Issue an upload token">
If the form has `file` or `file-list` inputs, call [Issue Upload Token](/api-reference/human-input/issue-upload-token) for a short-lived `upload_token`. Reuse the token across multiple uploads on the same form.
</Step>
<Step title="(File inputs only) Upload files">
For each file the recipient attaches, call one of:

- [Upload Local File](/api-reference/human-input/upload-local-file) for files from the recipient's device (multipart).
- [Upload Remote File](/api-reference/human-input/upload-remote-file) for files at a public URL that you want the backend to fetch and validate before submission.

Both return an `id` you'll reference as `upload_file_id` in the submit payload.
</Step>
<Step title="Submit the response">
Call [Submit Human Input Form](/api-reference/human-input/submit-human-input-form) with the recipient's input values and the selected action.

File inputs accept either a `{transfer_method: local_file, upload_file_id}` mapping (from Step 4) or an inline `{transfer_method: remote_url, url}` mapping. See [Upload First vs. Inline Remote URL](#upload-first-vs-inline-remote-url) for the trade-off.

On success, the workflow resumes along the matching action branch.
</Step>
<Step title="Resume listening to the workflow">
If the original SSE stream closed, reopen it via the [Workflow](/api-reference/workflows/stream-workflow-events) or [Chatflow](/api-reference/chatflows/stream-workflow-events) Stream Workflow Events API with the `task_id` from Step 1.
</Step>
</Steps>

## Upload First vs. Inline Remote URL

Both submission patterns work for file inputs:

- **Pre-upload, then reference `upload_file_id`** (recommended)

The backend validates file size, type, and extension at upload time, so the recipient gets immediate feedback and can retry before committing the whole submission.

- **Submit inline with `transfer_method: remote_url`**

The backend fetches the file at submit time. Faster to integrate, but any size, type, or fetch failure rejects the entire submission, forcing the recipient to redo other fields.

<Tip>
For interactive forms with recipient feedback, prefer the pre-upload pattern. The trade-off only pays off when the integration is fully programmatic and no human is waiting to retype anything.
</Tip>

## Authentication

The form-facing endpoints, [Get Human Input Form](/api-reference/human-input/get-human-input-form) and [Submit Human Input Form](/api-reference/human-input/submit-human-input-form), are unauthenticated. The `form_token` itself is the credential, and the endpoints are rate-limited per client IP.

The file-upload endpoints require the `Bearer` `upload_token` from Step 3.

## Delivery Method Requirement

The Human Input API works only with forms delivered via the Human Input node's WebApp method. Email-only delivery doesn't expose a `form_token`.

## Example: File-Attached Submission

This example uses a form with a `feedback` paragraph input, an `attachments` file-list input, and `approve` / `revise` actions.

1. Call [Get Human Input Form](/api-reference/human-input/get-human-input-form) to get the form definition:

```http
GET /form/human_input/<form_token>
```

2. Call [Issue Upload Token](/api-reference/human-input/issue-upload-token) to get an upload token:

```http
POST /form/human_input/<form_token>/upload-token
```

Returns `{"upload_token": "hitl_upload_...", "expires_at": ...}`.

3. For each local file, call [Upload Local File](/api-reference/human-input/upload-local-file):

```http
POST /form/human_input/files/upload
Authorization: Bearer hitl_upload_...
Content-Type: multipart/form-data

file=<binary>
```

Returns `{"id": "1a77f0df-...", ...}`.

4. Call [Submit Human Input Form](/api-reference/human-input/submit-human-input-form) with the recipient's input and selected action:

```http
POST /form/human_input/<form_token>
Content-Type: application/json

{
"inputs": {
"feedback": "Looks good to ship",
"attachments": [
{"transfer_method": "local_file", "upload_file_id": "1a77f0df-..."}
]
},
"action": "approve",
"user": "abc-123"
}
```

Returns `{}`. The workflow resumes along the `approve` branch.
118 changes: 107 additions & 11 deletions en/use-dify/nodes/human-input.mdx
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
---
title: Human Input
description: Pause workflows to request human input, review, or decisions
description: Pause workflows to request human input
---

The Human Input node pauses workflows at key points to deliver a customizable request form. Recipients can use the form to review information, provide input, and choose from predefined decisions that determine how the workflow proceeds.

By embedding human judgement directly where it matters, you can **balance automated efficiency with human oversight**.
By embedding human judgement directly where it matters, you can *balance automated efficiency with human oversight*.

<Frame caption="Request Form Example">
![Request Form Example](/images/use-dify/workflow/human-input-request-form-example.png)
</Frame>
<Tip>
For a workflow design example, see [Example: Content Review Workflow](#example-content-review-workflow).
</Tip>

## Configuration

Expand All @@ -27,10 +27,10 @@ Configure the following to define how the node requests and processes human inpu

Choose the channel through which the request is delivered. Currently available methods:

- **Web app**: Displays the request form to the WebApp end user. Not available in workflows started by a Trigger.
- **Webapp**: Displays the request form to the WebApp end user. Not available in workflows started by a Trigger.

<Info>
External clients can also retrieve and submit WebApp forms through the Service API. See [Get Human Input Form](/api-reference/human-input/get-human-input-form).
External clients can drive the WebApp form lifecycle through the Service API. See [API Integration Flow](/en/use-dify/nodes/hitl-api-integration-flow).
</Info>

- **Email**: Sends a request link via email to specific workspace members, external email addresses, or every member of the workspace. Anyone with the link can respond, no Dify account required.
Expand All @@ -51,19 +51,29 @@ Customize the form recipients see and interact with:

Reference workflow variables to show dynamic content, such as AI-generated text for review or any needed contextual information from upstream nodes.

In WebApp delivery, the form itself displays to end users. Any variables you reference render their values directly in the form, so **no Answer or Output node is needed before the Human Input node**.

<Tip>
Reasoning models emit their thinking process alongside the final answer. Referencing the `text` output variable shows both by default.

To show only the answer, toggle on **Enable Reasoning Tag Separation** for the corresponding LLM node.
</Tip>

- **Collect input with input fields**
- **Collect input with form fields**

Input fields can start empty or pre-filled with variables (e.g., LLM output to refine) or static text (e.g., example or default values) that recipients can edit.
Add fields into the request form to capture different types of input from recipients. Each field becomes a variable for downstream use.

For example, in a blog review workflow, you can pass recipient feedback to a downstream LLM node for content revision.

Each input field becomes a variable for downstream use. For instance, pass edited content for further processing or send feedback to an LLM for content revision.
| Field Type | Description |
|:-----------|:------------|
| Paragraph | Text input. Can start empty, or pre-filled with variables (e.g., LLM output to refine) or static text (examples or default values). <br></br><br></br>No maximum length, but very long inputs may exceed downstream LLM context windows. |
| Select | Single-choice selection from a list of options. Define options manually, or reference an `array[string]` variable to use its items as options. |
| Single File / File List | Single or multiple file uploads. <Info>On self-hosted deployments, file upload limits are tunable via environment variables: <ul><li>`UPLOAD_FILE_SIZE_LIMIT`, `UPLOAD_IMAGE_FILE_SIZE_LIMIT`, `UPLOAD_VIDEO_FILE_SIZE_LIMIT`, and `UPLOAD_AUDIO_FILE_SIZE_LIMIT` cap per-file size by extension.</li><li>`WORKFLOW_FILE_UPLOAD_LIMIT` caps the max files a File List field can be configured to accept.</li></ul>See [Environment Variables](/en/self-host/configuration/environments) for defaults.</Info> |

After the recipient responds, the form content with all values filled in is available downstream as the `__rendered_content` variable.
<Note>Only Paragraph is optional; Select, Single File, and File List are mandatory. The form's action buttons stay disabled until all mandatory fields are filled.</Note>

After the recipient responds, the form content with all values filled in is available downstream as the `__rendered_content` variable. File field values render as plain-text placeholders: `[file]` for Single File and `[N files]` for File List.

### User Action

Expand All @@ -90,3 +100,89 @@ Configure how long the request stays open before it expires. The default is 3 da
If no recipient responds before the timeout, the workflow follows the timeout branch from the node. Wire this branch to a fallback path, such as a notification or a retry loop.

If no timeout branch is connected, the workflow ends.

## Example: Content Review Workflow

<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<Frame caption="Workflow Example">
![Workflow Example](/images/use-dify/workflow/human-input-workflow-example.png)
</Frame>
<Frame caption="Request Form Example">
![Request Form Example](/images/use-dify/workflow/human-input-request-form-example.png)
</Frame>
</div>

This workflow drafts a blog post from the `topic` and `language` that a workflow initiator inputs, emails the draft to a reviewer, and finalizes the output based on the reviewer's choice.

It is designed around three things the reviewer should be able to do:

1. **See the AI-generated draft**: Reference the upstream LLM node's `text` variable in the form so the rendered form displays the draft directly.

2. **Edit the draft directly if needed**: Add a Paragraph field named `edits` in the form, pre-filled with the same `text` variable, so the reviewer sees the draft as starting content and can edit in place.

Because blog posts are long, the form's Markdown display (point 1) reads better than a Paragraph field on its own. For shorter content, the pre-filled Paragraph field alone can handle both reading and editing.

3. **Provide feedback for an AI revision**:

1. Add a Paragraph field named `feedback` in the form for the reviewer's feedback.
2. Connect two downstream LLM nodes in sequence:
1. A Regenerate node that takes the original draft `text` and the reviewer's `feedback` to produce a revised draft.
2. A Check Revision node that takes `feedback` and the revised draft to verify whether the revision addresses the feedback. The verified result is what flows downstream.

On the received request form, the reviewer fills the relevant Paragraph fields (or leaves them blank) based on their judgment, then clicks the matching action button. Each action wires to a different output:

- **Approve**: the original draft from the upstream LLM
- **Apply Edit**: the reviewer's edited content from the `edits` field
- **Regenerate**: the revised draft from the downstream LLM pipeline

<Accordion
title="LLM Node Prompts for Reference"
><Tabs>
<Tab title="Generate Draft">
**System**

```text wrap
Write a marketing blog post around the given topic in the specified language.
```

**User**

```text
Topic: {{#user_input.topic#}}
Language: {{#user_input.language#}}
```

</Tab>
<Tab title="Regenerate">
**System**

```text wrap
Regenerate the draft based on user feedback.
```

**User**

```text
Draft: {{#generate_draft.text#}}
User Feedback: {{#human_input.feedback#}}
```

</Tab>
<Tab title="Check Revision">
**System**

```text wrap
Check whether the draft below addresses the user's feedback. Return the draft unchanged if it does; revise it to address the feedback if it doesn't.
```

**User**

```text
User Feedback: {{#human_input.feedback#}}
Regenerated Draft: {{#regenerate.text#}}
```

</Tab>
</Tabs>

</Accordion>
Binary file modified images/use-dify/workflow/human-input-request-form-example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading