Tasks

Create a task

Creates a task bound to a workflow. By default the task is placed in the workflow's initial state; passing state places it directly in that named state instead — an alternate entry point for starting a task mid-flow (e.g. "a new recorte for an existing theme by id"), rather than re-submitting from the initial state and hoping a guard or similarity gate recognizes it. Entering the resulting state, initial or named, behaves identically: that state's on_enter automation fires and its stalled_after clock arms.

post/v1/projects/{project_id}/tasks

Request

  • Base URL: https://api.naturali.ai
  • URL: https://api.naturali.ai/v1/projects/{project_id}/tasks
  • 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)

Headers

Idempotency-Keystring

The request's deduplication key. Equivalent to idempotency_key in the request body; sending both with different values is 400 VALIDATION_FAILED. A non-empty string of at most 255 characters.

Request body

workflow_idstring required
titlestring required
payloadobject
assigneestring nullable
idempotency_keystring

The create's deduplication key, unique within the project and claimed for as long as the task it created exists. The same key with the same body returns that task as it is now (200) instead of creating a second one; with a different body it is 409 IDEMPOTENCY_KEY_REUSED. A create that fails claims no key. Equivalent to the Idempotency-Key header.

statestring

Name of a declared workflow state to create the task in directly, instead of the workflow's initial state. Must name a state declared on the workflow, or the request is rejected with TASK_STATE_NOT_FOUND (400). Defaults to the initial state.

tool_contextobject

Key-value pairs forwarded as X-Naturali-Context-<key> headers on every http and mcp tool call made by this task's automation dispatches — the agent generations a state's on_enter starts, and the agent nodes of any orchestration run it starts. The header name is X-Naturali-Context- plus the key verbatim; no character is re-cased. Creation is the task's first move, so this is the bag the entry state's on_enter runs with. Each transition may replace it (see TransitionTaskRequest.tool_context). The reserved identity keys (session_id, actor_id, actor_external_id) are stripped in any casing and re-derived server-side, so a task-dispatched generation cannot forge them. A key outside the HTTP header-name grammar is rejected with INVALID_TOOL_CONTEXT_KEY (400). Write-only: the stored bag is never returned by any task read, and it is cleared when the task reaches a terminal state.

metadataMetadataBag

Caller-owned annotations on a resource, stored as the object they were written as: the types a value was written with are the types a read returns, so a filter can ask an ordering question about a number. Unlike other body fields, keys are stored and returned verbatim in the casing supplied — they are not converted between snake_case and camelCase.

No key is reserved, and that is the point: every piece of state the platform owns lives in its own typed column, so nothing written here reaches platform state. The platform never reads the bag — it is not an IAM context, not a policy input and not part of a prompt — which is what separates it from a tag bag.

Example request

{
  "metadata": {
    "author": "John",
    "revision": 2
  }
}

Response

The idempotency key already names a task created by the same request; that task is returned as it is now and no second task is created

idstring
project_idstring
workflow_idstring
workflow_versioninteger nullable

The workflow version this task runs on, fixed when the task was created. Transitions, approval gates and payload validation all resolve through it, so editing the workflow never re-shapes a task already in flight. null for tasks created before pinning existed, which run on the live definition.

titlestring
statestring
status'open' | 'closed'
payloadobject

Caller-owned task data; input to guards (as task.payload) and dispatch mappings. The engine never writes into it except the workflow's declared payload_writes.

metadataMetadataBag

Caller-owned annotations on a resource, stored as the object they were written as: the types a value was written with are the types a read returns, so a filter can ask an ordering question about a number. Unlike other body fields, keys are stored and returned verbatim in the casing supplied — they are not converted between snake_case and camelCase.

No key is reserved, and that is the point: every piece of state the platform owns lives in its own typed column, so nothing written here reaches platform state. The platform never reads the bag — it is not an IAM context, not a policy input and not part of a prompt — which is what separates it from a tag bag.

last_resultunknown
assigneestring nullable
active_dispatchobject nullable

{ kind, id, status } of the current state's dispatch, if any. kind is generation, orchestration_run or tool_call; a tool_call always carries a null id, since a direct tool call leaves no addressable record. Carries an additional attempt (1-based) while the state's on_enter.retry policy is in effect.

automation_status'running' | 'completed' | 'failed' | 'unrouted' | 'paused' | 'null' nullable

Status of the current state's dispatch. null until a state with an automation is entered. paused means an operator pause suppressed this state's on_enter before it ran, so resume-task will start it.

pause_requested_atstring date-time nullable

When an operator paused this task's automation, or null when no pause is in force.

pause_reasonstring nullable

The reason supplied with the pause, when one was.

automation_chain_depthinteger

Server-owned. How many machine-driven transitions have run back-to-back with no outside intervention — a dispatch outcome routed through on_complete/on_failure, or a transition-task call made by a dispatched run or agent with its run-as token. Any move by a person, a plain API key, or an approval resolution resets it to 0. Once it would exceed the server's limit (TASK_AUTOMATION_CHAIN_LIMIT, default 50) the next such transition is refused with TASK_AUTOMATION_CHAIN_LIMIT, bounding a cycle composed across workflows and orchestrations.

pending_transitionstring nullable

The name of a requires_approval transition parked awaiting a human decision. Non-null while an ApprovalItem gates the move; the task stays in its current state and no other transition may fire until the approval resolves.

entered_state_atstring date-time
created_atstring date-time
updated_atstring date-time

Example response

{
  "workflow_version": 1,
  "metadata": {
    "author": "John",
    "revision": 2
  }
}

Changes

No recorded changes to this operation across all 1 revision of this API.