> For the complete documentation index, see llms.txt.
Skip to main content

Check out Port for yourself ➜ 

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:

  1. The workflow reaches the input node, which enters an in progress state and pauses the run on that branch.
  2. Port evaluates any JQ templates in the node's description and userInputs, then notifies the configured responders (by email and/or webhook).
  3. 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.
  4. Port records the response and re-evaluates the node's outlets.
  5. 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:

FieldRequiredDescription
typeYesMust be "INPUT".
descriptionNoA short explanation shown to responders. Supports JQ templates.
userInputsYesThe form responders fill in, plus the submit buttons. Supports JQ templates. See user inputs below.
outletsYesThe branches the node can continue on, and how many responders each branch requires. See outlets below.
respondersNoThe users who are notified and allowed to respond. See responders below.
notificationsNoWhere 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:

FieldRequiredDescription
identifierYesA unique identifier for the button. Outlets reference this value.
labelYesThe text shown on the button.
variantYesThe button's visual style: PRIMARY, SECONDARY, or DANGER.
iconNoAn optional icon to display on the button.
Button identifiers must be unique

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.

Template syntax vs form jqQuery

Input 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:

FieldRequiredDescription
evaluationMethodYesHow the outlet is evaluated. Currently "button" is supported.
identifierYesMust match the identifier of one of the node's buttons.
titleNoA human-readable name for the outlet, shown in the graph.
numOfRespondersYesThe number of responses on this button required to continue down this path. Must be a positive integer.
statusLabelNoA 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.
workflowStatusLabelNoSame as statusLabel, but applied to the entire workflow run.
How outlets are evaluated

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"]
}
}
One response per responder

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 a label and value) to include in the notification.
  • A webhook target, used to notify an external system. It supports url, method (defaults to POST), and optional headers and body.
{
"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 (submitterInfo with submittedBy and createdAt).

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 }}