Enviar menu interativo (botões, carrossel, lista ou enquete)

POST /send/menu

Este endpoint oferece uma interface unificada para envio de quatro tipos principais de mensagens interativas:

  • Botões: Para ações rápidas e diretas
  • Carrossel de botões: Para uma lista horizontal de botões com imagens
  • Listas: Para menus organizados em seções
  • Enquetes: Para coleta de opiniões e votações

Suporte a campos de rastreamento: Este endpoint também suporta track_source e track_id documentados na tag "Enviar Mensagem".

Estrutura Base do Payload

Todas as requisições seguem esta estrutura base:

{
  "number": "5511999999999",
  "type": "button|button_legacy|list|poll|carousel",
  "text": "Texto principal da mensagem",
  "choices": ["opções baseadas no tipo escolhido"],
  "footerText": "Texto do rodapé (opcional para botões e listas)",
  "listButton": "Texto do botão (para listas)",
  "selectableCount": "Número de opções selecionáveis (apenas para enquetes)"
}

Tipos de Mensagens Interativas

1. Botões (type: "button")

Cria botões interativos com diferentes funcionalidades de ação.

Campos Específicos

  • footerText: Texto opcional exibido abaixo da mensagem principal
  • choices: Array de opções que serão convertidas em botões

Formatos de Botões

Cada botão pode ser configurado usando | (pipe) ou \n (quebra de linha) como separadores:

  • Botão de Resposta:

    • "texto|id" ou
    • "texto\nid" ou
    • "texto" (ID será igual ao texto)
  • Botão de Cópia:

    • "texto|copy:código" ou
    • "texto\ncopy:código"
  • Botão de Chamada:

    • "texto|call:+5511999999999" ou
    • "texto\ncall:+5511999999999"
  • Botão de URL:

    • "texto|https://exemplo.com" ou
    • "texto|url:https://exemplo.com"

Botões com Imagem

Para adicionar uma imagem aos botões, use o campo imageButton no payload:

Exemplo com Imagem

{
  "number": "5511999999999",
  "type": "button",
  "text": "Escolha um produto:",
  "imageButton": "https://exemplo.com/produto1.jpg",
  "choices": [
    "Produto A|prod_a",
    "Mais Info|https://exemplo.com/produto-a",
    "Produto B|prod_b",
    "Ligar|call:+5511999999999"
  ],
  "footerText": "Produtos em destaque"
}

Suporte: O campo imageButton aceita URLs ou imagens em base64.

Botões no formato legado (type: "button_legacy")

Use button_legacy somente quando precisar enviar os botões de resposta do formato anterior. O formato de choices é:

  • "texto|id"
  • "texto\nid"
  • "texto|reply:id"
  • "texto" — o próprio texto será usado como ID

Este modo cria apenas botões de resposta. Ações de URL, chamada e cópia e o campo imageButton continuam disponíveis no formato atual type: "button".

{
  "number": "5511999999999",
  "type": "button_legacy",
  "text": "Deseja continuar?",
  "choices": [
    "Sim|confirmar",
    "Não|reply:cancelar"
  ],
  "footerText": "Escolha uma opção"
}

Exemplo Completo

{
  "number": "5511999999999",
  "type": "button",
  "text": "Como podemos ajudar?",
  "choices": [
    "Suporte Técnico|suporte",
    "Fazer Pedido|pedido",
    "Nosso Site|https://exemplo.com",
    "Falar Conosco|call:+5511999999999"
  ],
  "footerText": "Escolha uma das opções abaixo"
}

Limitações e Compatibilidade

Importante: Ao combinar botões de resposta com outros tipos (call, url, copy) na mesma mensagem, será exibido o aviso: "Não é possível exibir esta mensagem no WhatsApp Web. Abra o WhatsApp no seu celular para visualizá-la."

2. Listas (type: "list")

Cria menus organizados em seções com itens selecionáveis.

Campos Específicos

  • listButton: Texto do botão que abre a lista
  • footerText: Texto opcional do rodapé
  • choices: Array com seções e itens da lista

Formato das Choices

  • "[Título da Seção]": Inicia uma nova seção
  • "texto|id|descrição": Item da lista com:
    • texto: Label do item
    • id: Identificador único, opcional
    • descrição: Texto descritivo adicional e opcional

Exemplo Completo

{
  "number": "5511999999999",
  "type": "list",
  "text": "Catálogo de Produtos",
  "choices": [
    "[Eletrônicos]",
    "Smartphones|phones|Últimos lançamentos",
    "Notebooks|notes|Modelos 2024",
    "[Acessórios]",
    "Fones|fones|Bluetooth e com fio",
    "Capas|cases|Proteção para seu device"
  ],
  "listButton": "Ver Catálogo",
  "footerText": "Preços sujeitos a alteração"
}

