> ## Documentation Index
> Fetch the complete documentation index at: https://moengage.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Flows Overview

> List, read, and control MoEngage Flows.

Use the Flows endpoints to discover flows, view a single flow (including its versions and stages), and change a flow's status. These endpoints read and control flows; they do not create them.

<Note>
  The Flows endpoints are in early access. MoEngage enhances these endpoints based on feedback during this period, but does not make breaking changes.
</Note>

## Endpoints

The Flows API is a collection of the following endpoints:

* [Search Flows](/docs/api/flows/search-flows): Lists flows with filters and keyset pagination.
* [Get a Single Flow](/docs/api/flows/get-a-single-flow): Returns a single flow, and optionally a specific version with `version_no`.
* [Get a Specific Version of a Flow](/docs/api/flows/get-a-specific-version-of-a-flow): Returns a flow as it was in a specific version, addressed by its version ID.
* [Update Flow Status](/docs/api/flows/update-flow-status): Pauses, resumes, stops, retires, archives, or unarchives a flow.

## Authentication

Authentication is done via Basic Auth. This requires a Base64-encoded string of your credentials in the format `username:password`.

* **Username**: Use your MoEngage Workspace ID (also known as the App ID). Find it in the MoEngage dashboard at **Settings** > **Account** > **API keys**.
* **Password**: Use an API key from **Settings** > **Account** > **API keys**.

Refer to [API Key Dashboard](/docs/user-guide/settings/account/api-and-api-keys/api-key-dashboard) for details on creating and managing API keys.

<Note>
  When creating an API key for this API, ensure the **Campaigns** checkbox is selected under **Select APIs for access** (Flows reuse the Campaigns permissions). The key permissions you select determine your access level:

  * **View** for read endpoints (Search Flows, Get a Single Flow, Get a Specific Version of a Flow)
  * **Create & Manage** to update flow status
</Note>

The workspace is resolved from the Basic Auth credentials in the `Authorization` header. You can also send an optional `X-MOE-Request-Id`.

Requests go to your data center's host, `https://api-{dc}.moengage.com`. For the full list of data centers and their identifiers, refer to [Data Centers](/docs/api/introduction#data-centers).

## Conventions

* IDs are raw 24-character ObjectIds. `flow_id` addresses the flow; `version_id` addresses one version.
* Every successful response uses the envelope `{response_id, type, data}`.
* Datetimes are ISO 8601 UTC (`YYYY-MM-DDThh:mm:ssZ`).
* Search results are paginated. Each response includes a `next_cursor`. To get the next page, send that value as the `cursor` parameter in your next request.

## Flow Structure and Nodes

**Get a single flow** and **Get a specific version of a flow** return the flow's stages in `data.structure.nodes[]`. Each entry is a node (one stage of the flow). Nodes share a common envelope and carry a `config` object whose shape depends on the node's `type` and `sub_type`.

Every node includes these fields:

| Field             | Description                                                                       |
| ----------------- | --------------------------------------------------------------------------------- |
| `stage_id`        | Unique ID of the node within the flow.                                            |
| `type`            | Node category: `TRIGGER`, `ACTION`, `CONDITION`, `SPLIT`, `BRANCH`, or `CONTROL`. |
| `sub_type`        | Specific variant within the `type`.                                               |
| `parent_stage_id` | ID of the upstream node, or `null` for the entry (`TRIGGER`) node.                |
| `child_stage_ids` | IDs of the immediate downstream nodes.                                            |
| `label`           | Human-readable label from the flow builder. May be `null`.                        |
| `config`          | Type-specific configuration. Null-valued fields are omitted.                      |

The sections below describe the `config` shape for each node type. Field sets are documented from real flows; a node may include additional keys not shown here.

### Condition Nodes

A `CONDITION` node evaluates a condition and routes users down one of two paths. `child_stage_ids` always has exactly two entries: index `0` is the path taken when the condition matches, and index `1` is the path when it does not.

`config` fields:

