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 a configured result key or a step's documented default output
@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 that is present when the reviewer approved
@__comment PbotApproval Comment from the reviewer's decision
doc:doc_xxx Document Storage Resolves supported uploaded content from the latest version
doc:doc_xxx@N Document Storage Resolves a supported document version for reproducible execution

Naming Step Outputs

Matcher outputs use their configured output names (outputMatched, outputUnmatchedLeft, outputUnmatchedRight). ReAct loops use result_key. Registered LLM and DB workflow actions use properties.outputKey or their documented default.

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 LLM and DB workflow actions, properties.outputKey controls the context key:

json
{
  "type": "get_customer_record",
  "properties": { "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": 50,
          "outputMatched": "reconciled",
          "outputUnmatchedLeft": "exceptions"
        }
      },
      {
        "type": "loop",
        "properties": {
          "mode": "foreach",
          "items_path": "@exceptions",
          "item_variable_name": "exception",
          "actions_to_execute": [
            {
              "type": "custom-table",
              "properties": {
                "table": "vendor_exception_queue",
                "operation": "upsert",
                "key_fields": ["run_id", "invoice_id"],
                "keys": ["run_id", "invoice_id", "vendor_id", "status"],
                "values": ["@__run_id", "@exception.invoice_id", "@exception.vendor_id", "ready"]
              }
            }
          ],
          "collect_results": true,
          "result_key": "exception_writes"
        }
      },
      {
        "type": "PbotApproval",
        "filter": {
          "condition": { "greaterThan": ["@exceptions.length", 5] }
        },
        "properties": {
          "comment": "{{exceptions.length}} exceptions found. Review before proceeding.",
          "request_payload": {
            "exception_count": "@exceptions.length",
            "exceptions": "@exceptions",
            "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