Conditional Node

The Conditional node provides multi-branch routing logic for your webhook pipeline. Define named branches with filters, and events are routed to the first matching branch — with an else path for events that match no branch.

Overview

The Conditional node evaluates named branches in order against the incoming event payload. Each branch has its own set of filters and output targets. The first branch whose filters match wins — the event is routed to that branch's output nodes. If no branch matches, the event falls through to the else path.

ℹBranches are evaluated in order — first match wins. If no branch matches, the event is routed through the else path (elseWebhookIds / elseTransformIds).

Configuration

FieldTypeDefaultDescription
namestring—Friendly label for the conditional
branchesBranch[]—Array of named branches evaluated in order (first match wins)
elseWebhookIdsstring[][]Webhook IDs for the else path (when no branch matches)
elseTransformIdsstring[][]Transform IDs for the else path
inputNodes{ nodeType, nodeId }[][]Upstream nodes that feed this one. nodeType is the node kind (webhook, scheduledWorkflow, filter, …)
loopBackDelaySecnumber0Seconds to wait before looping back — only when the taken branch points to a node that already ran in this run (0–300). See Looping back.
isActivebooleantrueWhether the conditional is enabled

Branch Object

FieldTypeDescription
namestringHuman-readable label for this branch (e.g. "High value")
filtersPayloadFilter[]Array of filter conditions to evaluate against the payload
filterMode'and' | 'or'How to combine filters — all must match (and) or any (or)
outputNodesstring[]Node IDs to route to when this branch matches
outputWebhookIdsstring[]Webhook IDs to route to when this branch matches
outputTransformIdsstring[]Transform IDs to route to when this branch matches

Create a Conditional

Define branches with filters and output targets. The first branch whose filters match wins. Use elseWebhookIds and elseTransformIds for events that match no branch.

bashCreate Conditional with branches
curl -X POST /api/conditionals \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "workspaceId": "<your-workspace-id>",
    "name": "Payment Amount Router",
    "branches": [
      {
        "name": "High value",
        "filters": [
          { "field": "data.object.amount", "operator": "gt", "value": "5000" }
        ],
        "filterMode": "and",
        "outputNodes": ["delay_6655aaa", "discord_6655bbb"],
        "outputWebhookIds": [],
        "outputTransformIds": []
      },
      {
        "name": "Standard",
        "filters": [
          { "field": "data.object.amount", "operator": "lte", "value": "5000" }
        ],
        "filterMode": "and",
        "outputNodes": [],
        "outputWebhookIds": ["ep_standard_processing"],
        "outputTransformIds": []
      }
    ],
    "elseWebhookIds": ["ep_fallback"],
    "elseTransformIds": [],
    "inputNodes": [{ "nodeType": "webhook", "nodeId": "6654a1b2c3d4e5f6a7b8c9d0" }],
    "isActive": true
  }'

Response

json201 Created
{
  "_id": "6655b8c9d0e1f2a3b4c5d6e7",
  "name": "Payment Amount Router",
  "branches": [
    {
      "name": "High value",
      "filters": [
        { "field": "data.object.amount", "operator": "gt", "value": "5000" }
      ],
      "filterMode": "and",
      "outputNodes": ["delay_6655aaa", "discord_6655bbb"],
      "outputWebhookIds": [],
      "outputTransformIds": []
    },
    {
      "name": "Standard",
      "filters": [
        { "field": "data.object.amount", "operator": "lte", "value": "5000" }
      ],
      "filterMode": "and",
      "outputNodes": [],
      "outputWebhookIds": ["ep_standard_processing"],
      "outputTransformIds": []
    }
  ],
  "elseWebhookIds": ["ep_fallback"],
  "elseTransformIds": [],
  "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 Conditional

bashUpdate Conditional
curl -X PATCH /api/conditionals/6655b8c9d0e1f2a3b4c5d6e7 \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "branches": [
      {
        "name": "VIP",
        "filters": [
          { "field": "data.object.amount", "operator": "gt", "value": "50000" }
        ],
        "filterMode": "and",
        "outputNodes": ["delay_6655aaa", "discord_6655bbb"],
        "outputWebhookIds": [],
        "outputTransformIds": []
      },
      {
        "name": "Standard",
        "filters": [
          { "field": "data.object.amount", "operator": "lte", "value": "50000" }
        ],
        "filterMode": "and",
        "outputNodes": [],
        "outputWebhookIds": ["ep_standard_processing"],
        "outputTransformIds": []
      }
    ],
    "elseWebhookIds": ["ep_fallback"]
  }'

Looping back (polling)