* `condition` — the rule to evaluate. It reuses the same filter grammar as a trigger's event filter (`filter_operator`, `filters[]`, and per-filter `action_name`, `executed`, `execution.type`/`execution.count`, and nested `attributes`).
* `wait` — how long to wait before evaluating, as `{ duration, unit }`. `unit` is `mins`, `hours`, or `days`.
* `evaluation_timing` — when the condition is evaluated: `ON_ENTRY` or `SINCE_PREVIOUS_STAGE`.

Observed `sub_type` values: `HAS_DONE_EVENT`, `ON_EMAIL_CLICK`, `ON_EMAIL_OPEN`.

```json Two-way CONDITION node theme={null}
{
  "stage_id": "Y8nSUSRe6",
  "type": "CONDITION",
  "sub_type": "HAS_DONE_EVENT",
  "parent_stage_id": "ZL4rtyzrd",
  "child_stage_ids": ["2iuY5k_zy", "TOGBwROjc7"],
  "label": "Email Clicked",
  "config": {
    "condition": {
      "filter_operator": "and",
      "filters": [
        {
          "action_name": "MOE_EMAIL_CLICK",
          "executed": true,
          "filter_type": "actions",
          "execution": { "count": 1, "type": "atleast" },
          "attributes": { "filter_operator": "and", "filters": [] }
        }
      ]
    },
    "wait": { "duration": 5, "unit": "mins" },
    "evaluation_timing": "SINCE_PREVIOUS_STAGE"
  }
}
```

### Branch Nodes

A `BRANCH` node represents a single arm of a parent `CONDITION` or `SPLIT`. Its `sub_type` mirrors the parent's `sub_type`, and its single child is the next real stage on that path. Its `config` depends on the parent:

* Arm of a `CONDITION` (explicit YES/NO pattern): `config` is `{ "outcome": "YES" }` or `{ "outcome": "NO" }`.
* Arm of a `CONDITIONAL_SPLIT`: `config` carries `branch_index` (0-based) and, for a conditioned arm, `branch_type: "main"` with a `condition` object. The catch-all arm uses `branch_type: "default"` and has no `condition`.
* Arm of an `AB_SPLIT` or `IPO`: `config` carries `name` and `percentage`.

```json BRANCH arms of a CONDITIONAL_SPLIT theme={null}
{
  "stage_id": "KFs3PrkZJ",
  "type": "BRANCH",
  "sub_type": "CONDITIONAL_SPLIT",
  "parent_stage_id": "f3OFPd5GNe",
  "child_stage_ids": ["dJ0FjUCcC"],
  "label": "Signed up and Purchased",
  "config": {
    "branch_type": "main",
    "branch_index": 0,
    "condition": { "filter_operator": "and", "filters": [] }
  }
}
```

### Split Nodes

A `SPLIT` node splits users into more than two paths (or two paths for `IPO`). Its `child_stage_ids` map one-to-one to `BRANCH` children. `config` fields depend on the `sub_type`:

* `CONDITIONAL_SPLIT` routes each user down a branch based on a condition. Its `config` carries `branch_order` (the ordered list of child `stage_id`s), `evaluation_timing`, and `wait`.
* `AB_SPLIT` divides users randomly by percentage. Its `config.branches[]` lists each arm as `{ stage_id, name, percentage }`.
* `IPO` (Intelligent Path Optimizer) uses the same `config.branches[]` as `AB_SPLIT`, plus an `optimization_metric` (observed value: `ENGAGEMENT`). The ordering comes from `branches[]`, so there is no `branch_order`.

```json Multi-way CONDITIONAL_SPLIT theme={null}
{
  "stage_id": "f3OFPd5GNe",
  "type": "SPLIT",
  "sub_type": "CONDITIONAL_SPLIT",
  "child_stage_ids": ["KFs3PrkZJ", "CfwMAJthB", "EfjrVVREJ9"],
  "label": "Has Signed Up and Purchased a Policy?",
  "config": {
    "evaluation_timing": "ON_ENTRY",
    "branch_order": ["KFs3PrkZJ", "CfwMAJthB", "EfjrVVREJ9"],
    "wait": { "duration": 0, "unit": "hours" }
  }
}
```

