For the complete documentation index, see llms.txt. This page is also available as Markdown.

Autonomous routines

Autonomous routines run end-to-end without a conversation: typed JSON in via trigger endpoint or webhook, typed JSON out via signed callback.

Context — This page covers the autonomous execution mode: what happens between POST /routines/{id}/trigger and the callback hitting your service. It assumes you know routines. Authoring details: Authoring autonomous routines; wire shapes: Events & callbacks.

YAML examples follow manifest schema 6.1.5. Manifest and content shapes are schema-versioned and differ across runtime versions — see Versioning & compatibility.

What changes in autonomous mode

A routine becomes autonomous by declaring an autonomous: block:

id: kyc-decision
title: KYC Decision
conditions:
  - The KYC verification result for an applicant needs to be processed.
entry: assess
nodes:
  - id: assess
    think: >
      Assess the verification payload. Decide approved, rejected, or
      escalate, with a one-sentence explanation.
    output_schema:
      type: object
      required: [decision, explanation]
      properties:
        decision:
          enum: [approved, rejected, escalate]
        explanation:
          type: string
    transitions:
      - to: finish

  - id: finish
    tools: built-in:emit_output
    tool_instruction: >
      Call emit_output with output_json containing the decision and
      explanation produced by the assess node.

autonomous:
  input_schema:
    type: object
    required: [applicant_id, verification_result]
    properties:
      applicant_id:
        type: string
      verification_result:
        type: object
  output_schema:
    type: object
    required: [decision, explanation]
    properties:
      decision:
        enum: [approved, rejected, escalate]
      explanation:
        type: string
  timeout_seconds: 60
  callback_url_allowlist:
    - api.example.com

The same engine runs the same node machinery — but:

Conversational
Autonomous

Woken by

Customer message

Trigger endpoint or webhook

Input

Free text

JSON validated against input_schema

Output

Messages streamed as events

One JSON object validated against output_schema

Terminates

Agent speaks

A terminal node calls built-in:emit_output

Result delivery

Event stream

Signed HTTP callback to your URL

Session

Yours, long-lived

Ephemeral by default (created and deleted per run)

The autonomous block

Field
Type
Required
Default
Meaning

input_schema

JSON Schema

yes

Validates the trigger/webhook payload. Rejection → HTTP 400, no run.

output_schema

JSON Schema

yes

Validates the final output at emit_output time. The runtime tightens it with additionalProperties: false on all object nodes.

timeout_seconds

integer ≥ 1

no

operator default (120s)

Per-run deadline. Capped by the operator maximum (600s). See Limits & defaults.

callback_url_allowlist

list of hostnames

no

empty = any

Hostnames callbacks may target. An entry starting with . matches the apex and subdomains (.example.com matches example.com and api.example.com); a full-URL entry matches by its hostname only. No wildcards — omit the field to allow any host. Disallowed URL → HTTP 400.

webhook

object

no

Opts this routine into its own POST /webhooks/{routine_id} entry — see webhook entry points.

The terminal rule: every terminal node of an autonomous routine (a node with no outbound transitions) must be a TOOL node calling built-in:emit_output. Terminal THINK nodes are rejected at validation time. A run that ends any other way never succeeds — it times out or hits the iteration cap and the callback reports failure.

Node types

An autonomous routine is built from the same graph of nodes as a conversational routine, with two differences driven by the fact that there is no customer in the conversation: it uses no CHAT nodes, and it ends by emitting typed output rather than by speaking.

Node type
Set by
What it does

TOOL

tools (+ tool_instruction)

Calls one or more tools. Completes when the tool executes. The terminal node is always a TOOL node calling built-in:emit_output.

THINK

think (+ output_schema)

Typed inference — a model call that returns JSON matching output_schema, with no message. Does not end the turn, so THINK and TOOL nodes chain freely within a run.

Routing-only (fork)

no action field, 2+ conditional transitions

A pure branch point — the engine evaluates the outgoing conditions and takes the matching path.

No CHAT nodes. An autonomous run has no conversational customer to speak to — its result is delivered as typed JSON via emit_output, not as a message. chat_state nodes don't belong in an autonomous routine.

THINK node — typed inference

