Loading

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 instead.

Schema convention. In each schema table on this page, the Location column indicates where each parameter sits in the YAML:

  • top level: Alongside type and name, at the top of the step or trigger definition.
  • `with`: Inside the step's with: block.
  • `on`: Inside the trigger's on: 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.

Warning

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: "..."
		
  1. The submitted payload (fields from your schema)
  2. 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.timeout cancels the run.
  • By default, the step times out after 72h. This default applies whether you configure an external channel. Override it with a top-level timeout on 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 }}"
		
  1. 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"]