Sea12Docs
Engine & Data

Data Flow & Templates

Template syntax, the data bucket, type preservation, built-in functions, file references.


How Data Flows

When a node completes, its output data is:

  1. Stored in the data bucket under the node's label (display name).
  2. Merged into each child node's input under the parent's label.
  3. Available to all downstream nodes via template references, not just direct children.

Example Flow

[Email Input] → [Extract Order] → [Calculate Quote] → [Send Quote]

After Email Input completes with {"sender": "john@example.com", "body": "..."}:

  • The bucket contains: {"Email Input": {"sender": "john@example.com", "body": "..."}}
  • Extract Order receives this as input.

After Extract Order completes with {"Order Request": {"weight_lbs": 5000}}:

  • The bucket contains both nodes' outputs.
  • Calculate Quote can reference both Email Input and Extract Order.

After Calculate Quote completes:

  • Send Quote can reference all three upstream nodes.

Template Syntax

Templates use double curly braces: {{expression}}

Path References

The primary form references a node's output field:

{{input["Node Label"]["field_name"]}}

Bracket notation (recommended):

{{input["Email Input"]["sender"]}}
{{input["Extract Order"]["Order Request"]["weight_lbs"]}}
{{input["Calculate Quote"]["output"]["total_price"]}}

Nested fields:

{{input["Node"]["address"]["city"]}}
{{input["Node"]["items"][0]["name"]}}

Array elements:

{{input["Node"]["items"][0]}}     -- first element
{{input["Node"]["items"][1]}}     -- second element

File References

Files are under a special "files" key:

{{input["files"]["Node Label"][0]["name"]}}      -- filename
{{input["files"]["Node Label"][0]["url"]}}         -- file URL
{{input["files"]["Node Label"][0]["size"]}}       -- size in bytes

Escaped Quotes in JSON

When templates appear inside JSON config values, the quotes inside the template are escaped:

JSON
{
  "url": "https://api.example.com/{{input[\"Node\"][\"id\"]}}"
}

The engine automatically unescapes these before resolution.


Built-in Functions

Functions use the syntax: {{functionName(arg1, arg2)}}

Arguments can be:

  • String literals: "quoted text"
  • Path references: input["Node"]["field"]
  • Nested function calls: lower(input["Node"]["name"])

Function Reference

FunctionSignatureDescriptionExampleResult
uuiduuid()Generate a UUID v4{{uuid()}}"a1b2c3d4-e5f6-..."
nownow("format")Current time in Go format{{now("2006-01-02")}}"2026-04-12"
lowerlower(value)Lowercase string{{lower("HELLO")}}"hello"
upperupper(value)Uppercase string{{upper("hello")}}"HELLO"
lengthlength(value)Count elements in array, string, or map{{length(input["N"]["items"])}}3
joinjoin(array, separator)Join array elements{{join(input["N"]["tags"], ", ")}}"a, b, c"
defaultdefault(value, fallback)Return fallback if value is nil or empty{{default(input["N"]["x"], "N/A")}}"N/A"
toJSONtoJSON(value)Serialize to JSON string{{toJSON(input["N"]["data"])}}"{\"key\":\"val\"}"
fileExtensionfileExtension(path)Extract file extension{{fileExtension("doc.pdf")}}".pdf"

now() Format Strings

Go uses a reference time for format strings. The reference time is:

Mon Jan 2 15:04:05 MST 2006

Common formats:

Format StringOutput
"2006-01-02"2026-04-12
"2006-01-02T15:04:05Z"2026-04-12T14:30:00Z
"01/02/2006"04/12/2026
"January 2, 2006"April 12, 2026
"15:04:05"14:30:00
"Mon 3:04 PM"Sat 2:30 PM

Nesting Functions

Functions can be nested:

{{lower(fileExtension(input["Node"]["filename"]))}}

This extracts the extension and lowercases it: ".PDF" -> ".pdf".

{{default(lower(input["Node"]["name"]), "unknown")}}

If name is nil, returns "unknown". If name is "JOHN", returns "john".


Type Preservation

Whole-Value Replacement

When the entire JSON value is a single template, the resolved type is preserved:

JSON
{
  "count": "{{input[\"Node\"][\"count\"]}}",
  "active": "{{input[\"Node\"][\"active\"]}}",
  "items": "{{input[\"Node\"][\"items\"]}}"
}

If count is 42 (number), active is true (boolean), and items is [1,2,3] (array), the result is:

JSON
{
  "count": 42,
  "active": true,
  "items": [1, 2, 3]
}

Partial Interpolation

When a template is part of a larger string, the result is always a string:

JSON
{
  "message": "Order has {{input[\"Node\"][\"count\"]}} items"
}

Result: {"message": "Order has 42 items"} (string).