3. Enquetes (type: "poll")

Cria enquetes interativas para votação.

Campos Específicos

  • selectableCount: Número de opções que podem ser selecionadas (padrão: 1)
  • choices: Array simples com as opções de voto

Exemplo Completo

{
  "number": "5511999999999",
  "type": "poll",
  "text": "Qual horário prefere para atendimento?",
  "choices": [
    "Manhã (8h-12h)",
    "Tarde (13h-17h)",
    "Noite (18h-22h)"
  ],
  "selectableCount": 1
}

Cria um carrossel de cartões com imagens e botões interativos.

Campos Específicos

  • choices: Array com elementos do carrossel na seguinte ordem:
    • [Texto do cartão]: Texto do cartão entre colchetes
    • {URL ou base64 da imagem}: Imagem entre chaves
    • Botões do cartão (um por linha):
      • "texto|copy:código" para botão de copiar
      • "texto|https://url" para botão de link
      • "texto|call:+número" para botão de ligação

Exemplo Completo

{
  "number": "5511999999999",
  "type": "carousel",
  "text": "Conheça nossos produtos",
  "choices": [
    "[Smartphone XYZ\nO mais avançado smartphone da linha]",
    "{https://exemplo.com/produto1.jpg}",
    "Copiar Código|copy:PROD123",
    "Ver no Site|https://exemplo.com/xyz",
    "Fale Conosco|call:+5511999999999",
    "[Notebook ABC\nO notebook ideal para profissionais]",
    "{https://exemplo.com/produto2.jpg}",
    "Copiar Código|copy:NOTE456",
    "Comprar Online|https://exemplo.com/abc",
    "Suporte|call:+5511988888888"
  ]
}

Nota: Criamos outro endpoint para carrossel: /send/carousel, funciona da mesma forma, mas com outro formato de payload. Veja o que é mais fácil para você.

Termos de uso

Os recursos de botões interativos e listas podem ser descontinuados a qualquer momento sem aviso prévio. Não nos responsabilizamos por quaisquer alterações ou indisponibilidade destes recursos.

Alternativas e Compatibilidade

Considerando a natureza dinâmica destes recursos, nosso endpoint foi projetado para facilitar a migração entre diferentes tipos de mensagens (botões, listas e enquetes).

Recomendamos criar seus fluxos de forma flexível, preparados para alternar entre os diferentes tipos.

Em caso de descontinuidade de algum recurso, você poderá facilmente migrar para outro tipo de mensagem apenas alterando o campo "type" no payload, mantendo a mesma estrutura de choices.

Autenticação

[
  {
    "token": []
  }
]
{
  "token": {
    "name": "token",
    "type": "apiKey",
    "in": "header"
  }
}

Corpo da requisição

{
  "required": true,
  "content": {
    "application/json": {
      "schema": {
        "type": "object",
        "properties": {
          "number": {
            "type": "string",
            "description": "ID do chat para o qual a mensagem será enviada. Pode ser um número de telefone em formato internacional, um ID de grupo (`@g.us`), um ID de usuário (com `@s.whatsapp.net` ou `@lid`).",
            "example": "5511999999999"
          },
          "type": {
            "type": "string",
            "description": "Tipo do menu. Use button para o formato atual e button_legacy apenas para botões de resposta no formato anterior.",
            "enum": [
              "button",
              "button_legacy",
              "list",
              "poll",
              "carousel"
            ],
            "example": "list"
          },
          "text": {
            "type": "string",
            "description": "Texto principal (aceita placeholders)",
            "example": "Escolha uma opção:"
          },
          "footerText": {
            "type": "string",
            "description": "Texto do rodapé (opcional)",
            "example": "Menu de serviços"
          },
          "listButton": {
            "type": "string",
            "description": "Texto do botão principal",
            "example": "Ver opções"
          },
          "selectableCount": {
            "type": "integer",
            "description": "Número máximo de opções selecionáveis (para enquetes)",
            "example": 1
          },
          "choices": {
            "type": "array",
            "description": "Lista de opções. Use [Título] para seções em listas",
            "items": {
              "type": "string"
            },
            "example": [
              "[Eletrônicos]",
              "Smartphones|phones|Últimos lançamentos",
              "Notebooks|notes|Modelos 2024",
              "[Acessórios]",
              "Fones|fones|Bluetooth e com fio",
              "Capas|cases|Proteção para seu device"
            ]
          },
          "imageButton": {
            "type": "string",
            "description": "URL da imagem para botões (recomendado para type: button)",
            "example": "https://exemplo.com/imagem-botao.jpg"
          },
          "replyid": {
            "type": "string",
            "description": "ID da mensagem para responder",
            "example": "3EB0538DA65A59F6D8A251"
          },
          "mentions": {
            "type": "string",
            "description": "Números para mencionar (separados por vírgula)",
            "example": "5511999999999,5511888888888"
          },
          "readchat": {
            "type": "boolean",
            "description": "Marca conversa como lida após envio",
            "example": true
          },
          "readmessages": {
            "type": "boolean",
            "description": "Marca últimas mensagens recebidas como lidas",
            "example": true
          },
          "delay": {
            "type": "integer",
            "description": "Atraso em milissegundos antes do envio, durante o atraso apacerá 'Digitando...'",
            "example": 1000
          },
          "track_source": {
            "type": "string",
            "description": "Origem do rastreamento da mensagem",
            "example": "chatwoot"
          },
          "track_id": {
            "type": "string",
            "description": "ID para rastreamento da mensagem (aceita valores duplicados)",
            "example": "msg_123456789"
          },
          "async": {
            "type": "boolean",
            "description": "Se true, envia a mensagem de forma assíncrona via fila interna",
            "example": false
          }
        },
        "required": [
          "number",
          "type",
          "text",
          "choices"
        ]
      },
      "examples": {
        "button": {
          "summary": "Botões de resposta",
          "value": {
            "number": "5511999999999",
            "type": "button",
            "text": "Como podemos ajudar?",
            "choices": [
              "Suporte|suporte",
              "Fazer pedido|pedido"
            ]
          }
        },
        "list": {
          "summary": "Lista de opções",
          "value": {
            "number": "5511999999999",
            "type": "list",
            "text": "Escolha uma categoria",
            "listButton": "Ver opções",
            "choices": [
              "[Atendimento]",
              "Suporte|suporte|Falar com o suporte",
              "Financeiro|financeiro|Falar com o financeiro"
            ]
          }
        },
        "poll": {
          "summary": "Enquete com uma escolha",
          "value": {
            "number": "5511999999999",
            "type": "poll",
            "text": "Qual horário prefere?",
            "selectableCount": 1,
            "choices": [
              "Manhã",
              "Tarde"
            ]
          }
        }
      }
    }
  }
}

