---
title: "Messaging API Reference"
description: "Read conversations and send text, attachments and WhatsApp templates on WhatsApp, Instagram, Facebook, TikTok and the website widget through the Public API."
canonical_url: https://chatfuel.com/docs/public-api/messaging
markdown_url: https://chatfuel.com/docs/public-api/messaging.md
last_updated: 2026-10-06
lang: en
site: https://chatfuel.com/docs
llms_txt: https://chatfuel.com/llms.txt
---

# Messaging API Reference

Read conversations and send text, attachments and WhatsApp templates on WhatsApp, Instagram, Facebook, TikTok and the website widget through the Public API.

The messaging part of the Chatfuel Public API covers the inbox: reading conversations and their messages, sending text, files and WhatsApp templates to contacts on every channel, taking over a chat as a human and handing it back to a flow, and listening for new messages in real time. All of it works for **Agent** virtual users, so a support or CRM integration doesn't need the Editor role.

A conversation belongs to one contact, and its `id` is the same value as the contact's ID. Wherever an operation asks for a `conversationID`, you can pass the contact ID from the [contacts API](https://chatfuel.com/docs/public-api/contacts).

## How to read conversations and messages [#how-to-read-conversations-and-messages]

```graphql
type Bot {
  # Agent. The conversation with one contact.
  conversation(conversationID: ConversationID!): Conversation!
  # Agent. Number of conversations per status: open, closed, automated.
  conversationCount: [ConversationCountEntry!]!
  # Agent. Open conversations nobody has looked at yet.
  unseenOpenDialogsCount: Int!
  # Agent. The inbox list: contacts with conversations, filtered like the dashboard inbox.
  contactChatsConnection(
    first: Int!, before: ContactSearchCursor, after: ContactSearchCursor,
    assigneeFilter: ContactAssigneeFilter!, unreadOnly: Boolean!,
    salesStageV2Filter: [SalesStageV2!]!, textInputFilter: String
  ): ContactConnection!
}
```

```graphql
query Chat($botID: BotID!, $contactID: ConversationID!) {
  bot(id: $botID) {
    conversation(conversationID: $contactID) {
      id
      platform          # whatsapp, instagram, facebook, tiktok, widget
      status            # open, closed, automated
      read
      messages(first: 20) {
        edges {
          node {
            id
            sentTime
            sender { name }
            errors { code originalErrMessage }
            ... on WhatsAppInTextMessage { text }
            ... on WhatsAppOutTextMessage { text whatsappStatus }
          }
        }
        pageInfo { hasNextPage endCursor }
      }
    }
  }
}
```

`Message` is an interface with one type per channel, direction and content, for example `WhatsAppInTextMessage`, `InstagramInStoryReplyMessage` or `FacebookOutAttachment`. Select the fields of the types you handle and skip the rest; new message types can appear at any time.

## How to send messages [#how-to-send-messages]

Every channel has a text mutation and an attachment mutation. They all take the bot ID and the conversation ID (the contact ID) and work for **Agent**:

```graphql
type Mutation {
  whatsAppTextMessageSend(botID: BotID!, conversationID: ConversationID!, message: WhatsAppTextMessageSendInput): WhatsAppOutTextMessage
  whatsappAttachmentMessageSend(botID: BotID!, conversationID: ConversationID!, message: WhatsAppAttachmentMessageSendInput!): WhatsAppOutAttachment
  instagramTextMessageSend(botID: BotID!, conversationID: ConversationID!, message: InstagramTextMessageSendInput!): InstagramOutTextMessage!
  instagramAttachmentMessageSend(botID: BotID!, conversationID: ConversationID!, message: InstagramAttachmentMessageSendInput!): InstagramOutAttachment
  facebookTextMessageSend(botID: BotID!, conversationID: ConversationID!, message: FacebookTextMessageSendInput!): FacebookOutTextMessage!
  facebookAttachmentMessageSend(botID: BotID!, conversationID: ConversationID!, message: FacebookAttachmentMessageSendInput!): FacebookOutAttachment
  tiktokTextMessageSend(botID: BotID!, conversationID: ConversationID!, message: TikTokTextMessageSendInput!): TikTokOutTextMessage!
  tiktokAttachmentMessageSend(botID: BotID!, conversationID: ConversationID!, message: TikTokAttachmentMessageSendInput!): TikTokOutAttachment
  widgetTextMessageSend(botID: BotID!, conversationID: ConversationID!, message: WidgetTextMessageSendInput): WebWidgetTextMessage
  widgetAttachmentMessageSend(botID: BotID!, conversationID: ConversationID!, message: WidgetAttachmentMessageSendInput): WebWidgetAttachmentMessage
}
```

