Skip to main content
Every parameter slot (WhatsApp template {{n}}, HTTP/Task placeholders, Fireberry fields, conditions) is one of three JSON shapes. The board picker has the same three tabs: Fixed, Expression, Custom. Runtime discriminators are isCustomValue, isExpression, and isVariable in @pingmee/aws-sharedmodel (automations.ts). Check Custom first: a Custom object also carries id / name / parentId / valueType, so isVariable would match it. workflow.variables is a top-level map and is usually {}.

Three ways to bind a parameter

Fixed — field from another node (isVariable)

The value is an input of an upstream node. In the GUI, expand a node row (label plus a # badge of the last four characters of the node id, for example Workflow Trigger #a5a8) and pick a leaf from the tree. JSON is a Variable. parentId is the upstream node id that produced the value. name is the dotted path into that node’s output. Downstream nodes copy that object. They do not invent a new parent.

Expression — JavaScript over node outputs (isExpression)

The value is computed JS. Discriminator is "expression". Runtime wraps the string in a function, so the body must return. Node outputs are referenced with $ plus the full node id:
In the GUI, type $ to autocomplete available parameters. Typing more characters filters the list. Suggestion labels show the last four characters of the node id; the inserted text is the full $nodeId.path. parentId / name on the Expression object are unused at runtime (resolution reads $ids in the body). The GUI often leaves them empty. Filling them in for readability is fine.
If you copy a node into a different workflow, every $nodeId in Expression bodies must be rewritten to ids that exist in the destination (usually the new trigger node’s id). Stale $oldId.triggerData… does not resolve.
triggerData is the usual Expression example: keys from POST /trigger are not in the board catalog, so you cannot pick them as Fixed.

Custom — static text (isCustomValue)

A literal string. Discriminator is "custom". The GUI is a free-text field (for example www.pingmee.co.il). Runtime returns custom as-is.

Compact examples

Same three objects work in a WhatsApp bodyVariables slot, an HTTP bodyVariables / titleVariables slot, and a Fireberry objectVariable or queries[].value. Fixed — catalog field from the trigger:
ExpressiontriggerData key (HTTP body / Fireberry field: same object):
Custom — literal (Fireberry objectVariable shown; a WhatsApp slot is the same object under "1"):

Output catalog — node.data.variables

Triggers ship a tree generated from empty customer / message / trigger objects. Nested objects use valueType: "Object" and a nested variables map. That tree is what Fixed picks from. Typical trigger names (not an exhaustive dump):
  • customer.customerName, customer.phoneNumber, customer.email, customer.customerNickname
  • message (object) plus nested message fields
  • trigger.platform, trigger.eventType
WhatsApp (and other message) nodes add a self output { name: "#<last-4-of-node-id>", parentId: <this node> } and one entry per answer button (name = button label, parentId = message node). AI summarizeConversation exposes { name: "summary", parentId: <ai node id>, valueType: "string" }.

Template slots

On message nodes, numbered maps bind WhatsApp template placeholders: Each value is Fixed, Expression, or Custom.

HTTP and Task {{n}}

httpRequest.data.body and task.task.title / task.task.body use the same {{1}} syntax. Bindings live in bodyVariables and titleVariables (numeric keys). Each value is the same three shapes.

Conditions

If / Switch case firstValue is typically Fixed. secondValue is often a raw string, not a parameter object.
Is Empty / Is Not Empty omit a useful secondValue. Contains uses [{ "label": "hello" }].

triggerData

triggerData is the JSON object you send when you start a run. The engine copies it onto the trigger node’s output. Downstream fields (WhatsApp template slots, HTTP/Task {{n}}, conditions) read it with an Expression:
That $… id is the trigger node’s id, not the workflow id. Real node ids are often hex strings:

How it maps

  1. POST /workflows/{workflowId}/trigger with "triggerData": { "websiteLink": "https://example.com" }.
  2. The run starts at the workflow’s trigger (pingmeeTrigger, workflowTrigger, instagramTrigger, or facebookTrigger).
  3. That node’s execution output becomes { message, customer, triggerData }.
  4. A WhatsApp or HTTP binding with expression: "return $trig_1.triggerData.websiteLink;" resolves to "https://example.com".
Keys are not declared on the trigger node. They are whatever you put on the incoming object. Nested objects work with more dots ($trig_1.triggerData.order.id). The board catalog (node.data.variables) is generated from empty customer / message / trigger shapes and does not include triggerData — that is why you use Expression instead of Fixed. You can also bind a Fixed Variable whose name is triggerData.websiteLink and parentId is the trigger node. Same output path; no expression field. Prefer the Expression form when you author graphs by API.

vs catalog Variables and bodyVariables

bodyVariables is only the slot map ("1", "2", …). It does not invent payload keys.

Constraints

  • JSON object (not an array or string). Omit the field if you have no payload.
  • At most 32 768 bytes serialized, nesting depth at most 10.
  • The trigger node must actually run. startWithNodeId that skips the trigger does not seed output.triggerData.
  • A workflow pointer does not forward triggerData into the child. POST /trigger on the workflow you want the payload on (often a workflowTrigger child).
  • Copying the Expression into another workflow requires a new $nodeId. See Hard rules.
See a full dual-graph snippet in Examples.
Last modified on September 4, 2026