---
title: "Create an orchestration"
method: POST
path: "/v1/projects/{project_id}/orchestrations"
tags: ["Orchestrations"]
---

# Create an orchestration

`POST /v1/projects/{project_id}/orchestrations`

## Request

- Base URL: `https://api.naturali.ai`
- URL: `https://api.naturali.ai/v1/projects/{project_id}/orchestrations`
- Auth: one of:
  - HTTP bearer
  - OAuth 2 (authorization code, authorization URL `https://api.naturali.ai/authorize`, token URL `https://api.naturali.ai/token`, scopes: `mcp:access`)

Creates a new orchestration (pipeline) definition in the project.

## Request body

- CreateOrchestrationRequest
  - `name` string, required: Human-readable name.
  - `description` string, nullable
  - `nodes` OrchestrationNode[], required: The graph's nodes. Each `id` is unique and is what edges and `nodes.<id>` state references name; a node with no incoming edge starts the run.
    - `id` string, required: Unique node identifier within this orchestration.
    - `type` string, required: Node execution type. Known types: agent, tool, transform, knowledge, condition, human, approval, loop, poll, delay, webhook, emit_event, sub_orchestration. Open set — new types may be added in minor releases, and an unrecognized type is accepted at create time (the run fails when the node dispatches), so clients must tolerate unknown values.
    - `agent_id` string: For agent nodes — public ID of the agent to invoke.
    - `tool_id` string: For tool and poll nodes — public ID of the tool to call.
    - `operation_id` string: For tool and poll nodes — specific operation/action on MCP tools.
    - `expression` unknown
    - `exit_condition` unknown
    - `prompt` string: For human nodes — prompt shown to the human reviewer.
    - `options` string[]: For human nodes — constrained choices.
    - `arguments` object: For approval nodes — input-mapping-style object (JSON Logic values) resolved against run state into the proposed tool call's arguments, frozen onto the created approval item.
    - `expires_in` integer: For approval nodes — seconds until the created approval item expires. Defaults to 86400 (24h) when omitted. An expired item can never execute; the run routes down its `on_expired` edge.
    - `instructions` string: For approval nodes — optional guidance shown to the approver.
    - `reasoning` unknown
    - `evidence` unknown
    - `predicted_impact` unknown
    - `input_mapping` object: Maps node input keys to values. Each value is JSON Logic (https://jsonlogic.com), the same evaluator used by transform and condition nodes. A single-key object is evaluated against the run state — `{"var": "key"}` reads `state.key`, `{"cat": [...]}` and `{">": [...]}` compute derived values. Any other value (string, number, boolean, array, multi-key object) is passed through as a literal.
    - `state_mapping` object: Maps state write paths to values. Each key is a `state.<path>` destination (the `state.` prefix is optional); each value is JSON Logic (https://jsonlogic.com) evaluated against `{ "output": <node artifact>, "state": <run state> }` — e.g. `{ "summary": {"var": "output.content"} }` writes the artifact's `content` field to `state.summary`. The same evaluator as input_mapping/transform/condition; only the context differs. An `agent` node's artifact always carries both `content` (the text response) and `object` (the parsed value when a schema applied, `null` otherwise), so `output.content` reads the same whether or not the node declares an `output_schema`.
    - `output_schema` object: For agent nodes — JSON Schema the model's answer is parsed into, reaching the artifact as `object`. Declaring it here is only needed for an agent that carries no `output_schema` of its own: the agent's schema already produces the artifact's `object` wherever that agent generates.
    - `collection` string: For loop nodes — state path to the collection to iterate over.
    - `item_variable` string: For loop nodes — variable name injected into state for each item.
    - `parallelism` integer: For loop nodes — number of items to process in parallel.
    - `on_item_error` 'fail' | 'collect': For loop nodes — what a failed item does. `fail` (the default) fails the node on the first item whose child run settles `failed`, `cancelled` or `expired`. `collect` keeps going: that item's entry in `results` becomes `{ "error": { "code", "message" }, "orchestration_run_id" }`, in its original position. Either way the artifact carries `failed_count`. A child run that could not be started (for example past the nesting depth bound) fails the node in both modes.
    - `context_keys` string[], nullable: For loop and sub_orchestration nodes — allowlist of the run's `tool_context` keys the child run inherits. When `null` (the default), the child inherits the parent's whole bag. When set, only the listed keys are handed down, so a run holding a broad credential can delegate one step to a shared sub-graph without passing on what that sub-graph does not need; `[]` hands down nothing. Matching is case-insensitive, since an entry names a key that becomes an HTTP header name; an entry outside that grammar is rejected at write time with `INVALID_TOOL_CONTEXT_KEY`. The server-derived identity keys (`session_id`, `actor_id`, `actor_external_id`) are unaffected — they are re-derived per generation in the child regardless of this list. Ignored for other node types.
    - `interval` string: For poll nodes — wait between attempts. Accepts a friendly suffix form (`5s`, `30s`, `5m`, `2h`, `500ms`) or ISO 8601 (e.g. PT5S).
    - `fail_on_timeout` boolean: For poll nodes — when max_iterations is reached without the exit condition becoming true, fail the run (true) instead of completing with condition_met=false (default false).
    - `duration` string: For delay nodes — how long to wait. Accepts a friendly suffix form (`5s`, `30s`, `5m`, `2h`, `500ms`) or ISO 8601 (e.g. PT5S).
    - `mode` 'receive': For webhook nodes — parks the run awaiting an inbound callback. `receive` is the only mode; to send a notification out of a graph, use an `emit_event` node instead.
    - `event_type` string: For emit_event nodes — the internal event type to emit (e.g. `guardrail.exception`). The node's input_mapping becomes the event `data`. Any Webhook subscribed to this event type in the run's project then delivers it — signed, retried, and tracked by the Webhooks module — so the graph holds no URL or secret of its own.
    - `orchestration_id` string: Public ID of the orchestration this node runs — the child orchestration for sub_orchestration nodes, and the orchestration run once per item for loop nodes.
    - `max_iterations` integer: Maximum iterations before the node is aborted. For poll nodes this is the maximum number of attempts (default 10, ceiling 1000).
    - `retry` object: Retry-on-failure policy. When the node throws a transient error (unexpected/infrastructure errors and upstream 5xx) and attempts remain, the run parks as `sleeping` and re-executes the node after the backoff delay. Terminal errors (4xx business errors) fail immediately. Absent or `max_attempts <= 1` means fail-fast.
      - `max_attempts` integer: Total attempts including the first (default 1, ceiling 20).
      - `backoff` object
        - `strategy` 'fixed' | 'exponential': `fixed` waits `delay_ms` between every attempt; `exponential` doubles per prior attempt. Default `fixed`.
        - `delay_ms` integer: Base delay between attempts in ms (default 1000).
        - `max_delay_ms` integer: Cap on the computed backoff delay in ms (default 300000).
  - `edges` OrchestrationEdge[], required: Directed edges between node `id`s. Every `from`/`to` must name a node in `nodes`, and the graph must be acyclic unless it contains a `loop` node. A node with several outgoing edges activates every target in parallel. `[]` for a graph of independent nodes.
    - `from` string, required: Source node ID.
    - `to` string, required: Target node ID.
    - `condition` string: For condition node routing — label to match against condition output.
    - `activation_group` string: Groups edges for join semantics.
    - `activation_condition` 'all' | 'any': Whether all or any edges in the activation group must come from a completed node before the target runs. Omitted, `all`.
  - `state_schema` object, nullable
  - `input_schema` object, nullable
  - `output_mapping` OrchestrationOutputMapping, nullable: The shape of a completed run's `output`, owned by the orchestration rather than by its terminal node ids. Each key is an output field (a dotted key such as `summary.title` builds a nested object); each value is JSON Logic (https://jsonlogic.com) evaluated against `{ "state": <final run state> }`, which includes `state.input` and `state.nodes.<id>`. A missing path maps to `null`; a mapping that throws fails the run. Null (or omitted) keys `output` by terminal node id. Versioned with the graph; `null` on update clears it.
  - `version_label` string: Optional tag for the version this create archives, e.g. `initial`.

## Response `201`

Orchestration created

- Orchestration
  - `id` string, required: Public ID (orch_...).
  - `project_id` string, required: Public ID of the owning project.
  - `name` string, required: Human-readable name.
  - `description` string, nullable: Optional description.
  - `version` integer, required: Incremented on every write that changes the graph; prior versions are archived. A run pins the version it started on, so these fields are a draft for runs started from now on rather than a live rewrite of the ones already executing.
  - `nodes` OrchestrationNode[], required
    - `id` string, required: Unique node identifier within this orchestration.
    - `type` string, required: Node execution type. Known types: agent, tool, transform, knowledge, condition, human, approval, loop, poll, delay, webhook, emit_event, sub_orchestration. Open set — new types may be added in minor releases, and an unrecognized type is accepted at create time (the run fails when the node dispatches), so clients must tolerate unknown values.
    - `agent_id` string: For agent nodes — public ID of the agent to invoke.
    - `tool_id` string: For tool and poll nodes — public ID of the tool to call.
    - `operation_id` string: For tool and poll nodes — specific operation/action on MCP tools.
    - `expression` unknown
    - `exit_condition` unknown
    - `prompt` string: For human nodes — prompt shown to the human reviewer.
    - `options` string[]: For human nodes — constrained choices.
    - `arguments` object: For approval nodes — input-mapping-style object (JSON Logic values) resolved against run state into the proposed tool call's arguments, frozen onto the created approval item.
    - `expires_in` integer: For approval nodes — seconds until the created approval item expires. Defaults to 86400 (24h) when omitted. An expired item can never execute; the run routes down its `on_expired` edge.
    - `instructions` string: For approval nodes — optional guidance shown to the approver.
    - `reasoning` unknown
    - `evidence` unknown
    - `predicted_impact` unknown
    - `input_mapping` object: Maps node input keys to values. Each value is JSON Logic (https://jsonlogic.com), the same evaluator used by transform and condition nodes. A single-key object is evaluated against the run state — `{"var": "key"}` reads `state.key`, `{"cat": [...]}` and `{">": [...]}` compute derived values. Any other value (string, number, boolean, array, multi-key object) is passed through as a literal.
    - `state_mapping` object: Maps state write paths to values. Each key is a `state.<path>` destination (the `state.` prefix is optional); each value is JSON Logic (https://jsonlogic.com) evaluated against `{ "output": <node artifact>, "state": <run state> }` — e.g. `{ "summary": {"var": "output.content"} }` writes the artifact's `content` field to `state.summary`. The same evaluator as input_mapping/transform/condition; only the context differs. An `agent` node's artifact always carries both `content` (the text response) and `object` (the parsed value when a schema applied, `null` otherwise), so `output.content` reads the same whether or not the node declares an `output_schema`.
    - `output_schema` object: For agent nodes — JSON Schema the model's answer is parsed into, reaching the artifact as `object`. Declaring it here is only needed for an agent that carries no `output_schema` of its own: the agent's schema already produces the artifact's `object` wherever that agent generates.
    - `collection` string: For loop nodes — state path to the collection to iterate over.
    - `item_variable` string: For loop nodes — variable name injected into state for each item.
    - `parallelism` integer: For loop nodes — number of items to process in parallel.
    - `on_item_error` 'fail' | 'collect': For loop nodes — what a failed item does. `fail` (the default) fails the node on the first item whose child run settles `failed`, `cancelled` or `expired`. `collect` keeps going: that item's entry in `results` becomes `{ "error": { "code", "message" }, "orchestration_run_id" }`, in its original position. Either way the artifact carries `failed_count`. A child run that could not be started (for example past the nesting depth bound) fails the node in both modes.
    - `context_keys` string[], nullable: For loop and sub_orchestration nodes — allowlist of the run's `tool_context` keys the child run inherits. When `null` (the default), the child inherits the parent's whole bag. When set, only the listed keys are handed down, so a run holding a broad credential can delegate one step to a shared sub-graph without passing on what that sub-graph does not need; `[]` hands down nothing. Matching is case-insensitive, since an entry names a key that becomes an HTTP header name; an entry outside that grammar is rejected at write time with `INVALID_TOOL_CONTEXT_KEY`. The server-derived identity keys (`session_id`, `actor_id`, `actor_external_id`) are unaffected — they are re-derived per generation in the child regardless of this list. Ignored for other node types.
    - `interval` string: For poll nodes — wait between attempts. Accepts a friendly suffix form (`5s`, `30s`, `5m`, `2h`, `500ms`) or ISO 8601 (e.g. PT5S).
    - `fail_on_timeout` boolean: For poll nodes — when max_iterations is reached without the exit condition becoming true, fail the run (true) instead of completing with condition_met=false (default false).
    - `duration` string: For delay nodes — how long to wait. Accepts a friendly suffix form (`5s`, `30s`, `5m`, `2h`, `500ms`) or ISO 8601 (e.g. PT5S).
    - `mode` 'receive': For webhook nodes — parks the run awaiting an inbound callback. `receive` is the only mode; to send a notification out of a graph, use an `emit_event` node instead.
    - `event_type` string: For emit_event nodes — the internal event type to emit (e.g. `guardrail.exception`). The node's input_mapping becomes the event `data`. Any Webhook subscribed to this event type in the run's project then delivers it — signed, retried, and tracked by the Webhooks module — so the graph holds no URL or secret of its own.
    - `orchestration_id` string: Public ID of the orchestration this node runs — the child orchestration for sub_orchestration nodes, and the orchestration run once per item for loop nodes.
    - `max_iterations` integer: Maximum iterations before the node is aborted. For poll nodes this is the maximum number of attempts (default 10, ceiling 1000).
    - `retry` object: Retry-on-failure policy. When the node throws a transient error (unexpected/infrastructure errors and upstream 5xx) and attempts remain, the run parks as `sleeping` and re-executes the node after the backoff delay. Terminal errors (4xx business errors) fail immediately. Absent or `max_attempts <= 1` means fail-fast.
      - `max_attempts` integer: Total attempts including the first (default 1, ceiling 20).
      - `backoff` object
        - `strategy` 'fixed' | 'exponential': `fixed` waits `delay_ms` between every attempt; `exponential` doubles per prior attempt. Default `fixed`.
        - `delay_ms` integer: Base delay between attempts in ms (default 1000).
        - `max_delay_ms` integer: Cap on the computed backoff delay in ms (default 300000).
  - `edges` OrchestrationEdge[], required
    - `from` string, required: Source node ID.
    - `to` string, required: Target node ID.
    - `condition` string: For condition node routing — label to match against condition output.
    - `activation_group` string: Groups edges for join semantics.
    - `activation_condition` 'all' | 'any': Whether all or any edges in the activation group must come from a completed node before the target runs. Omitted, `all`.
  - `state_schema` object, nullable: Optional JSON Schema for state validation.
  - `input_schema` object, nullable: Schema for run inputs (initial state).
  - `output_mapping` OrchestrationOutputMapping, nullable: The shape of a completed run's `output`, owned by the orchestration rather than by its terminal node ids. Each key is an output field (a dotted key such as `summary.title` builds a nested object); each value is JSON Logic (https://jsonlogic.com) evaluated against `{ "state": <final run state> }`, which includes `state.input` and `state.nodes.<id>`. A missing path maps to `null`; a mapping that throws fails the run. Null (or omitted) keys `output` by terminal node id. Versioned with the graph; `null` on update clears it.
  - `created_at` string, date-time, required
  - `updated_at` string, date-time, required

## Other responses

- `400`: Validation error
- `401`: Unauthorized
- `403`: Forbidden

---

[API](https://skmtc.dev/naturali/apis/naturali-ai-api.md) · [All operations](https://skmtc.dev/naturali/apis/naturali-ai-api/llms.txt) · [OpenAPI document](https://skmtc.dev/naturali/apis/naturali-ai-api/revisions/e4905b78f422?raw)