Respostas

{
  "200": {
    "description": "Menu enviado com sucesso",
    "content": {
      "application/json": {
        "schema": {
          "allOf": [
            {
              "$ref": "#/components/schemas/Message"
            },
            {
              "type": "object",
              "properties": {
                "response": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "example": "success"
                    },
                    "message": {
                      "type": "string",
                      "example": "Menu sent successfully"
                    }
                  }
                }
              }
            }
          ]
        }
      }
    }
  },
  "400": {
    "description": "Requisição inválida",
    "content": {
      "application/json": {
        "schema": {
          "type": "object",
          "properties": {
            "error": {
              "type": "string",
              "example": "Missing required fields or invalid menu type"
            }
          }
        }
      }
    }
  },
  "401": {
    "description": "Não autorizado",
    "content": {
      "application/json": {
        "schema": {
          "type": "object",
          "properties": {
            "error": {
              "type": "string",
              "example": "Invalid token"
            }
          }
        }
      }
    }
  },
  "429": {
    "description": "Limite de requisições excedido",
    "content": {
      "application/json": {
        "schema": {
          "type": "object",
          "properties": {
            "error": {
              "type": "string",
              "example": "Rate limit exceeded"
            }
          }
        }
      }
    }
  },
  "500": {
    "description": "Erro interno do servidor",
    "content": {
      "application/json": {
        "schema": {
          "type": "object",
          "properties": {
            "error": {
              "type": "string",
              "example": "Failed to send menu"
            }
          }
        }
      }
    }
  }
}

#/components/schemas/Message

