Inquir Compute
Browse sections

Pipelines and graph workflows

Automate cron schedules, async jobs, DAG fan-out, retries, and webhook-triggered workflows with either step pipelines or graph pipelines—each reuses the same serverless functions as your HTTP routes.

Automate multi-step work in two ways: step pipelines (ordered list of function steps with triggers) and visual graph pipelines (a canvas of nodes and edges). Both live under Pipelines in the sidebar — create New step pipeline or New visual pipeline from the list.

Visual graph editor

The graph editor is for HTTP-style workflows: triggers feed Lambda and control-flow nodes; a Map node shapes JSON; Respond sends the HTTP response to the client.

  • From Pipelines, open a visual pipeline card and choose Open visual editor (or create one with New visual pipeline).
  • Drag node types from the left palette — Triggers (Manual, HTTP, Cron), Steps (Lambda, If, Set, Map, Respond, Human), the Parallel group (Parallel and Merge), and Note. Connect handles to define execution order.
  • Click a node to edit its settings in the side panel (function binding, expressions, template JSON, status codes, and so on).
  • Note nodes are documentation-only: they are not executed and must not be connected by edges — use them as on-canvas annotations.

The visual editor lets you wire Manual, HTTP, and Cron triggers into Lambda, If, Map, Respond, and Human nodes; the screenshot below shows the canvas inside the console.

Screenshot of the visual pipeline editor in the Inquir console
Visual pipeline editor in the console.

Graph node types (reference)

Connect bottom handles to the top handle of the next node. Triggers, Parallel, Map, Set, and Merge expose one default output. Lambda exposes ok and err outputs; If exposes true and false. Human gate (approve) exposes ok and no outputs (approve/reject handles); Human gate (question) uses one default output. Respond and Note have no outgoing handles. The graph must always include at least one trigger.

Manual trigger

Starts a run from the pipeline UI or execute API. No extra configuration. Use this (or another trigger) as the beginning of a path.

HTTP trigger

Starts a run when the pipeline’s public HTTP entry receives a request with the configured method and path (for example POST /hook). The graph type also stores auth metadata (none, API key, or bearer) for that entry; defaults apply when you add the node from the palette.

Cron trigger

Starts a run on a schedule. Set a five-field cron expression and a timezone string such as UTC. Invalid cron is rejected when you save or publish.

Lambda (function)

Invokes a workspace function. Pick the function in the properties panel. On error: failPipeline stops the run; continue still follows the ok handle while recording the failure; errorBranch routes to the err handle—wire that handle for recovery paths. Leave input payload override empty to pass the previous node’s output, the trigger body after a trigger, or a structured error payload when a prior step failed and the graph continued. Optional override JSON uses the same template placeholders as elsewhere, but input is not wired for that field—use trigger, vars, and steps paths. The executor supports retryPolicy in graph JSON; the UI focuses on function, onError, and the override.

If (branch)

Splits flow based on a boolean expression. Connect both the true and false outgoing handles. Use one comparison (===, !==, ==, !=, or numeric inequalities with coercion) or a single path for truthiness — not compound expressions with && or || yet (chain If nodes or add a Map step if you need that). While evaluating, input.* is built from the previous step’s output: if it is a string that looks like a JSON object or array, it is parsed so fields work like a Lambda JSON body. Values written by Set nodes are shallow-merged into the same input view (vars override on duplicate keys); you can still use vars.NAME or steps.NODE_ID.output… explicitly. Default step output is the upstream payload unchanged (the condition value is stored separately as ifEvaluated on the step, not mixed into output). To attach the boolean to JSON for downstream nodes, set Output shape (outputMapping), e.g. with {{ifResult}}. Optional Output shape uses {{input}}, {{ifResult}}, {{vars…}}, and {{steps…}}.