```json AB_SPLIT theme={null}
{
  "stage_id": "uMQdmCeJia",
  "type": "SPLIT",
  "sub_type": "AB_SPLIT",
  "parent_stage_id": "CfwMAJthB",
  "child_stage_ids": ["LsyB1exIx", "eElEw9Uaq8", "fxHKPCJ-M"],
  "label": "A/B Split",
  "config": {
    "branches": [
      { "stage_id": "LsyB1exIx", "name": "Branch 1", "percentage": 33 },
      { "stage_id": "eElEw9Uaq8", "name": "Branch 2", "percentage": 33 },
      { "stage_id": "fxHKPCJ-M", "name": "Branch 3", "percentage": 34 }
    ]
  }
}
```

### Action Nodes

An `ACTION` node sends a campaign or selects a channel. For a channel send (`EMAIL`, `PUSH`, `SMS`, `WHATSAPP`, and so on), `config` carries `campaign_id`, `channel`, and `campaign_name`.

The `NBA` (Next Best Action) sub\_type is an `ACTION` node — it occupies the same slot as channel sends, not a separate node type. Its `config` fields:

* `channels[]` — one entry per eligible channel, each `{ channel, stage_id, campaign_id }`. `stage_id` references a sibling `ACTION` node (of that channel) that owns the actual send; the NBA node itself holds no campaign content.
* `fallback_channel` — the channel used when the best channel can't be determined (observed value: `PUSH`).
* `best_time_to_send` — the send-time strategy (observed shape: `{ "default_time": "SEND_IMMEDIATELY" }`).

```json ACTION node with NBA sub_type theme={null}
{
  "stage_id": "0-5G1JPQX",
  "type": "ACTION",
  "sub_type": "NBA",
  "parent_stage_id": "abw_MALGPa",
  "child_stage_ids": ["dgVoYPiUPT"],
  "label": "Next Best Action",
  "config": {
    "channels": [
      { "channel": "PUSH", "stage_id": "x_-NueaX4X", "campaign_id": "69844f63316edd32518c5aef" },
      { "channel": "EMAIL", "stage_id": "zN9ibHPXbE", "campaign_id": "69844f63316edd32518c5af1" }
    ],
    "fallback_channel": "PUSH",
    "best_time_to_send": { "default_time": "SEND_IMMEDIATELY" }
  }
}
```

### Control Nodes

A `CONTROL` node manages flow control and path convergence. Observed `sub_type` values:

* `EXIT` — exits the user from the flow.
* `WAIT_FOR_TIMER` — holds the user for a set duration.
* `GO_TO` — routes the user to a shared downstream stage (`config.target_stage_id`), used to re-converge parallel branches.

## FAQs

<AccordionGroup>
  <Accordion title="Can I create or edit flows with these endpoints?">
    No, this isn't supported. You can search, read, and change the status of existing flows.
  </Accordion>

  <Accordion title="How do I enroll users into a flow through the API?">
    You do not enroll users directly. For event-triggered flows, use the Track Event API — a user enters the flow when a tracked event matches the flow's entry condition. For business-event-triggered flows, use the Trigger Business Event API. Periodic flows are audience-driven and do not support API enrollment.
  </Accordion>

  <Accordion title="What is the difference between flow_id and version_id?">
    `flow_id` identifies the flow. `version_id` identifies one of its versions. To read a specific version, either pass `version_no` on **Get a single flow**, or call **Get a specific version of a flow** with the `version_id`.
  </Accordion>

  <Accordion title="Which status changes can I make?">
    You can `pause`, `resume`, `stop`, `retire`, `archive`, and `unarchive` a flow. Each change is validated against the flow's current status; an invalid change returns a `409`. Publish isn't available.
  </Accordion>

  <Accordion title="What permissions do I need?">
    The read endpoints (search, get flow, get version) require the `campaigns:view` permission. The status endpoint requires the `campaigns:create_manage` permission.
  </Accordion>
</AccordionGroup>

## Postman Collection

Test these endpoints quickly using our pre-configured Postman collection: [View MoEngage Flows APIs Collection](https://www.postman.com/moengage-dev/api-docs/collection/wtlm44a/moengage-flows-apis).