{
  "type": "object",
  "description": "Representa uma mensagem trocada no sistema",
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid",
      "description": "ID único interno da mensagem (formato r + 7 caracteres hex aleatórios)"
    },
    "messageid": {
      "type": "string",
      "description": "ID original da mensagem no provedor"
    },
    "chatid": {
      "type": "string",
      "description": "ID da conversa relacionada"
    },
    "sender": {
      "type": "string",
      "description": "ID do remetente da mensagem"
    },
    "senderName": {
      "type": "string",
      "description": "Nome exibido do remetente"
    },
    "isGroup": {
      "type": "boolean",
      "description": "Indica se é uma mensagem de grupo",
      "default": false
    },
    "fromMe": {
      "type": "boolean",
      "description": "Indica se a mensagem foi enviada pelo usuário",
      "default": false
    },
    "messageType": {
      "type": "string",
      "description": "Tipo de conteúdo da mensagem"
    },
    "source": {
      "type": "string",
      "description": "Plataforma de origem da mensagem"
    },
    "messageTimestamp": {
      "type": "integer",
      "description": "Timestamp original da mensagem em milissegundos",
      "default": 0
    },
    "status": {
      "type": "string",
      "description": "Status do ciclo de vida da mensagem.\nExemplos comuns: `Queued`, `Canceled`, `Failed`, `Sent`, `Delivered`, `Read`.\n"
    },
    "text": {
      "type": "string",
      "description": "Texto original da mensagem",
      "default": ""
    },
    "quoted": {
      "type": "string",
      "description": "ID da mensagem citada/respondida",
      "default": ""
    },
    "edited": {
      "type": "string",
      "description": "Histórico de edições da mensagem",
      "default": ""
    },
    "reaction": {
      "type": "string",
      "description": "ID da mensagem reagida",
      "default": ""
    },
    "vote": {
      "type": "string",
      "description": "Dados de votação de enquete e listas",
      "default": ""
    },
    "convertOptions": {
      "type": "string",
      "description": "Conversão de opções da mensagem, lista, enquete e botões",
      "default": ""
    },
    "buttonOrListid": {
      "type": "string",
      "description": "ID do botão ou item de lista selecionado",
      "default": ""
    },
    "owner": {
      "type": "string",
      "description": "Dono da mensagem",
      "default": ""
    },
    "error": {
      "type": "string",
      "description": "Mensagem de erro caso o envio tenha falhado",
      "default": ""
    },
    "content": {
      "description": "Conteúdo bruto da mensagem (JSON serializado ou texto)",
      "oneOf": [
        {
          "type": "object",
          "additionalProperties": true
        },
        {
          "type": "string",
          "description": "Texto bruto quando não for JSON"
        }
      ]
    },
    "wasSentByApi": {
      "type": "boolean",
      "description": "Indica se a mensagem foi enviada via API"
    },
    "sendFunction": {
      "type": "string",
      "description": "Função usada para enviar a mensagem (quando enviada via API)"
    },
    "sendPayload": {
      "description": "Payload usado no envio quando disponível. Dados de mídia em base64 são omitidos.",
      "oneOf": [
        {
          "type": "object",
          "additionalProperties": true
        },
        {
          "type": "string",
          "description": "Texto bruto quando não for JSON"
        }
      ]
    },
    "fileURL": {
      "type": "string",
      "description": "URL ou referência de arquivo da mensagem"
    },
    "callPeer": {
      "type": "object",
      "description": "Contato conhecido associado a uma ligação, quando disponível em `messageType: call`.",
      "properties": {
        "jid": {
          "type": "string"
        },
        "name": {
          "type": "string"
        },
        "imagePreviewUrl": {
          "type": "string"
        }
      }
    },
    "send_folder_id": {
      "type": "string",
      "description": "Pasta associada ao envio (quando aplicável)"
    },
    "track_source": {
      "type": "string",
      "description": "Origem de rastreamento"
    },
    "track_id": {
      "type": "string",
      "description": "ID de rastreamento (pode repetir)"
    },
    "ai_metadata": {
      "type": "object",
      "description": "Metadados do processamento por IA",
      "properties": {
        "agent_id": {
          "type": "string",
          "description": "ID do agente de IA responsável"
        },
        "request": {
          "type": "object",
          "description": "Dados da requisição à API de IA",
          "properties": {
            "messages": {
              "type": "array",
              "description": "Histórico de mensagens enviadas para a API"
            },
            "tools": {
              "type": "array",
              "description": "Ferramentas disponíveis para o agente"
            },
            "options": {
              "type": "object",
              "description": "Opções de configuração da API",
              "properties": {
                "model": {
                  "type": "string"
                },
                "temperature": {
                  "type": "number"
                },
                "maxTokens": {
                  "type": "integer"
                },
                "topP": {
                  "type": "number"
                },
                "frequencyPenalty": {
                  "type": "number"
                },
                "presencePenalty": {
                  "type": "number"
                }
              }
            }
          }
        },
        "response": {
          "type": "object",
          "description": "Resposta da API de IA",
          "properties": {
            "choices": {
              "type": "array",
              "description": "Resultados retornados pela API"
            },
            "toolResults": {
              "type": "array",
              "description": "Resultados da execução de ferramentas"
            },
            "error": {
              "type": "string",
              "description": "Mensagem de erro, se houver"
            }
          }
        }
      }
    },
    "sender_pn": {
      "description": "JID PN resolvido do remetente (quando disponível)",
      "oneOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ]
    },
    "sender_lid": {
      "description": "LID original do remetente (quando disponível)",
      "oneOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ]
    },
    "sender_image_preview_url": {
      "type": "string",
      "format": "uri",
      "description": "URL temporária de preview do remetente, quando conhecida. A hidratação desta versão preenche mensagens recebidas em grupos; o campo pode ser omitido. Não provoca consulta de foto ao WhatsApp na listagem."
    }
  }
}

Guias relacionados

Autenticação · Erros e retries · Server URL