Implications

  • Use whole-value templates when you need numbers, booleans, or objects downstream.
  • Use partial interpolation for building strings (email bodies, URLs, messages).

Unresolved Templates

If a path cannot be resolved (node doesn't exist, field is missing), the template is left as-is:

{{input["Missing Node"]["field"]}}

Remains literally {{input["Missing Node"]["field"]}} in the output.

The engine generates warnings for unresolved references that include suggestions for close matches (using fuzzy matching).


Variable Name Sanitization

The engine performs case-insensitive matching with name sanitization:

  • Hyphens and spaces are converted to underscores
  • "Order-Request" matches "Order_Request" or "order_request"

This provides resilience against minor naming inconsistencies, particularly with LLM-generated field names that may normalize to snake_case.


Conditional Node References

Conditional nodes use a slightly different reference syntax within their condition fields:

In Condition Fields

Dot notation:

NodeName.fieldName
NodeName.nested.field

Bracket notation:

input["NodeName"]["field"]

String literals (quoted):

"literal value"

Interpolated strings:

"Hello {NodeName.firstName}"
"user-{NodeName.id}@example.com"
Conditional field references use single curly braces {...} for interpolation within quoted strings, not double curly braces.

Loop Data Context

Inside a loop body, template references have additional context:

Data Loop

{{input["Loop Node Label"]["data"]}}           -- current iteration item
{{input["Loop Node Label"]["parent"]}}         -- parent data (if includeParent)

File Loop

{{input["Loop Node Label"]["__loop_file_name__"]}}   -- current file name
{{input["Loop Node Label"]["__loop_file_link__"]}}   -- current file URL
{{input["Loop Node Label"]["__loop_file_size__"]}}   -- current file size

Sequential Loop Iteration History

In sequential loops, prior iterations' results are available:

{{input["Loop Label_iter_0"]["field"]}}    -- iteration 0 output
{{input["Loop Label_iter_1"]["field"]}}    -- iteration 1 output

This enables sequential accumulation patterns where each iteration builds on the previous.


File Reference Patterns

Files are referenced in node configs (particularly output and integration nodes) using these patterns:

All Files from a Node

input["files"]["Node Label"]

Specific File by Index

input["files"]["Node Label"][0]     -- first file
input["files"]["Node Label"][1]     -- second file

File Properties

Each file object has:

PropertyDescription
nameOriginal filename
urlStorage URL
sizeSize in bytes

In Output Node Config

Gmail and integration nodes accept fileReferences as an array of reference strings:

JSON
{
  "fileReferences": [
    "input[\"files\"][\"Email Input\"]",
    "input[\"files\"][\"OCR Node\"][0]"
  ]
}

Duplicate Filename Handling

When multiple files have the same name, behavior is controlled by duplicateFilenameBehavior:

ValueBehavior
auto_suffixAppend _1, _2, etc. to duplicates
errorFail if duplicates exist
last_winsKeep only the last occurrence

Out of Bounds Handling

When a file index doesn't exist, behavior is controlled by outOfBoundsBehavior:

ValueBehavior
errorFail if index is out of bounds
skipSilently skip the missing file

Data Bucket Snapshot

At any point during execution, the full bucket can be inspected. It contains every completed node's output keyed by label:

JSON
{
  "Email Input": {
    "id": "msg_abc123",
    "thread_id": "thread_xyz789",
    "sender": "buyer@example.com",
    "subject": "Steel Order Request",
    "body": "Need 5000 lbs carbon steel..."
  },
  "Extract Order": {
    "Order Request": {
      "customer_name": "John Smith",
      "weight_lbs": 5000,
      "steel_type": "carbon"
    }
  },
  "Calculate Quote": {
    "output": {
      "total_price": 2250.00,
      "price_per_lb": 0.45
    }
  }
}

Files are tracked separately:

JSON
{
  "files": {
    "Email Input": [
      {
        "name": "spec-sheet.pdf",
        "url": "https://storage.example.com/files/...",
        "size": 245760
      }
    ]
  }
}

Common Patterns

Forward a Field

{{input["Upstream Node"]["field"]}}

Build a URL with Dynamic Segments

https://api.example.com/orders/{{input["Node"]["order_id"]}}/status

Conditional Default

{{default(input["Node"]["optional_field"], "fallback_value")}}

Generate a Unique Filename

report-{{uuid()}}-{{now("20060102")}}.pdf

Serialize Data for an API Call

JSON
{
  "payload": "{{toJSON(input[\"Extract Order\"][\"Order Request\"])}}"
}

Access Loop Iteration Data in Loop Body

{{input["Process Orders"]["data"]["order_id"]}}

Reference the Thread ID for Gmail Reply

{{input["Email Input"]["thread_id"]}}