Skip to main content

Nodes

Nodes are the steps that do the work between the trigger and the result. Each node runs a plugin. The node library in the editor is the complete, up-to-date reference; this page covers how a node is configured and the families you'll use most.

How a node is configured

Every node has the same fields: an id, a type, the plugin_type it runs, inputs wired with expressions, and a plugin-specific config. Retries are declared per node under error_handling:

- id: create_item
type: module
plugin_type: np-action-item-create
error_handling:
retry_policy:
max_attempts: 3
initial_interval: "2s"
backoff_strategy: exponential
jitter: 0.3
config:
apiKey: "${{ secrets.NP_API_KEY }}"
categorySlug: "${{ workflow.inputs.category_slug }}"
nrn: "${{ workflow.inputs.finding.scope_nrn }}"
title: "Deprecated AMI on scope ${{ workflow.inputs.finding.scope_name }}"
priority: medium

To run a node once per element of an array (one action item per finding, one API call per path), add a forEach block:

- id: ensure_items
type: module
plugin_type: sub-workflow
forEach:
expression: "${{ steps.detect_drift.outputs.findings }}"
itemVariable: finding
parallel: true
maxConcurrency: 5
config:
workflowId: wf_ensure_action_item
waitForCompletion: true

Inside the node, each element is available as inputs.finding. Iterations retry independently, and in parallel mode a failed iteration doesn't stop the rest. Loops and iteration covers the other ways to repeat work, and when each one fits.

Nullplatform nodes

These nodes talk to your organization. The ones you'll use most:

  • np-api-call: an authenticated request to any nullplatform REST endpoint, with optional pagination. Prefer a dedicated node when one exists for the operation.
  • np-lake-query: read-only SQL against the data lake. Each row becomes one item for downstream steps, and organization scoping is automatic.
  • Action items family: create, find, update, comment on, and wait for action items, plus their suggestions. The find node searches by a metadata key, which is how a scanner avoids opening the same item twice.
  • Checklists and deployments: create checklists and wait for their outcome, or wait for deployment follow-up actions like a traffic switch.
  • np-agent-command: dispatch a command to a nullplatform agent and optionally wait for the result.

Branch, wait, and call other workflows

Deciders branch the run. conditional evaluates a boolean expression and sends each item out its true or false port; case does the same with several ports. Write decider expressions without the ${{ }} wrapper:

- id: missing
type: decider
plugin_type: conditional
config:
expression: "variables.found.count == 0"

Four more nodes control the flow. sub-workflow calls another workflow by id, either waiting for it to finish or not. split-in-batches processes a large array in chunks through a loop. delay pauses for a fixed duration. signal-wait parks the run until an external signal or a timeout arrives; a parked run costs nothing while it waits and survives restarts, which is what makes waiting for Jira or for a person practical.

Transform data with code and variables

The code-exec node runs inline JavaScript in isolation. The code receives the step's resolved inputs as read-only values, returns an object, and anything it prints with log.* lands in the run's log view. By default there's no require, no fetch, and no filesystem. If the step declares network hosts or npm libraries in its config, the engine runs it in an isolated runtime with exactly those capabilities. What goes into and comes out of the code is capped at 1 MB each way, tighter than any other step, so trim a large result before it reaches the code rather than inside it.

For simpler needs, set-variable writes per-run variables that later steps read as ${{ variables.* }}, and log writes a message to the run's log and never fails.

Call external systems

For systems outside nullplatform: http-request for any HTTP call, paginated-fetch for APIs that page their results, and webhook-wait to create a one-time callback URL and pause until it's called. Jira nodes create, find, and wait for issues to reach a status. Slack nodes post messages, and two of them bring people into the loop: slack-ask posts buttons and waits for a click, slack-wait-message waits for a reply in a thread.

All of them take their credentials from secrets and variables, never inline.

Next steps