waitForInput
The waitForInput step pauses workflow execution until a human submits input. It's the human-in-the-loop primitive for collecting structured input, such as escalation checkpoints, review steps, and multi-field decisions.
For the end-to-end pattern, including the Inbox app, external Slack delivery, and design guidance, refer to Human-in-the-loop. For a fixed approve/reject decision without a custom schema, use waitForApproval
Schema convention. In each schema table on this page, the Location column indicates where each parameter sits in the YAML:
top level: Alongsidetypeandname, at the top of the step or trigger definition.`with`: Inside the step'swith:block.`on`: Inside the trigger'son:block.
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
name |
top level | string | Yes | Unique step identifier. |
type |
top level | string | Yes | Must be waitForInput. |
timeout
|
top level | duration | No | How long the step waits for input. Format: number + unit (ms/s/m/h/d/w). Defaults to 72h. If no one responds before the timeout, the step fails. |
message |
with |
string | No | Markdown message displayed to the responder. |
schema |
with |
object | No | JSON Schema that describes the expected input. Renders as a form in the Kibana UI and Inbox Respond flyout, and validates the resume payload. If you omit schema, the resume accepts any payload. |
channels
|
with |
object | No | External notification channels that send resume links. Responders can act without signing in to Kibana. See External channels. |
Use with.channels to notify responders outside Kibana. Slack is the only built-in channel. Responders can act without a Kibana session, either by opening a hosted HTML form or by using a query link.
| Channel key | Connector type | Required fields | Notes |
|---|---|---|---|
slack |
Slack webhook (slack) |
connector-id |
Posts to the channel configured on the webhook connector. |
slack_api |
Slack API (slack_api) |
connector-id, channels (array of Slack channel IDs) |
Posts interactive notifications to the listed channels. |
Both channel configs accept an optional message template. Use {{ context.hitl.externalFormLink }} for the hosted form URL and {{ context.hitl.externalQueryLink }} for the query link. If you omit message, the notification includes the form link by default.
The query link is a base URL that carries the resume token. To resume through it, responders must append at least one schema field as a query parameter (for example, &approved=true). A query link with no schema field parameters returns an error and directs the caller to use the form link instead. When in doubt, use the hosted form link.
External channels send public, short-lived resume links. Don't use them for destructive, production-impacting, or hard-to-reverse workflows.
To turn off external resume, set both hitlExternalResume.enabled keys in kibana.yml (both default to true). For the exact settings, refer to Human-in-the-loop.
How the resume payload appears on the step output depends on the Stack version:
The submitted fields are under response, and who responded is respondedBy:
response:
approved: true
notes: "..."
respondedBy: "..."
- The submitted payload (fields from your schema)
- Who responded
Downstream steps reference fields under response: {{ steps.<step_name>.output.response.<field> }}.
The resume body is the step output directly. Downstream steps reference fields like {{ steps.<step_name>.output.<field> }}.
approved: true
notes: "..."
- While waiting, the execution state is
WAITING_FOR_INPUT. -
There is no step-level default timeout. The workflow waits until someone responds, or until a workflow-level settings.timeoutcancels the run. -
By default, the step times out after 72h. This default applies whether you configure an external channel. Override it with a top-leveltimeouton the step. If no one responds before the timeout, the step fails.
- name: review
type: waitForInput
timeout: 24h
with:
message: |
## Confirm host isolation
Host `{{ event.alerts[0].host.name }}` was flagged by rule {{ event.rule.name }}.
AI classification: **{{ steps.classify.output.category }}**
Proceed with isolation?
schema:
type: object
properties:
approved:
type: boolean
title: "Isolate the host"
notes:
type: string
title: "Reviewer notes"
required: ["approved"]
- name: isolate
type: http
if: "steps.review.output.response.approved : true"
connector-id: "edr-connector"
with:
method: "POST"
url: "https://edr.example.com/isolate"
body:
host: "{{ event.alerts[0].host.name }}"
reason: "{{ steps.review.output.response.notes }}"
- overrides the 72h default
- name: review
type: waitForInput
timeout: 72h
with:
message: "Review this escalation and submit your decision."
schema:
type: object
properties:
approved:
type: boolean
title: "Approve"
notes:
type: string
title: "Notes"
required: ["approved"]
channels:
slack:
connector-id: my-slack-webhook-connector
slack_api:
connector-id: my-slack-api-connector
channels: ["C0123456789"]
- Human-in-the-loop pattern: The full HITL pattern, including Inbox and external resume.
waitForApprovalstep: Approve/reject without a custom schema. - Flow control steps: Other flow-control types you'll often combine with
waitForInput. - If step: Typical gate around the post-review action.