Chatfuel

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.

Last updated on

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.

How to read conversations and messages

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!
}
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

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:

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
}
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 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 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

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

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. 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 explains how templates are created and approved.

How to take over a chat and hand it back

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

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

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

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.

"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.

On this page