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
| Field | Type | Default | Description |
|---|---|---|---|
| name | string | — | Friendly label for the transform |
| transformMode | string | handlebars | 'handlebars', 'jq', or 'javascript' |
| template | string | — | The transformation template or expression |
| inputNodes | { nodeType, nodeId }[] | [] | Upstream nodes that feed this one. nodeType is the node kind (webhook, scheduledWorkflow, filter, …) |
| isActive | boolean | true | Whether the transform is enabled |
Transform modes
| Mode | Description | Best for |
|---|---|---|
| handlebars | Mustache-style {{payload.field}} interpolation in a JSON template | Simple field mapping and restructuring |
| jq | Full jq expression language for querying and transforming JSON | Complex filtering, array operations, computed fields |
| javascript | JavaScript function body with access to payload variable | Custom logic, conditional transforms, string manipulation |
Create a Transform
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
{
"_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
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.
{
"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:
{
"type": "payment_intent.succeeded",
"data": {
"object": {
"amount": 5000,
"currency": "usd",
"customer": "cus_abc123"
}
}
}Template:
{
"event": "{{payload.type}}",
"amount": {{payload.data.object.amount}},
"currency": "{{payload.data.object.currency}}",
"customerId": "{{payload.data.object.customer}}"
}Output (delivered to target):
{
"event": "payment_intent.succeeded",
"amount": 5000,
"currency": "usd",
"customerId": "cus_abc123"
}jq expression
{ event: .type, items: [.data.object | { id: .id, total: (.amount / 100) }] }JavaScript function
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
| Method | Webhook | Description |
|---|---|---|
| GET | /api/transform-nodes | List all transform nodes |
| POST | /api/transform-nodes | Create a new transform node |
| GET | /api/transform-nodes/:id | Get a specific transform node |
| PATCH | /api/transform-nodes/:id | Update a transform node |
| DELETE | /api/transform-nodes/:id | Delete a transform node |