A THINK node runs a model call (the built-in built-in:reason tool) whose output must match the node's output_schema. The runtime tightens that schema with additionalProperties: false on every object so the model can't smuggle in extra fields, and a validation failure is a hard error. The validated result is stored at session.metadata.step_outputs[<node-id>] and is visible in history, so later nodes — and your emit_output step — can reference the typed fields. Because it doesn't end the turn, you chain think → tool → think → emit_output within a single run. This is generic engine node-completion behavior, not specific to autonomous routines — THINK nodes chain the same way in conversational routines; a CHAT node is the only node type that ends the turn.

A THINK node can never be a routine's terminal node — the run's typed output comes from emit_output, so a THINK must always transition onward.

Run lifecycle

  1. TriggerPOST /routines/{routine_id}/trigger with:

    session_id may be supplied to run inside an existing session instead of an ephemeral one. idempotency_key makes retries safe: a duplicate key returns the prior run (HTTP 409) instead of starting a new one. metadata is opaque and echoed verbatim in the callback.

  2. Accepted — the response is immediate:

    HTTP 202. Everything after this is asynchronous.

  3. Execution — the engine walks the routine's graph exactly as described in Conversation lifecycle, with the validated input visible to every node as a tool result in history.

  4. Completion — the routine calls built-in:emit_output with the final JSON. The runtime validates it against output_schema; on success the run is succeeded, on schema violation it is failed with output_validation_failed.

  5. Callback — the server POSTs the result to your callback_url with Authorization: Bearer <agent api key> (the same key you use inbound), retrying with backoff on non-2xx (default 5 attempts). Payload:

    Dedupe deliveries on run_id. Return 200 as soon as you've durably enqueued the payload; do heavy work afterwards.

  6. Cleanup — ephemeral session and customer are deleted after the callback settles.

Failure taxonomy

status: "failed" carries a structured error:

error.code

Meaning

Your usual fix

timeout

Run exceeded timeout_seconds

Raise the timeout, or shorten/parallelise the routine

max_engine_iterations_reached

Iteration cap hit before emit_output

Raise runtime.max_engine_iterations, or shorten the routine

output_validation_failed

emit_output payload violated output_schema (details include the JSON path)

Fix the schema or the terminal node's tool_instruction

input_validation_failed

Input rejected (also surfaces as HTTP 400 at trigger time)

Fix the caller's payload

engine_error

Internal engine failure during the run

Check traces/logs; see Troubleshooting

tool_error

A tool call failed irrecoverably

Check the MCP server

session_error

Session preparation/dispatch failed

Check server health; retry with the same idempotency_key

Webhook entry points

Third-party providers (KYC vendors, payment gateways) can fire autonomous routines directly by POSTing to the agent — authenticated by an HMAC signature over the raw body instead of the bearer token. Webhook-triggered runs are fire-and-forget: no callback is sent; results live in traces and the run store.

Two declaration styles:

Per-routine (autonomous.webhook in the routine YAML) — exposes POST /webhooks/{routine_id}; the routine fires unconditionally on a valid signature:

Manifest-level fan-out (agent_config.webhooks[]) — one URL, POST /webhooks/{name}, dispatching to one or more autonomous routines. With multiple routines, the engine evaluates each routine's conditions against the payload and fires the one(s) that match:

conditions: only matters here. Multi-routine webhook fan-out is the only path where an autonomous routine's conditions: are evaluated against anything. Direct POST /routines/{id}/trigger calls and single-routine (inline) webhooks activate their target routine unconditionally — the routine id itself is the hard gate, not the condition text. So conditions: on an autonomous routine is a no-op everywhere except manifest-level fan-out with 2+ routines.

Shared mechanics:

  • Signature = prefix + hex(HMAC(secret, raw_body)) using algorithm (sha256, sha1, or sha512), carried in header. Verification is constant-time; failure → 401.

  • The secret env var is re-read on every request, so rotating it needs no restart.

  • The raw JSON body becomes the run's input and must satisfy the routine's input_schema (a fan-out match whose schema rejects the body is dropped with a warning).

  • Replays dedupe automatically: the idempotency key is derived from the webhook name, routine id, and a hash of the body, so a provider retry returns the existing run (HTTP 200) instead of firing twice.

  • These paths bypass bearer auth — the signature is the auth. See Security.

The fan-out response enumerates what fired:

Tracing autonomous runs

Each run is one trace. By default the trace is named with a synthetic run id; set agent_config.traces.trace_id_field to an input-payload key (e.g. customer_id) to name traces after your business identifier instead. The trace_id in the callback links the result back to the trace. See Observability.

See also

Last updated

Was this helpful?