Context Resolution

Every workflow run maintains a context object. It starts with the input payload and grows as each step adds its output. The @path syntax lets any step reference data produced by earlier steps.


How Context Accumulates

flowchart TD subgraph "Context after input" I["@input.invoices<br/>@input.payments"] end subgraph "Context after Step 1 (Matcher)" M["+ @matched<br/>+ @unmatched_invoices<br/>+ @unmatched_payments"] end subgraph "Context after Step 2 (ReAct)" R["+ @investigation"] end subgraph "Context after Step 3 (Approval)" A["+ @__approved<br/>+ @__comment"] end I --> M --> R --> A

Each step can read anything from context that was set before it. Steps cannot read from steps that haven't executed yet.


@path Reference Table

Path Source Description
@input.* Workflow execution payload The JSON body sent to POST /workflows/:id/execute
@input.field.nested Nested input Dot notation traverses nested objects
@outputKey.* Step output References output by outputKey or default output name
@matched Matcher Array of matched record pairs
@unmatchedLeft Matcher Left-side records with no match (or custom name via outputUnmatchedLeft)
@unmatchedRight Matcher Right-side records with no match (or custom name via outputUnmatchedRight)
@item Foreach loop Current item being processed
@item.field Foreach loop Field on the current loop item
@__run_id System Current run identifier
@__step System Current step index (0-based)
@now System Current ISO timestamp
@__approved PbotApproval Boolean — whether the reviewer approved
@__comment PbotApproval Comment from the reviewer's decision
doc:doc_xxx Document Storage Resolves uploaded document content (latest version). CSVs → Array, JSON → Object, text → String, binary → handle
doc:doc_xxx@N Document Storage Resolves specific document version N — for audit reproducibility

Naming Step Outputs

By default, matcher outputs use their configured output names (outputMatched, outputUnmatchedLeft, outputUnmatchedRight). Other steps store results under their outputKey or under a default name.

json
{
  "type": "loop",
  "properties": {
    "mode": "react",
    "objective": "Investigate exceptions",
    "tools": [{ "type": "action", "name": "lookup_record" }],
    "result_key": "investigation"
  }
}

The agent's final answer is stored at @investigation in context, accessible by all subsequent steps.

For registered actions, outputKey controls the context key:

json
{
  "type": "fetch_customer",
  "properties": { "customer_id": "@input.id" },
  "outputKey": "customer_data"
}

Result available at @customer_data for subsequent steps.


Nested Path Resolution

Dot notation traverses nested objects to any depth:

json
{
  "type": "send_notification",
  "properties": {
    "email": "@customer_data.contact.email",
    "name": "@customer_data.contact.first_name",
    "company": "@customer_data.organization.name"
  }
}

Array Indexing

Access specific array elements by index:

json
{
  "first_match_id": "@matched.0.invoice_id",
  "second_match_amount": "@matched.1.amount"
}

Access array length:

json
{
  "condition": {
    "greaterThan": [{ "length": "@unmatched_invoices" }, 0]
  }
}

Template Interpolation: {{ }}

Double braces perform string interpolation — they convert a value to a string and embed it in surrounding text. Use for building human-readable messages, email subjects, Slack posts, and log entries.

json
{
  "type": "gmail_send",
  "properties": {
    "subject": "Invoice {{input.invoice_id}} — {{input.vendor_name}}",
    "body": "Dear {{input.vendor_name}},\n\nYour invoice for ${{input.amount}} has been processed.\n\nMatched records: {{matched.length}}\nExceptions: {{unmatched_invoices.length}}"
  }
}

@path vs {{ }} — These serve different purposes. @path returns the raw value (object, array, number, boolean) and is used for data references in properties. {{template}} converts to string and embeds in text. Use @path when passing data. Use {{ }} when constructing messages.

json
{
  "data_reference": "@input.invoices",
  "message": "Processing {{input.invoices.length}} invoices"
}
---

Worked Example: 4-Step Workflow

json
{
  "name": "full_context_example",
  "definition": {
    "actions": [
      {
        "type": "matcher",
        "properties": {
          "left": "@input.invoices",
          "right": "@input.payments",
          "matchOn": ["invoice_id"],
          "tolerance": 0.02,
          "outputMatched": "reconciled",
          "outputUnmatchedLeft": "exceptions"
        }
      },
      {
        "type": "loop",
        "properties": {
          "mode": "foreach",
          "items_path": "@exceptions",
          "item_variable_name": "exception",
          "actions_to_execute": [
            {
              "type": "lookup_vendor",
              "properties": {
                "vendor_id": "@exception.vendor_id"
              }
            }
          ],
          "collect_results": true,
          "result_key": "vendor_lookups"
        }
      },
      {
        "type": "PbotApproval",
        "filter": {
          "condition": { "greaterThan": ["@exceptions.length", 5] }
        },
        "properties": {
          "comment": "{{exceptions.length}} exceptions found — review before proceeding",
          "request_payload": {
            "exception_count": "@exceptions.length",
            "vendor_details": "@vendor_lookups",
            "reconciled_count": "@reconciled.length"
          }
        }
      },
      {
        "type": "custom-table",
        "properties": {
          "table": "recon_log",
          "operation": "write",
          "keys": ["run_id"],
          "values": ["@__run_id"],
          "fields": {
            "matched": "@reconciled.length",
            "exceptions": "@exceptions.length",
            "approved": "@__approved",
            "timestamp": "@now"
          }
        }
      }
    ]
  }
}

Context at each step:

After Step Keys Added Available
Input @input.invoices, @input.payments input
Step 1 (Matcher) @reconciled, @exceptions input + matcher output
Step 2 (Loop) @vendor_lookups input + matcher + loop results
Step 3 (Approval) @__approved, @__comment input + matcher + loop + approval
Step 4 (Table) — (writes to storage) everything

→ Next: Conditional Logic