Input nodes
Input nodes pause a workflow and wait for a response before it continues. Use them to add human-in-the-loop steps such as approvals, manual gates, and decision points that require extra input.
When the workflow reaches an input node, the node stays in progress and notifies the responders you defined. Each responder opens the run, fills in a form, and clicks one of the node's buttons. Like condition nodes, an input node branches: it routes the workflow down a different path depending on which button responders press and how many of them respond.
How it works
The lifecycle of an input node is:
- The workflow reaches the input node, which enters an in progress state and pauses the run on that branch.
- Port evaluates any JQ templates in the node's
descriptionanduserInputs, then notifies the configured responders (by email and/or webhook). - A responder opens the run, clicks Provide inputs, fills in the form fields (with any resolved defaults already filled in), and clicks one of the submit buttons.
- Port records the response and re-evaluates the node's outlets.
- When an outlet reaches its required number of responders, the node completes and the workflow continues down that outlet's path.
Example
The following input node asks responders to approve or decline, and continues on the approve path only after two approvals, or on the decline path after a single decline:
{
"identifier": "inputNode",
"title": "Approve/Decline",
"config": {
"type": "INPUT",
"description": "Approve or decline the production deployment.",
"userInputs": {
"properties": {
"reason": {
"type": "string",
"title": "Reason",
"description": "The reason for the approve/decline."
}
},
"buttons": [
{
"identifier": "approve",
"label": "Approve",
"variant": "PRIMARY"
},
{
"identifier": "decline",
"label": "Decline",
"variant": "DANGER"
}
]
},
"outlets": [
{
"evaluationMethod": "button",
"identifier": "approve",
"title": "Approve",
"numOfResponders": 2
},
{
"evaluationMethod": "button",
"identifier": "decline",
"title": "Decline",
"numOfResponders": 1
}
],
"responders": {
"users": ["alice@example.com", "bob@example.com"]
}
}
}
Configuration
An input node is defined by a config object with "type": "INPUT" and the following fields:
| Field | Required | Description |
|---|---|---|
type | Yes | Must be "INPUT". |
description | No | A short explanation shown to responders. Supports JQ templates. |
userInputs | Yes | The form responders fill in, plus the submit buttons. Supports JQ templates. See user inputs below. |
outlets | Yes | The branches the node can continue on, and how many responders each branch requires. See outlets below. |
responders | No | The users who are notified and allowed to respond. See responders below. |
notifications | No | Where Port sends notifications when the node is reached. See notifications below. |
User inputs
userInputs defines two things:
properties- the form fields shown to responders. These use the same schema as self-service user inputs, so you can collect text, numbers, entities, and any other supported input type.buttons- the submit buttons shown in the footer of the form. Each button records a different kind of response (for example, Approve or Decline).
Each button has the following fields:
| Field | Required | Description |
|---|---|---|
identifier | Yes | A unique identifier for the button. Outlets reference this value. |
label | Yes | The text shown on the button. |
variant | Yes | The button's visual style: PRIMARY, SECONDARY, or DANGER. |
icon | No | An optional icon to display on the button. |
Each button's identifier must be unique within the node, and every outlet must reference a valid button identifier.
Dynamic values with JQ
description and userInputs support the same {{ }} JQ templates as other mid-workflow nodes. When the workflow reaches the input node, Port evaluates the templates against the workflow run context and shows the resolved values in the provide-inputs form.
Use this to personalize the prompt responders see, or to pre-fill form defaults from earlier node outputs:
Approve deployment node example (click to expand)
{
"identifier": "approve-deploy",
"title": "Approve deployment",
"config": {
"type": "INPUT",
"description": "Approve deploying {{ .outputs.trigger.service }} version {{ .outputs.trigger.version }} to production.",
"userInputs": {
"properties": {
"service": {
"type": "string",
"title": "Service",
"default": "{{ .outputs.trigger.service }}",
"readOnly": true
},
"reason": {
"type": "string",
"title": "Reason",
"description": "Why you are approving or declining this deployment."
}
},
"required": ["reason"],
"buttons": [
{
"identifier": "approve",
"label": "Approve",
"variant": "PRIMARY"
},
{
"identifier": "decline",
"label": "Decline",
"variant": "DANGER"
}
]
},
"outlets": [
{
"evaluationMethod": "button",
"identifier": "approve",
"title": "Approve",
"numOfResponders": 1
},
{
"evaluationMethod": "button",
"identifier": "decline",
"title": "Decline",
"numOfResponders": 1
}
],
"responders": {
"users": ["{{.workflowRun.trigger.by.email}}"]
}
}
}
In this example, the description and the service default resolve from the trigger outputs before responders open the form. Any string field under userInputs (including titles, descriptions, defaults, and button labels) can use the same template syntax.
jqQueryInput node templates use {{ .outputs... }}, the same syntax as action nodes. This is different from the {"jqQuery": "..."} object used for dynamic defaults on self-service triggers, which evaluate against the live form state.
Outlets
The node stays in progress until one of its outlets is satisfied. Each outlet maps to a button and defines how many responders must press that button before the workflow continues down its path.
In the example above, the node continues on the approve path after 2 approvals, or on the decline path after a single decline.
Each outlet has the following fields:
| Field | Required | Description |
|---|---|---|
evaluationMethod | Yes | How the outlet is evaluated. Currently "button" is supported. |
identifier | Yes | Must match the identifier of one of the node's buttons. |
title | No | A human-readable name for the outlet, shown in the graph. |
numOfResponders | Yes | The number of responses on this button required to continue down this path. Must be a positive integer. |
statusLabel | No | A label applied to the node run when this outlet is taken. Has a text field (supports JQ) and an optional variant of success or alert. |
workflowStatusLabel | No | Same as statusLabel, but applied to the entire workflow run. |
Port tallies responses per button. After each response, it checks the outlets in the order they are defined and continues on the first outlet whose numOfResponders threshold has been reached. Outlet identifiers must be unique within the node.
Responders
responders defines who is notified and allowed to respond to the node. Responders are specified by users, a list of user emails:
{
"responders": {
"users": ["alice@example.com", "bob@example.com"]
}
}
Each responder can submit a response only once. Attempting to respond again returns an error.
Notifications
notifications controls where Port sends a notification when the node is reached. It is an array of targets, each of which is one of:
- An email target, with a list of
fields(each alabelandvalue) to include in the notification. - A webhook target, used to notify an external system. It supports
url,method(defaults toPOST), and optionalheadersandbody.
{
"notifications": [
{
"target": "email",
"fields": [
{ "label": "Service", "value": ".outputs.trigger.serviceName" }
]
},
{
"target": "webhook",
"url": "https://example.com/notify",
"method": "POST",
"body": { "message": "An input node is awaiting your response" }
}
]
}
Responders specified under responders.users are automatically notified by email, in addition to any targets you define here.
To disable notifications for an input node, set notifications to an empty array ([]). Omitting the field keeps the default behavior.
Connections
Like condition nodes, connections from an input node must specify which outlet they belong to, using sourceOutletIdentifier:
{
"connections": [
{
"sourceIdentifier": "inputNode",
"targetIdentifier": "production-deploy",
"sourceOutletIdentifier": "approve"
},
{
"sourceIdentifier": "inputNode",
"targetIdentifier": "sendSlack",
"sourceOutletIdentifier": "decline"
}
]
}
Each sourceOutletIdentifier must reference a valid outlet identifier on the node.
Outputs
An input node produces the following outputs, which subsequent nodes can reference through data flow:
selectedOutlet.identifier- the identifier of the outlet the node continued on (for example,approve).responses- the list of submitted responses. Each response includes the button that was pressed (buttonIdentifier), the submitted form values (inputs), and submitter information (submitterInfowithsubmittedByandcreatedAt).
For example, to branch on the chosen outlet in a downstream node:
{{ .outputs["inputNode"].selectedOutlet.identifier }}
To reference a value a responder submitted in the form:
{{ .outputs["inputNode"].responses[0].inputs.reason }}
Related links
- Condition nodes - Branch a workflow based on JQ expressions.
- Data flow - Pass outputs between nodes.
- User inputs - Configure the form fields responders fill in.