```graphql
mutation Reply($botID: BotID!, $contactID: ConversationID!) {
  whatsAppTextMessageSend(
    botID: $botID
    conversationID: $contactID
    message: { text: "Hi! Your order has shipped.", clientId: "order-1042-shipped" }
  ) {
    id
    whatsappStatus
    errors { code originalErrMessage }
  }
}
```

`clientId` is an optional ID you choose; it comes back on the message and in subscription events, so you can match your own records to Chatfuel messages. For attachments, upload the file to the chat upload endpoint described in [making requests](https://chatfuel.com/docs/public-api/requests) and pass its ID. On WhatsApp, `attachmentType` is `image`, `document` or `audio`. Attachment mutations can fail with `FileContentTypeNotSupported`, `FileTooBig` or `FileDoesNotExist`.

Channel rules still apply. On WhatsApp, a free-form message reaches the contact only within the [24-hour messaging window](https://chatfuel.com/docs/whatsapp/24-hour-messaging-window) after the contact's last message; after that, send an approved template. Instagram and Facebook have similar messaging windows. Delivery problems show up in the message's `errors`.

## How to send a WhatsApp template [#how-to-send-a-whatsapp-template]

Sending a template takes three steps: pick an approved template, fill in its parameters, and send the filled template. All steps work for **Agent**.

```graphql
type Bot {
  # Agent. The bot's WhatsApp templates with their status, language, category and components.
  whatsAppTemplates(first: Int, after: WhatsAppTemplateCursor, before: WhatsAppTemplateCursor): WhatsappTemplates
  filledWhatsAppTemplate(id: FilledWhatsAppTemplateID!): FilledWhatsAppTemplate!
}

type Mutation {
  # 1. Create a one-time filled copy of a template.
  filledWhatsAppTemplateCreateTemporary(botID: BotID!, templateID: WhatsAppTemplateID!): FilledWhatsAppTemplate!
  # 2. Fill in variables and media.
  filledWhatsAppTemplateSetHeaderTextParamValue(templateID: FilledWhatsAppTemplateID!, name: WhatsAppTemplateTextParamName!, value: String!): FilledWhatsAppTemplate!
  filledWhatsAppTemplateSetBodyTextParamValue(templateID: FilledWhatsAppTemplateID!, name: WhatsAppTemplateTextParamName!, value: String!): FilledWhatsAppTemplate!
  filledWhatsAppTemplateSetFooterTextParamValue(templateID: FilledWhatsAppTemplateID!, name: WhatsAppTemplateTextParamName!, value: String!): FilledWhatsAppTemplate!
  filledWhatsAppTemplateSetHeaderImageFile(templateID: FilledWhatsAppTemplateID!, fileID: FileID!): FilledWhatsAppTemplate!
  filledWhatsAppTemplateSetHeaderVideoFile(templateID: FilledWhatsAppTemplateID!, fileID: FileID!): FilledWhatsAppTemplate!
  filledWhatsAppTemplateSetHeaderDocumentFile(templateID: FilledWhatsAppTemplateID!, fileID: FileID!, fileName: String!): FilledWhatsAppTemplate!
  filledWhatsAppTemplateSetURLButtonParamValue(templateID: FilledWhatsAppTemplateID!, buttonID: ComponentHandleID!, name: WhatsAppTemplateTextParamName!, value: String!): FilledWhatsAppTemplate!
  filledWhatsAppTemplateSetCopyCodeButtonCodeValue(templateID: FilledWhatsAppTemplateID!, buttonID: ComponentHandleID!, codeValue: String!): FilledWhatsAppTemplate!
  # 3. Send it to the contact.
  whatsAppTemplateSend(botID: BotID!, conversationID: ConversationID!, template: WhatsAppTemplateSendInput): WhatsAppOutTemplateMessage
}
```

Header images, videos and documents are uploaded to the template upload endpoint described in [files and tasks](https://chatfuel.com/docs/public-api/files-and-tasks). Check `FilledWhatsAppTemplate.errors` before sending: it lists parameters that are still empty or invalid. Use only templates whose `IsSupportedInLivechat` is `true`. [How to create WhatsApp templates](https://chatfuel.com/docs/whatsapp/how-to-create-whatsapp-templates) explains how templates are created and approved.

## How to take over a chat and hand it back [#how-to-take-over-a-chat-and-hand-it-back]

```graphql
type Mutation {
  # Agent. Returns the conversation with a contact, creating it if the contact has none yet.
  conversationCreate(contactID: ContactID!): Conversation!
  # Agent. Opens a live chat with the contact (status open), like a teammate taking over the chat in the inbox.
  conversationStart(botID: BotID!, conversationID: ConversationID!): Conversation!
  # Agent. Closes the live chat and sends the contact to a flow.
  conversationFinishSendToFlow(botID: BotID!, conversationID: ConversationID!, flowID: FlowID!): Conversation!
  # Agent. Marks messages up to the given one as read.
  conversationReadMessages(botID: BotID!, conversationID: ConversationID!, before: MessageID!): Conversation!
}
```

Live chats can close automatically after a period without messages. `bot.livechatAutoClosingConfig` shows the setting to any virtual user; changing it with `botSetLivechatAutoClosingConfig(botID, cfg: { enabled, delay })`, where `delay` runs from `Minutes10` to `Days7`, is **Personal token only** and needs the Admin role.

## Messaging subscriptions [#messaging-subscriptions]

```graphql
type Subscription {
  # Agent. A new message in one conversation, incoming or outgoing.
  messageAdded(botID: BotID!, conversationID: ConversationID!): Message
  # Agent. A message changed, for example its WhatsApp delivery status.
  messageUpdated(botID: BotID!, conversationID: ConversationID!): Message
  # Agent. Changes in the inbox list for the given filters.
  contactsChatUpdates(
    botID: BotID!, assigneeFilter: ContactAssigneeFilter!, unreadOnly: Boolean!,
    salesStageV2Filter: [SalesStageV2!]!, textInputFilter: String
  ): ContactsChatUpdate
  # Agent. The number of unseen open conversations changed.
  unseenOpenDialogsCountChanged(botID: BotID!): Int
}
```

To follow the whole inbox, subscribe to `contactsChatUpdates` with `assigneeFilter: { type: Any }`, then open `messageAdded` for the conversations you care about.

## Common issues [#common-issues]

### A WhatsApp message is accepted but never delivered [#a-whatsapp-message-is-accepted-but-never-delivered]

The 24-hour window is probably closed. Read `errors` on the returned message or wait for `messageUpdated`; to reach the contact, send an approved template with `whatsAppTemplateSend`.

### "NotEnoughPermissions" when sending a message [#notenoughpermissions-when-sending-a-message]

The conversation belongs to another bot, or the bot's role settings limit the virtual user to contacts assigned to it. Assign the contact to the virtual user with `contactSetAssignee`, or change the role settings, as described in [team and roles](https://chatfuel.com/docs/public-api/team-and-roles).

### "FileContentTypeNotSupported" [#filecontenttypenotsupported]

The channel doesn't accept this file type, or the `fileType` used at upload doesn't match `attachmentType`. Upload the file again with the right type.
