---
title: "Flows API Reference"
description: "Read and build Chatfuel flows through the Public API: flows and groups, blocks and connections, message and action components, keyword rules and test chats."
canonical_url: https://chatfuel.com/docs/public-api/flows
markdown_url: https://chatfuel.com/docs/public-api/flows.md
last_updated: 2026-10-06
lang: en
site: https://chatfuel.com/docs
llms_txt: https://chatfuel.com/llms.txt
---

# Flows API Reference

Read and build Chatfuel flows through the Public API: flows and groups, blocks and connections, message and action components, keyword rules and test chats.

A flow is a scripted conversation made of blocks connected to each other; each block holds components such as a WhatsApp message, a list, a condition or a JSON API call. Through the Public API you can read flows, create and organize them, add and connect blocks, configure every component, manage keyword rules and triggers, and run test chats. Reading flows and running test chats works for **Agent** virtual users; every change needs **Editor**.

## How to read flows [#how-to-read-flows]

```graphql
type Bot {
  # Agent.
  flowGroups: [FlowGroup!]!            # { id name flows { id name } }
  flowsWithoutGroup: [RegularFlow!]!
  defaultReplyFlows: [DefaultReplyFlow!]!
  flow(flowID: FlowID!): Flow!
}
```

```graphql
query FlowStructure($botID: BotID!, $flowID: FlowID!) {
  bot(id: $botID) {
    flow(flowID: $flowID) {
      id
      name
      platform
      startingPointBlock { id }
      blocks {
        id
        name
        positionX
        positionY
        blockElements { id errors { __typename } }
      }
      connections { __typename }
    }
  }
}
```

`Block` and `BlockElement` are interfaces with one type per kind of block and component. Use inline fragments for the types you work with.

## How to create and organize flows [#how-to-create-and-organize-flows]

All mutations on this page change flows and need **Editor**.

```graphql
type Mutation {
  createFlow(botID: BotID!, platform: Platform!): Bot!
  updateFlowName(flowID: FlowID!, name: String!): Flow!
  deleteFlow(flowID: FlowID!): Bot!
  createFlowGroup(botID: BotID!): Bot!
  updateFlowGroupName(id: FlowGroupID!, name: String!): FlowGroup!
  deleteFlowGroup(flowGroupId: FlowGroupID!): Bot!
  moveFlowToGroup(flowID: FlowID!, groupID: FlowGroupID!): Bot!
  removeFlowFromGroup(flowID: FlowID!): Bot!
  sortFlowGroups(botID: BotID!, flowGroupIds: [FlowGroupID!]!): Bot!
  sortFlowsInGroup(groupID: FlowGroupID!, flowIds: [FlowID!]!): FlowGroup!
  sortUngroupedFlows(botID: BotID!, flowIds: [FlowID!]!): Bot!
}
```

## How to work with blocks and connections [#how-to-work-with-blocks-and-connections]

```graphql
type Mutation {
  updateBlockName(flowID: FlowID!, blockID: BlockID!, name: String!): Block!
  updateBlockPosition(flowID: FlowID!, blockID: BlockID!, positionX: Int!, positionY: Int!): Block!
  updateBlockPositionBulk(flowID: FlowID!, update: [BlockPositionBulkUpdate!]!): Flow!
  deleteBlock(flowID: FlowID!, blockID: BlockID!): Flow!
  blockSetStartingPoint(flowID: FlowID!, blockID: BlockID!): Flow!
  blockEnableEntryPoint(flowID: FlowID!, blockID: BlockID!): Flow!
  blockDisableEntryPoint(flowID: FlowID!, blockID: BlockID!): Flow!
  sortBlockElements(blockID: BlockID!, elementIDs: [BlockElementID!]!): Block!
  blockElementDelete(botID: BotID!, elementID: BlockElementID!): Flow!

  # Block → block, or a component's output handle (a button, a condition branch) → block.
  blockToBlockConnectionCreateOrUpdate(flowID: FlowID!, request: BlockToBlockConnectionCreateRequest!): Flow!
  blockToBlockConnectionDelete(flowID: FlowID!, sourceBlockID: BlockID!): Flow!
  componentToBlockConnectionCreateOrUpdate(flowID: FlowID!, request: ComponentToBlockConnectionCreateRequest!): Flow!
  componentToBlockConnectionDelete(flowID: FlowID!, sourceBlockElementID: BlockElementID!, sourceHandleID: ComponentHandleID!): Flow!
}
```

Positions are canvas coordinates in pixels, the same as in the dashboard flow editor.

## How to add components [#how-to-add-components]

Each component kind has a family of mutations named after it. Most families have three ways to create a component:

* `<family>CreateWithBlock(flowID, positionX, positionY)` creates a new block with the component at the given position.
* `<family>CreateWithBlockAndConnection(flowID, request, positionX, positionY)` also connects it to an existing block or handle given in `request: { sourceBlockID, sourceBlockElementID, sourceHandleID }`.
* `<family>CreateInBlock(blockID)` adds the component to an existing block.

Setters then take the component's `blockElementID`. A WhatsApp text message, for example:

```graphql
type Mutation {
  whatsAppTextCreateWithBlock(flowID: FlowID!, positionX: Int!, positionY: Int!): Flow!
  whatsAppTextCreateWithBlockAndConnection(flowID: FlowID!, request: UndefinedTargetBlockConnectionCreateRequest!, positionX: Int!, positionY: Int!): Flow!
  whatsAppTextCreateInBlock(blockID: BlockID!): ContentBlock!
  whatsAppTextSetText(blockElementID: BlockElementID!, text: String!): ContentBlock!
  whatsAppTextSetWaitForReplies(blockElementID: BlockElementID!, waitForReplies: Boolean!): ContentBlock!
  whatsAppTextSetSaveContactReplyToAttribute(blockElementID: BlockElementID!, saveContactReply: Boolean!, attribute: AttributeName): ContentBlock!
}
```

The component families and their mutations, after the family prefix:

* **`whatsAppText`, `whatsAppImage`, `whatsAppVideo`, `whatsAppAudio`, `whatsAppDocument`:** the three create mutations, `SetText` or `SetImageFile` / `SetVideoFile` / `SetAudioFile` / `SetDocumentFile`, `SetCaption` (image, video, document), `SetWaitForReplies`, `SetSaveContactReplyToAttribute`.
* **`whatsAppTextAndButtons`:** `CreateWithBlock`, `CreateWithBlockAndConnection`, `SetHeaderText`, `SetBodyText`, `SetFooterText`, `SetButtonTitle`, `AddNewContinueFlowButton`, `DeleteButton`, `MoveButton`, `WaitForReplies`, `SetSaveContactReplyToAttribute`.
* **`whatsAppTextAndURL`:** `CreateWithBlock`, `CreateWithBlockAndConnection`, `SetHeaderText`, `SetBodyText`, `SetFooterText`, `SetButtonTitle`, `SetButtonURL`, `WaitForReplies`, `SetSaveContactReplyToAttribute`.
* **`whatsAppList`:** `CreateWithBlock`, `CreateWithBlockAndConnection`, `SetBodyText`, `SetButtonTitle`, `AddRow`, `DeleteRow`, `ReorderRows`, `SetRowTitle`, `SetRowDescription`, `WaitForReplies`, `SetSaveContactReplyToAttribute`.
* **`whatsAppTemplate`:** `CreateWithBlock`, `CreateWithBlockAndConnection`, `SetTemplate`, `DeleteTemplate`, `SetHeaderTextParamValue`, `SetBodyTextParamValue`, `SetFooterTextParamValue`, `SetHeaderImageFile`, `SetHeaderVideoFile`, `SetHeaderDocumentFile`, `SetURLButtonTextParamValue`, `SetCopyCodeButtonCodeValue`, `SetWaitForReplies`, `SetSaveContactReplyToAttribute`.
* **`whatsAppScheduledMessage`:** `CreateWithBlock`, `CreateWithBlockAndWATemplate`, `UpdateSegment`, `SetFirstSendTime`, `SetRepeatType`, `SetRepeatEveryNDays`, `SetOnCertainDates`, `SetWeekdays`.
* **`whatsAppOneTimeNotification`:** `CreateWithBlock`, `CreateWithBlockAndWATemplate`, `UpdateSegment`, `Send`.
* **`widgetTextAndButtons`:** the three create mutations, `SetText`, `AddNewContinueFlowButton`, `AddNewOpenURLButton`, `AddNewPhoneButton`, `SetButtonTitle`, `SetButtonURL`, `SetButtonPhone`, `DeleteButton`, `MoveButton`, `SetWaitForReplies`, `SetSaveContactReplyToAttribute`.
* **`widgetImage`:** the three create mutations, `SetImageFile`, `SetWaitForReplies`, `SetSaveContactReplyToAttribute`.
* **`whatsAppSwitchToChatWithHumanAgent`, `facebookSwitchToChatWithHumanAgent`, `instagramSwitchToChatWithHumanAgent`, `tiktokSwitchToChatWithHumanAgent`, `widgetSwitchToChatWithHumanAgent`:** the three create mutations.
* **`aiAgent`:** `CreateWithBlock` and `CreateWithBlockAndConnection` with a `templateID` from the `aiAgentTemplates(locale:)` query, `UpdateAdditionalInstructions`, `CreateRule`, `DeleteRule`, `UpdateRuleTitle`, `UpdateRulePrompt`. A custom AI agent uses `aiAgentCustomUpdatePrompt`, `aiAgentCustomCreateRule`, `aiAgentCustomDeleteRule`, `aiAgentCustomUpdateRuleTitle` and `aiAgentCustomUpdateRulePrompt`.
* **`sendJson`** (the JSON API action): the three create mutations, `UpdateHTTPMethod`, `UpdateURL`, `AddHeader`, `UpdateHeaderTitle`, `UpdateHeaderValue`, `DeleteHeader`, `AddURLParam`, `UpdateURLParamTitle`, `UpdateURLParamValue`, `DeleteURLParam`, `UpdatePayloadType`, `UpdateCustomRequestPayload`, `EnableResponseParsingRules`, `DisableResponseParsingRules`, `ResponseAddParsingRule`, `UpdateResponseParsingRuleJSONPath`, `UpdateResponseParsingRuleAttributeName`, `DeleteResponseParsingRule`, `TestRequest`.
* **`albatoIntegration`:** the three create mutations with a `templateID`, `UpdateSelectedEvent`, `UpdateEventFieldValue`, `AddEventCustomField`, `RemoveEventCustomField`, `UpdateEventCustomFieldName`, `UpdateEventCustomFieldValue`.
* **`setCondition`:** the three create mutations, `UpdateSegment`.
* **`setContactProperty`:** the three create mutations, `SetAttribute`, `SetValue&#x60;. &#x2A;*`clearContactProperty`:** the three create mutations, `SetAttribute`.
* **`summarizeChat`:** the three create mutations, `AddEntry`, `UpdateEntry`, `DeleteEntry`.
* **`redirectToFlow`:** `CreateWithBlock`, `CreateWithBlockAndConnection`, `SetTargetFlow`, `RemoveTargetFlow`.
* **`triggeredMessage`:** `CreateWithBlock`, `CreateWithBlockAndWATemplate`, `SetSegment`.

[How to integrate with JSON API](https://chatfuel.com/docs/integrations/how-to-integrate-with-json-api) explains what each `sendJson` setting does.

## Keyword rules and triggers [#keyword-rules-and-triggers]

Keyword rules send a contact to a flow or reply with a message when their text matches. Reading them with `bot.keywordRules(first, after)` and changing them both need **Editor**.

```graphql
type Mutation {
  keywordRuleCreate(botID: BotID!): KeywordRule!
  # matchType: similarTo, contains or matches
  keywordRuleUpdateMatchType(botID: BotID!, keywordRuleID: KeywordRuleID!, matchType: KeywordRuleMatchType!): KeywordRule!
  keywordRuleUpdateKeywords(botID: BotID!, keywordRuleID: KeywordRuleID!, keywords: [String!]!): KeywordRule!
  # actionType: switchToFlow, sendMessage or doNothing, set per platform
  keywordRuleUpdateActionType(botID: BotID!, keywordRuleID: KeywordRuleID!, platform: Platform!, actionType: KeywordRuleActionType!): KeywordRule!
  keywordRuleDelete(botID: BotID!, keywordRuleID: KeywordRuleID!): Bot!

  # Triggers of triggered messages. An enabled trigger can't be changed (EnabledTriggerIsImmutable).
  # conditionType: LastMessageFromContact or ContactAttributeChanged
  triggerSetConditionType(id: TriggerID!, conditionType: TriggerConditionType!): Trigger!
  triggerSetAttributeFilter(id: TriggerID!, attrCondition: AttrFilterInput!): Trigger!
  triggerDeleteAttributeFilter(id: TriggerID!): Trigger!
  triggerSetDelay(id: TriggerID!, delay: TriggerDelayInput!): Trigger!
}
```

## How to test a flow in a test chat [#how-to-test-a-flow-in-a-test-chat]

A test chat runs a flow, the whole bot or an automation in a sandbox conversation without messaging real contacts. Starting a session works for **Agent**:

```graphql
type Mutation {
  previewResponsesStartInFlow(flowID: FlowID!): PreviewResponsesFlowSession!
  previewResponsesStartForBot(botID: BotID!, platform: Platform!): PreviewResponsesBotSession!
  previewResponsesStartForFuelyAutomation(botID: BotID!, fuelyAutomationID: FuelyAutomationID!): PreviewResponsesFuelyAutomationSession!
}
```

The session returns a `conversationID`. Play the contact's side with `previewResponsesWhatsappTextSend`, `previewResponsesInstagramTextSend`, `previewResponsesFacebookTextSend`, `previewResponsesTikTokTextSend` or `previewResponsesWidgetTextSend`, simulate comments with `previewResponsesInstagramPostCommentSend` and `previewResponsesFacebookPostCommentSend`, and simulate clicks with `previewResponsesWhatsappContinueFlowBtnClickSend`, `previewResponsesWhatsappListRowClickSend`, `previewResponsesWhatsappTemplateQuickReplyBtnClickSend` and the `previewResponsesWidget*BtnClickSend` mutations. All take `botID` and `conversationID`. Read the bot's answers with the `messageAdded` subscription described in [messaging](https://chatfuel.com/docs/public-api/messaging).

## Common issues [#common-issues]

### "NotEnoughPermissions" when changing a flow [#notenoughpermissions-when-changing-a-flow]

Agent virtual users can read flows and run test chats but not change them. [Change the role](https://chatfuel.com/docs/public-api/virtual-users) to Editor.

### "EnabledTriggerIsImmutable" [#enabledtriggerisimmutable]

A triggered message can't be edited while it is on. Turn it off in the dashboard, change the trigger, and turn it on again.

### A component shows errors in the flow [#a-component-shows-errors-in-the-flow]

`blockElements { errors }` lists what is missing, such as an empty text or an unset template. The dashboard shows the same errors on the block; fill in the missing settings with the component's setters.