Write one comparison (left operator right) or a single dotted path for truthiness — not compound && or || (use another If or a Map step). Do not wrap the expression in {{ }} — that syntax is only for JSON template fields. Replace NODE_ID with a real graph node id. Examples:

  • Upstream string equals — input.tier == 'pro' (single or double quotes around the literal).
  • Upstream number greater than — input.score > 50
  • Strict equality on a boolean-like field — input.ok === true (also works with false or null on the right).
  • Another node’s output — steps.NODE_ID.output.status == 'done'
  • Pipeline variable from Set — vars.environment == 'production'
  • Numeric greater-or-equal — input.retryCount >= 3
  • Truthiness (non-binary form): a single path such as input.recordId or steps.NODE_ID.output.items — the branch is true when the resolved value is truthy.
  • Inequality — input.code != 404 or input.code !== 404 (== / != allow coercion; === / !== do not).

Parallel

Fans out from one incoming edge to many outgoing edges on the parallel handle. Each downstream branch runs independently until a Merge (or another join pattern) collects them.

Merge

Joins branches after Parallel. Mode all (the supported mode) waits until every direct predecessor branch has finished, then runs once. Without outputMapping: a single incoming branch passes its payload through unchanged; multiple branches produce an object with an inputs map keyed by each upstream source node id. With outputMapping, use {{inputs}} for the full map or {{inputs.someNodeId}} for one branch; with only one incoming edge, {{input}} is also set to that branch’s payload (same as Map).

Set

Writes pipeline variables for later template placeholders. Provide a JSON object; values may include template strings. Resolved entries are merged into vars. The node’s output merges those values onto the upstream object when the upstream value is a plain object.

Map

Builds JSON for the next step using outputMapping and template resolution, with the upstream payload exposed as input in templates (see the graph template section above). If outputMapping is omitted, the engine defaults to passing input through.

Respond

Completes an HTTP-triggered run: set status code, optional response body templates, and optional response headers (a JSON object of header values; each value may use the same placeholders as the body). Leave Response body empty (no outputMapping or legacy bodyMapping) to return the previous step’s output unchanged—or the trigger body if Respond follows a trigger directly. Example header object: Content-Type application/json and X-Request-Id set to {{vars.requestId}}. This node has no outgoing edges. Prefer sync execution when the client should wait for this response.

Human gate

Pauses the run until someone calls POST /api/pipeline-graph-executions/:executionId/resume. Approve mode requires two outgoing edges labeled approve and reject; the API body is { "decision": "approve" } or { "decision": "reject" }. Question mode uses one default edge; the body is { "answer": <any JSON> }. Optional promptTemplate is a JSON template ({{input}} is the upstream payload) stored on the waiting step for operators. The completed step output merges the upstream payload with humanGate: { mode, decision or answer, optional prompt }.

Note

Documentation on the canvas: title, long description, optional link, and resizable box. Not executed; do not attach edges. Reachability validation ignores notes.

Step pipelines: JSON shape

Classic pipelines are configured as JSON (via API or the step editor): a trigger, execution mode, and an ordered list of steps. Example:

pipeline.json
// Pipeline config (API / stored config): trigger + executionMode + steps
// Each step needs id, name, and functionId (lambda steps). Order uses "order" or list order in sequential mode.
{
  "trigger": { "type": "manual" },
  "executionMode": "sequential",
  "steps": [
    { "id": "validate", "name": "Validate", "functionId": "fn-1" },
    { "id": "process", "name": "Process", "functionId": "fn-2" },
    { "id": "notify", "name": "Notify", "functionId": "fn-3" }
  ]
}

Event for each step

Unlike HTTP gateway invokes, pipeline steps receive one JSON object (not API Gateway shape). The run payload is spread at the top level for convenience and kept under payload as well. Context also includes pipeline, step, previousOutput, previousStepResults, and stepResults.

event (per step)
// Lambda step handlers receive one object. Payload fields are spread at the top level as well as under "payload".
{
  "userId": "u1",
  "payload": {
    "userId": "u1"
  },
  "pipeline": {
    "id": "pl_abc",
    "name": "Example"
  },
  "step": {
    "id": "process",
    "name": "Process"
  },
  "previousOutput": null,
  "previousStepResults": {},
  "stepResults": {}
}

