---
title: "Add a branch"
method: POST
path: "/v1/workflows/{workflowId}/nodes/{nodeId}/add-branch"
tags: ["Workflow nodes"]
---

# Add a branch

`POST /v1/workflows/{workflowId}/nodes/{nodeId}/add-branch`

Add a branch and a child node under an existing Branch or Experiment node. Returns the created child node plus the latest workflow.

- Adding a branch to a `BranchNode` creates one `AudienceFilter` child node.
- Adding a branch to an `ExperimentBranchNode` creates one `VariantNode` child node.

This endpoint does not accept node configuration fields; update the child node with `POST /v1/workflows/{workflowId}/nodes/{nodeId}` after creation. Public workflows are limited to 400 nodes, and this endpoint adds 1 node.

## Request body

- AddWorkflowBranchRequest
  - `expectedRevisionId` string, nullable, required — The workflow revision token returned by the latest workflow read or mutation. Older workflows may return `null` before their first revision-aware mutation; pass `null` back as `expectedRevisionId` in that case. If the token is stale, the API returns a `409 Conflict` error.

## Response `200`

Branch added.

- AddWorkflowBranchResponse
  - `node` union, required — Detailed workflow node returned from a mutation, plus the latest workflow revision token.
    - SignupTriggerWorkflowMutationNodeWithRevision
      - `id` string, required
      - `typeName` 'SignupTrigger', required
      - `nextNodeIds` string[], required — The IDs of the nodes that are downstream of this node.
      - `workflowRevisionId` string, required — The current workflow revision token. Pass the latest value as `expectedRevisionId` on the next workflow mutation.
    - EventTriggerWorkflowMutationNodeWithRevision
      - `id` string, required
      - `typeName` 'EventTrigger', required
      - `nextNodeIds` string[], required — The IDs of the nodes that are downstream of this node.
      - `eventName` string — The name of the event pattern that triggers this node.
      - `eventProperties` WorkflowEventProperty[] — The properties of the event pattern, which can be used in emails.
        - `name` string, required
        - `type` 'string' | 'number' | 'boolean' | 'date', required
      - `reEligible` boolean, required — If `true`, the contacts will be able to enter this workflow every time the trigger is matched. If `false`, contacts will only ever enter this workflow once. Matches the "Trigger frequency" option in the UI.
      - `workflowRevisionId` string, required — The current workflow revision token. Pass the latest value as `expectedRevisionId` on the next workflow mutation.
    - ContactPropertyTriggerWorkflowMutationNodeWithRevision
      - `id` string, required
      - `typeName` 'ContactPropertyTrigger', required
      - `nextNodeIds` string[], required — The IDs of the nodes that are downstream of this node.
      - `contactPropertyQuery` WorkflowContactPropertyQuery, required — Define the contact property change that triggers the workflow. In update requests, `key` must resolve to an existing contact property that is available for Contact Updated triggers. Hidden or unsupported fields, such as `createdAt`, `notes`, and computed contact properties, are rejected.
        - `key` string, required — The camel-cased `key` of the contact property to query. The property must exist for the team and must be available for Contact Updated triggers.
        - `is` WorkflowContactPropertyComparison, required — For Contact Updated triggers, the API validates `operator` against the selected contact property's type and the side of the comparison. The `was` comparison can use any operator supported by the selected property type. The `is` comparison uses the same operators, except number and boolean properties cannot use `empty`. String properties support `any`, `equal`, `not_equal`, `contains`, `not_contains`, `empty`, and `not_empty`. Number properties support `any`, `greater_than`, `less_than`, `numeric_equal`, `numeric_not_equal`, `empty`, and `not_empty`. Boolean properties support `any`, `true`, `false`, `empty`, and `not_empty`. Date properties support `any`, `empty`, `not_empty`, `after`, `before`, and `between`.
          - `value` union, required
            - string
            - number
            - boolean
          - `operator` 'any' | 'contains' | 'not_contains' | 'empty' | 'not_empty' | 'equal' | 'not_equal' | 'greater_than' | 'less_than' | 'true' | 'false' | 'numeric_equal' | 'numeric_not_equal' | 'after' | 'before' | 'between', required — The comparison operator. It must be valid for the selected contact property's type and for the `is` or `was` side of the comparison. Number and boolean properties allow `empty` on `was`, but not on `is`.
        - `was` WorkflowContactPropertyComparison, required — For Contact Updated triggers, the API validates `operator` against the selected contact property's type and the side of the comparison. The `was` comparison can use any operator supported by the selected property type. The `is` comparison uses the same operators, except number and boolean properties cannot use `empty`. String properties support `any`, `equal`, `not_equal`, `contains`, `not_contains`, `empty`, and `not_empty`. Number properties support `any`, `greater_than`, `less_than`, `numeric_equal`, `numeric_not_equal`, `empty`, and `not_empty`. Boolean properties support `any`, `true`, `false`, `empty`, and `not_empty`. Date properties support `any`, `empty`, `not_empty`, `after`, `before`, and `between`.
          - `value` union, required
            - string
            - number
            - boolean
          - `operator` 'any' | 'contains' | 'not_contains' | 'empty' | 'not_empty' | 'equal' | 'not_equal' | 'greater_than' | 'less_than' | 'true' | 'false' | 'numeric_equal' | 'numeric_not_equal' | 'after' | 'before' | 'between', required — The comparison operator. It must be valid for the selected contact property's type and for the `is` or `was` side of the comparison. Number and boolean properties allow `empty` on `was`, but not on `is`.
      - `reEligible` boolean, required — If `true`, the contacts will be able to enter this workflow every time the trigger is matched. If `false`, contacts will only ever enter this workflow once. Matches the "Trigger frequency" option in the UI.
      - `workflowRevisionId` string, required — The current workflow revision token. Pass the latest value as `expectedRevisionId` on the next workflow mutation.
    - AddToListTriggerWorkflowMutationNodeWithRevision
      - `id` string, required
      - `typeName` 'AddToListTrigger', required
      - `nextNodeIds` string[], required — The IDs of the nodes that are downstream of this node.
      - `mailingListId` string, nullable, required — The ID of the mailing list this trigger sends to, if set.
      - `reEligible` boolean, required — If `true`, the contacts will be able to enter this workflow every time the trigger is matched. If `false`, contacts will only ever enter this workflow once. Matches the "Trigger frequency" option in the UI.
      - `workflowRevisionId` string, required — The current workflow revision token. Pass the latest value as `expectedRevisionId` on the next workflow mutation.
    - BlankTriggerWorkflowMutationNodeWithRevision
      - `id` string, required
      - `typeName` 'BlankTrigger', required
      - `nextNodeIds` string[], required — The IDs of the nodes that are downstream of this node.
      - `workflowRevisionId` string, required — The current workflow revision token. Pass the latest value as `expectedRevisionId` on the next workflow mutation.
    - AudienceFilterWorkflowMutationNodeWithRevision
      - `id` string, required
      - `typeName` 'AudienceFilter', required
      - `nextNodeIds` string[], required — The IDs of the nodes that are downstream of this node.
      - `audienceFilter` AudienceFilter, nullable — A tree of audience conditions combined with `match`.
        - `match` 'all' | 'any', required
        - `conditions` AudienceFilterCondition[], required
          - union
            - PropertyCondition — Matches contacts by a property value.
              - …
            - OptInCondition — Matches contacts by mailing-list opt-in status.
              - …
            - ActivityCondition — Matches contacts by their activity on a campaign or workflow.
              - …
      - `audienceSegmentId` string — The ID of the audience segment this trigger targets.
      - `appliesDownstream` boolean, required — If `true`, the audience filter will apply to all downstream nodes. If `false`, the audience filter will only apply to the current node. Matches the "Filter scope" option in the UI.
      - `workflowRevisionId` string, required — The current workflow revision token. Pass the latest value as `expectedRevisionId` on the next workflow mutation.
    - TimerActionWorkflowMutationNodeWithRevision
      - `id` string, required
      - `typeName` 'TimerAction', required
      - `nextNodeIds` string[], required — The IDs of the nodes that are downstream of this node.
      - `amount` number, required — The amount of time to wait before triggering the next node. Set to `0` to move to the next node immediately.
      - `unit` 'm' | 'h' | 'd', required — The unit of time for the timer action node. m = minutes, h = hours, d = days.
      - `workflowRevisionId` string, required — The current workflow revision token. Pass the latest value as `expectedRevisionId` on the next workflow mutation.
    - SendEmailActionWorkflowMutationNodeWithRevision
      - `id` string, required
      - `typeName` 'SendEmailAction', required
      - `nextNodeIds` string[], required — The IDs of the nodes that are downstream of this node.
      - `emailMessageId` string, required — The ID of the email message to send. To edit this email, use the `POST /v1/email-messages/{emailMessageId}` endpoint.
      - `subject` string, required
      - `workflowRevisionId` string, required — The current workflow revision token. Pass the latest value as `expectedRevisionId` on the next workflow mutation.
    - ExitActionWorkflowMutationNodeWithRevision
      - `id` string, required
      - `typeName` 'ExitAction', required
      - `nextNodeIds` string[], required — The IDs of the nodes that are downstream of this node.
      - `workflowRevisionId` string, required — The current workflow revision token. Pass the latest value as `expectedRevisionId` on the next workflow mutation.
    - BranchWorkflowMutationNodeWithRevision
      - `id` string, required
      - `typeName` 'BranchNode', required
      - `nextNodeIds` string[], required — The IDs of the nodes that are downstream of this node.
      - `workflowRevisionId` string, required — The current workflow revision token. Pass the latest value as `expectedRevisionId` on the next workflow mutation.
    - ExperimentBranchWorkflowMutationNodeWithRevision
      - `id` string, required
      - `typeName` 'ExperimentBranchNode', required
      - `nextNodeIds` string[], required — The IDs of the nodes that are downstream of this node.
      - `samplingRate` number, required — The percentage of contacts that will be sent to variant branches, between `0` and `100`. The remaining percentage will be sent to the control branch. `100` sends all contacts to variant branches.
      - `workflowRevisionId` string, required — The current workflow revision token. Pass the latest value as `expectedRevisionId` on the next workflow mutation.
    - VariantWorkflowMutationNodeWithRevision
      - `id` string, required
      - `typeName` 'VariantNode', required
      - `nextNodeIds` string[], required — The IDs of the nodes that are downstream of this node.
      - `isControl` boolean — Whether this is the control variant of an experiment.
      - `workflowRevisionId` string, required — The current workflow revision token. Pass the latest value as `expectedRevisionId` on the next workflow mutation.
  - `workflow` SimplifiedWorkflow, required
    - `id` string, required — The ID of the workflow.
    - `url` string, uri, required — The URL of the workflow in the Loops app.
    - `workflowRevisionId` string, nullable, required — The current workflow revision token. Pass the latest value as `expectedRevisionId` on the next workflow mutation. Will be `null` for workflows without a revision token yet.
    - `status` 'Draft' | 'Sending' | 'Paused' | 'PausedAndQueueing', required
    - `name` string — The name of the workflow.
    - `description` string — The description of the workflow.
    - `mailingListId` string, nullable, required — The ID of the mailing list the workflow sends to.
    - `rootNodeId` string, required — The ID of the root node in the workflow graph.
    - `nodes` object, required — A map of node IDs to simplified node objects. Each node includes `typeName` and `nextNodeIds`, plus type-specific fields when present. To get the full node object, use the `GET /v1/workflows/{workflowId}/nodes/{nodeId}` endpoint.

## Other responses

- `400` — Invalid `workflowId`, `nodeId`, request body, branch target, workflow state, or workflow node cap.
- `401` — Invalid API key.
- `404` — Workflow or workflow node not found.
- `405` — Wrong HTTP request method.
- `409` — `expectedRevisionId` is stale.

## Changes

> 9 revisions in range; 1 not diffed.

- **2026-07-26** `72d136d738c3` — 1 info
  - added `SignupTrigger, EventTrigger, ContactPropertyTrigger, AddToListTrigger, BlankTrigger, AudienceFilter, TimerAction, SendEmailAction, ExitAction, BranchNode, ExperimentBranchNode, VariantNode` discriminator mapping keys to the `node` response property for the response status `200`
- **2026-07-21** `df77516a130a` — 1 info
  - endpoint added

[Change history](https://skmtc.dev/loops/apis/loops-openapi-spec/changes/v1/workflows/:workflowId/nodes/:nodeId/add-branch/post.md)

---

[API](https://skmtc.dev/loops/apis/loops-openapi-spec.md) · [All operations](https://skmtc.dev/loops/apis/loops-openapi-spec/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/loops/loops-openapi-spec/revisions/8bd0098300ec/schema)
