Transform Node

The Transform node rewrites event payloads before delivery. Choose between Handlebars templates, jq expressions, or JavaScript snippets to reshape data, extract fields, compute values, and adapt payloads for any downstream system.

Overview

Transform nodes intercept the event payload and apply a transformation template before the payload reaches its target webhook. This is essential when your source sends data in a format that doesn't match what your consumer expects — for example, converting a Stripe webhook payload into a simplified internal event format.

Three transform modes are supported: handlebars for template-based interpolation, jq for powerful JSON query/transform expressions, and javascript for arbitrary code execution. On the canvas, Transform nodes appear as pink rectangles.


Configuration

FieldTypeDefaultDescription
namestringFriendly label for the transform
transformModestringhandlebars'handlebars', 'jq', or 'javascript'
templatestringThe transformation template or expression
inputNodes{ nodeType, nodeId }[][]Upstream nodes that feed this one. nodeType is the node kind (webhook, scheduledWorkflow, filter, …)
isActivebooleantrueWhether the transform is enabled

Transform modes

ModeDescriptionBest for
handlebarsMustache-style {{payload.field}} interpolation in a JSON templateSimple field mapping and restructuring
jqFull jq expression language for querying and transforming JSONComplex filtering, array operations, computed fields
javascriptJavaScript function body with access to payload variableCustom logic, conditional transforms, string manipulation
JavaScript transforms run in a sandboxed environment with a 5-second timeout. Network access, file I/O, and module imports are not available.

Create a Transform

bashCreate Transform (Handlebars)
curl -X POST /api/transform-nodes \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Stripe to Internal Format",
    "transformMode": "handlebars",
    "template": "{ "event": "{{payload.type}}", "amount": {{payload.data.object.amount}}, "currency": "{{payload.data.object.currency}}", "customerId": "{{payload.data.object.customer}}" }",
    "inputNodes": [{ "nodeType": "webhook", "nodeId": "6654a1b2c3d4e5f6a7b8c9d0" }],
    "isActive": true
  }'

Response

json201 Created
{
  "_id": "6654f6a7b8c9d0e1f2a3b4c5",
  "name": "Stripe to Internal Format",
  "transformMode": "handlebars",
  "template": "{ "event": "{{payload.type}}", "amount": {{payload.data.object.amount}}, "currency": "{{payload.data.object.currency}}", "customerId": "{{payload.data.object.customer}}" }",
  "inputNodes": [{ "nodeType": "webhook", "nodeId": "6654a1b2c3d4e5f6a7b8c9d0" }],
  "isActive": true,
  "ownerId": "org_abc123",
  "createdAt": "2025-05-01T12:00:00.000Z",
  "updatedAt": "2025-05-01T12:00:00.000Z"
}

Update a Transform

bashUpdate Transform to JavaScript
curl -X PATCH /api/transform-nodes/6654f6a7b8c9d0e1f2a3b4c5 \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "transformMode": "javascript",
    "template": "const amount = payload.data.object.amount / 100; return { event: payload.type, amountDollars: amount, currency: payload.data.object.currency.toUpperCase(), processedAt: new Date().toISOString() };"
  }'

Canvas Integration

The Transform uses canvas type transform-node and renders in pink. It sits between a Webhook and its target, intercepting payloads before delivery. Transforms also participate in chain pipelines via bridge transforms.

jsonCanvas node data
{
  "id": "transform-6654f6a7b8c9d0e1f2a3b4c5",
  "type": "transform-node",
  "position": { "x": 350, "y": 200 },
  "data": {
    "label": "Stripe to Internal Format",
    "transformNodeId": "6654f6a7b8c9d0e1f2a3b4c5",
    "transformMode": "handlebars",
    "isActive": true
  }
}

Payload Examples

Handlebars template

Input payload:

jsonInput
{
  "type": "payment_intent.succeeded",
  "data": {
    "object": {
      "amount": 5000,
      "currency": "usd",
      "customer": "cus_abc123"
    }
  }
}

Template:

jsonHandlebars template
{
  "event": "{{payload.type}}",
  "amount": {{payload.data.object.amount}},
  "currency": "{{payload.data.object.currency}}",
  "customerId": "{{payload.data.object.customer}}"
}

Output (delivered to target):

jsonOutput
{
  "event": "payment_intent.succeeded",
  "amount": 5000,
  "currency": "usd",
  "customerId": "cus_abc123"
}

jq expression

bashjq template
{ event: .type, items: [.data.object | { id: .id, total: (.amount / 100) }] }

JavaScript function

javascriptJavaScript template
const obj = payload.data.object;
const amount = obj.amount / 100;

return {
  event: payload.type,
  amountFormatted: `$${amount.toFixed(2)} ${obj.currency.toUpperCase()}`,
  isHighValue: amount > 100,
  customer: obj.customer || 'guest',
  processedAt: new Date().toISOString()
};

Use Cases

  • Format conversion — Convert Stripe's nested payload into a flat structure your API expects.
  • Data enrichment — Add computed fields like formatted amounts, timestamps, or derived statuses.
  • PII redaction — Strip sensitive fields (emails, phone numbers) before forwarding to analytics services.
  • Discord/Slack formatting — Transform raw events into rich embed objects for chat notifications.
  • Chain bridge transforms — Reshape chain payloads between sequential webhook executions to match each webhook's expected input format.

API Reference

MethodWebhookDescription
GET/api/transform-nodesList all transform nodes
POST/api/transform-nodesCreate a new transform node
GET/api/transform-nodes/:idGet a specific transform node
PATCH/api/transform-nodes/:idUpdate a transform node
DELETE/api/transform-nodes/:idDelete a transform node
Start with Handlebars for simple field mapping. Switch to jq or JavaScript when you need array operations, conditionals, or computed values.
If a transform produces invalid JSON, the event is delivered with the original payload and a warning is logged. Check event details in the dashboard to debug transform errors.