Trigger Types

  • Manual — Trigger from the UI or API.
  • HTTP/API Trigger — Start runs with POST /pipelines/:id/execute and a JSON payload.
  • Schedule — Cron-based scheduling (e.g. 0 */6 * * *).

Manual Approval

Add approval steps that pause the pipeline until a user approves or the timeout expires.

Graph pipelines: JSON templates

In the visual graph editor, several nodes accept JSON whose string values can contain placeholders. The engine replaces each segment that uses the double-curly placeholder syntax (see the patterns below) with data from the current run.

  • {{input}} — Upstream payload from the previous node (or trigger body after a trigger). Use {{input.field}} for nested paths. Wired for Map, Respond (body and header values), optional If output JSON, and Merge outputMapping when there is only one incoming branch.
  • {{trigger.body}}, {{trigger.type}}, etc. — Data from the run trigger (available in all template fields on these nodes).
  • {{vars.name}} — Values written by Set nodes earlier in the graph.
  • {{steps.NODE_ID.output}} — Output of a finished step; substitute your graph node id for NODE_ID. Nested paths work (e.g. {{steps.abc.output.score}}).
  • {{inputs}} — While resolving a Merge node’s outputMapping, this object is keyed by each incoming branch’s source node id. Use {{inputs.someNodeId}} for one branch’s payload (substitute your graph node id).
  • {{ifResult}} — While resolving an If node’s optional outputMapping only: the boolean the condition evaluated to. In later nodes, the same value is available as {{steps.IF_NODE_ID.ifEvaluated}} (use the If node’s id). Not available inside the condition text itself.

If a string is only a single placeholder, the resolved value keeps its JSON type (object, array, number). Inside a longer string, values are stringified.

How this applies by node:

  • Lambda — Leave Input payload override empty to pass the previous node’s output (or trigger payload) unchanged. If you set it, build the object with {{trigger…}}, {{vars…}}, and {{steps…}}. {{input}} is not used for Lambda overrides.
  • Merge — Default behavior without outputMapping is described under Merge above. Custom outputMapping uses {{inputs…}}; with a single incoming edge, {{input}} works too.
  • If — The condition field is plain text (not {{ }}). The engine builds input from the predecessor output (parses JSON-looking strings) merged with vars from Set. Use input.field, steps.id.output…, or vars… as needed. Optional Output shape is JSON with placeholders like Map, plus {{ifResult}}.
  • MapoutputMapping describes the object this node emits; {{input}} refers to the upstream payload (engine default when omitted is pass-through {{input}}).
  • Respond — Optional outputMapping shapes the HTTP body; omit both outputMapping and legacy bodyMapping to return the upstream body as-is. Header values in headersMapping use the same rules (e.g. set Content-Type with application/json).
  • Note — Not part of execution or templates; ignored by the engine. Do not attach edges to or from notes.
graph-templates.json
// In Map, Respond (body/headers), and Lambda input override, values may be JSON
// with strings like "{{trigger.body.field}}" or "{{steps.lambdaNodeId.output}}".

// Map node — outputMapping (default shape is pass-through of upstream via {{input}})
{
  "data": "{{input}}",
  "userId": "{{input.userId}}",
  "enriched": "{{steps.analyze.output}}"
}

// Respond node — optional outputMapping; omit it to return the upstream body unchanged
{
  "result": "{{input}}",
  "meta": { "source": "pipeline" }
}

// Respond headers (same rules; header values are strings after resolution)
{
  "X-Request-Id": "{{steps.firstLambda.output.requestId}}"
}

// Lambda — input payload override only when you need a custom shape (otherwise leave empty for chain input)
{
  "item": "{{trigger.body}}",
  "prior": "{{steps.stepA.output}}"
}