How AI Workflow Nodes Communicate: Designing Universal Output Envelopes
Getting multi-tool AI nodes to talk to each other using Universal Data Envelopes and a centralized Node Contract Registry.

The hardest problem in building a multi-tool AI workflow engine isn't calling the tools — it's getting them to talk to each other.
Consider three nodes on a typical TenseAI canvas:
Now connect any of these to a Gmail node. How does Gmail know where to find the recipient's email address? Under the hood, every tool has its own idea of what "output" looks like, and without a shared contract, every new pairing of tools requires its own custom adapter. Multiply that across 20+ integrations and you get an explosion of fragile, one-off mapping code — the kind that breaks the moment a third-party API changes a field name.
TenseAI solves this with Universal Data Envelopes, governed by a centralized Node Contract Registry.
The Universal Envelope Architecture
Every tool node in TenseAI — regardless of vendor or function — packages its output into a standardized NodeOutputEnvelope, defined in envelope_validators.py:
{
"text": str, # Primary text / summary
"items": list, # Generic record dictionaries
"contacts": list, # Validated contact objects (email, name, phone)
"rows": list, # Tabular 2D arrays
"files": list, # File objects (base64, downloadUrl)
"links": list, # Extracted URLs
"events": list, # Calendar event objects
"emails": list, # Extracted email strings
"threads": list, # Thread identifiers
"ids": dict, # Resource identifiers (spreadsheetId, messageId)
"meta": dict # Provenance metadata (sourceTool, confidence)
}No matter which tool produced the data, downstream nodes only ever need to understand this one shape. A Gmail node doesn't need special-case logic for "Apollo output" versus "CRM output" — it just reads envelope["contacts"].
Automated Kind Aliasing
The interesting engineering problem is getting existing tool outputs into this shape without rewriting every integration by hand. That's the job of the Node Contract Registry (node_contract_registry.py), which defines a mapping table called KIND_ALIASES. When a tool returns a custom key, TenseAI automatically coerces it to the matching canonical field:
This means a legacy integration returning leadCandidates doesn't need to be rewritten — the registry quietly normalizes it into contacts on the way through, and every downstream node sees the same canonical shape regardless of which upstream tool produced it.
Enforcing Input Contracts, Not Just Output Shapes
Normalizing output is only half the problem — the registry also enforces what each action is allowed to consume. ACTION_INPUT_KIND_OVERRIDES maps a (tool, action) pair to the set of envelope fields it actually needs:
ACTION_INPUT_KIND_OVERRIDES: dict[tuple[str, str], set[str]] = {
("crm", "importContacts"): {"contacts"},
("gmail", "sendEmail"): {"contacts", "text", "files", "links", "events"},
("sheets", "appendValues"): {"rows", "items", "contacts", "links"},
}This gives the engine a validation layer for free: before a step executes, TenseAI can check whether the upstream envelope actually contains the fields the downstream action requires, and surface a clear error if it doesn't — instead of letting a KeyError blow up mid-campaign.
The Payoff
Downstream executors like Gmail simply request envelope["contacts"] or envelope["files"], confident that upstream data has already been validated, coerced, and sanitized. There's no custom adapter code between any pair of tools, and adding a 21st integration doesn't require touching the other 20.
The result is total composability: any node in TenseAI can connect to any other node on the canvas, and the data will arrive in a shape the receiving node already understands.
Next up: how this same envelope system supports fan-in — merging data from multiple parallel branches into a single downstream node.
Start Automating with TenseAI
Build production-grade, multi-tool workflows with Kahn's DAG compilation and Universal Output Envelopes.