Dynamiq
WorkflowsOrchestration

Map Node

Run one node once per item of a list — fan-out with optional concurrency, failure behavior, and a predictable list output.

The Map node takes a list and runs a single configured inner node once per item — an LLM call per document, an agent per ticket, a Python function per record. Iterations can run sequentially or in parallel, and the results come back as one ordered list, so a Map is the standard fan-out/fan-in primitive in Dynamiq workflows.

How it works

  • Input — one required field, input, which must be a list of objects. Each object becomes the full input of one inner-node run.
  • Execution — the inner node is cloned per item (with fresh internal ids, so each iteration appears separately in the trace), and the clones run over the list, in parallel up to the concurrency limit.
  • Output — a single output field: the list of inner-node outputs, in the same order as the input items.
// input
{ "input": [ { "ticket": "Printer is on fire" }, { "ticket": "Reset my password" } ] }

// output (inner node = an LLM)
{ "output": [ { "content": "..." }, { "content": "..." } ] }

Configure a Map on the canvas

Add the node and pick what it runs

Drag Map from the LOGIC section of the node palette onto the canvas. In its configuration panel, the Node selector chooses the inner node — any node type is allowed. Use the gear icon next to it to open and configure the inner node exactly as you would a standalone node.

The Map node configuration panel with the inner Node selector, Behavior dropdown, and concurrency checkbox

Map the input list

In the Input field, select the upstream variable that holds your list (press / to open the variable picker), for example:

$.split-tickets.output.items

The value must be a list of objects. If your upstream data is a plain list of strings, wrap each entry first — see Shaping items below.

Choose failure behavior and concurrency

Behavior controls what happens when one iteration fails:

  • raise — the whole Map node fails as soon as any iteration fails. Use when a partial result is useless.
  • return — failed iterations don't stop the run; each iteration's output (including a failed one's) is kept in the output list. Use when you'd rather process what succeeded and inspect failures downstream.

Check Set concurrency limit to run iterations in parallel and set Max workers (1–100; the field defaults to 10 when enabled). Leave it unchecked to run items one at a time. Parallel iterations multiply your LLM provider's request rate — size the limit against your rate limits.

Shaping items for the inner node

Each list item is passed to the inner node as its entire input, so the item's keys must match what the inner node expects:

  • Inner LLM node — item keys are available to the prompt's Jinja template. With items like {"ticket": "..."}, write {{ ticket }} in the prompt.
  • Inner Agent node — agents take an input string, so shape items as {"input": "Classify this ticket: ..."}.
  • Inner Python node — with Multiple params enabled (the default), item keys arrive as named function arguments: def run(ticket, **kwargs).

When the upstream output isn't already in that shape, put a small Python node before the Map to reshape it:

def run(tickets, **kwargs):
    # tickets: ["Printer is on fire", "Reset my password", ...]
    return {"items": [{"input": f"Classify this support ticket: {t}"} for t in tickets]}

…then map the Map node's Input to $.reshape.output.items. This is the standard nested-mapping pattern: reshape once, iterate cleanly.

Fan-out / fan-in example

A complete pattern — split, process per item, aggregate:

  1. Python node reshape — turns the raw input into a list of {"input": ...} items (code above).
  2. Map node — inner node is an Agent that classifies one ticket; Input is $.reshape.output.items; Behavior return; Max workers 10.
  3. Python node aggregate — fans the results back in:
def run(results, **kwargs):
    # results: $.map.output — one entry per ticket, in input order
    labels = [r.get("content", "unclassified") for r in results]
    return {"labels": labels, "total": len(labels)}

Map results to $.map.output. Because Map preserves input order, labels[i] always corresponds to ticket i — no correlation bookkeeping needed.

SDK equivalent

from dynamiq import Workflow
from dynamiq.connections import OpenAI as OpenAIConnection
from dynamiq.flows import Flow
from dynamiq.nodes import Behavior
from dynamiq.nodes.llms import OpenAI
from dynamiq.nodes.operators import Map
from dynamiq.prompts import Message, Prompt

classifier = OpenAI(
    name="ticket-classifier",
    model="gpt-4o-mini",
    connection=OpenAIConnection(),
    prompt=Prompt(
        messages=[
            Message(role="user", content="Classify this support ticket: {{ ticket }}"),
        ],
    ),
)

workflow = Workflow(
    flow=Flow(
        nodes=[Map(node=classifier, behavior=Behavior.RETURN, max_workers=10)],
    ),
)

result = workflow.run(
    input_data={
        "input": [
            {"ticket": "Printer is on fire"},
            {"ticket": "Reset my password"},
        ]
    }
)

In the SDK, max_workers defaults to 1 (sequential) and behavior defaults to Behavior.RETURN.

Pitfalls

On this page