A branch — or the else path — can point to a node that already ran earlier in the flow. When it does, the flow loops: that node runs again, its output comes back to the Conditional, and the branches are evaluated again. This is how you poll an external job until it finishes:

textPolling an external job
Start job  →  Check status  →  Loop Until Complete (Conditional)
                    ▲                    │ branch "Done": status is SUCCEEDED → Notify
                    └──── else ──────────┘

Every pass of a node inside the loop shows up as its own step, in the Run Pipeline modal and in the run history, labeled iteration 2 · back from Loop Until Complete, iteration 3 · … and so on. There is no total — how many passes it takes depends on the external service.

Waiting between passes

A loop back runs immediately by default. A status check takes milliseconds, so without a wait the loop would spin through its whole budget in a couple of seconds while the job is still running. Two ways to wait, and they combine:

  • Wait before looping back (loopBackDelaySec) on the Conditional itself: how many seconds to wait before sending the flow back. It only applies when the taken branch points to a node that already ran — branches that continue forward never wait. Up to 5 minutes.
  • A Delay node inside the loop (else → Delay → Check status). Use it for waits longer than 5 minutes: a Delay is queued, so it survives a restart of the engine.

The cycle budget

A step can run at most 10 times in one event on Free, and 100 times on Starter, Pro and Enterprise. It is set by your plan and you can't change it; Settings → Workspaces shows yours. When a loop reaches it, that branch stops there and the run says why: the step shows up in Run History as "Stopped: a step ran 100 times in one event (Pro limit)", and in Run Pipeline the node turns red with the same message. The account owner also gets an email, at most once a day for each flow.

If you hit it, add a wait (see above) or check the branch rules — the loop only ends when a branch that continues forward matches.

ℹRun Pipeline waits for real, but caps each wait at 60 seconds — both the Conditional's wait and a Delay node — so you can watch the loop without waiting an hour. Production honors the full value. A Delay set to a fixed date (until) is not waited for in Run Pipeline at all.

Canvas Integration

On the canvas, the Conditional node renders with one outgoing edge per branch (labeled with the branch name) and an additional dashed edge labeled "else" for the fallback path. The canvas type is conditional with blue styling.

jsonCanvas representation
{
  "id": "conditional-6655b8c9d0e1f2a3b4c5d6e7",
  "type": "conditional",
  "position": { "x": 400, "y": 200 },
  "data": {
    "label": "Payment Amount Router",
    "conditionalId": "6655b8c9d0e1f2a3b4c5d6e7",
    "branchCount": 2,
    "hasElse": true,
    "isActive": true
  }
}

Payload Examples

Branch 1 matches — high value

jsonRouted to 'High value' branch
{
  "type": "payment_intent.succeeded",
  "data": {
    "object": {
      "amount": 25000,
      "currency": "usd",
      "customer": "cus_vip123"
    }
  }
}
// amount (25000) > 5000 -> matches "High value" branch
// -> routed to delay_6655aaa -> discord_6655bbb

Branch 2 matches — standard

jsonRouted to 'Standard' branch
{
  "type": "payment_intent.succeeded",
  "data": {
    "object": {
      "amount": 3500,
      "currency": "usd",
      "customer": "cus_regular456"
    }
  }
}
// amount (3500) <= 5000 -> matches "Standard" branch
// -> routed to ep_standard_processing

No branch matches — else path

jsonRouted to else path
{
  "type": "payment_intent.failed",
  "data": {
    "object": {
      "error": "card_declined"
    }
  }
}
// no "amount" field -> no branch matches
// -> routed to elseWebhookIds (ep_fallback)

Use Cases

  • Amount thresholds — Route high-value transactions to a VIP processing pipeline and standard ones to the regular flow.
  • Feature gating — Check for a feature flag field in the payload and route to different processing versions.
  • Error handling — Check if the payload contains an error field; route errors to an error handler and successes to the main pipeline.
  • A/B testing — Split events based on a user segment field to test different processing logic.
  • Region-based routing — Route events to US or EU processing webhooks based on a region field.

API Reference

The Conditional node has its own API endpoints:

MethodWebhookDescription
GET/api/conditionalsList all conditionals
POST/api/conditionalsCreate a new conditional
GET/api/conditionals/:idGet a specific conditional
PATCH/api/conditionals/:idUpdate a conditional
DELETE/api/conditionals/:idDelete a conditional
✦Use filterMode: "and" when all conditions in a branch must be true, or "or" when any single condition is enough to match.
ℹBranches support outputNodes for connecting to any node type (delays, Discord actions, etc.) in addition to outputWebhookIds and outputTransformIds for direct webhook/transform routing.