A API do Facebook Messenger permite que os desenvolvedores criem experiências conversacionais que alcançam mais de 1,3 bilhões de usuários ativos mensais. Seja você um bot de suporte ao cliente, um assistente de e-commerce ou uma campanha de marketing interativa, a API da plataforma Messenger oferece as ferramentas necessárias para envolver os usuários em conversas significativas.
Este guia orienta você em tudo, desde a configuração inicial até recursos avançados, como modelos de mensagens, respostas rápidas e o protocolo de transferência para atendimento humano. Você encontrará exemplos práticos em TypeScript ao longo do texto, prontos para serem adaptados aos seus próprios projetos.
Introdução à Plataforma Messenger
A Plataforma Messenger é a estrutura da Meta para criar bots e integrações que se comunicam com os usuários através do Facebook Messenger. Ao contrário das interfaces web tradicionais, a API de chat do Facebook cria uma experiência conversacional onde os usuários interagem por meio de mensagens, botões e mídias ricas.
As principais funcionalidades da API do bot de mensagens incluem:
| Feature | Description | Caso de Uso |
|---|---|---|
| Mensagens de Texto | Envio e recepção básica de mensagens | Consultas de clientes, notificações |
| Modelos de Mensagens | Layouts estruturados com imagens e botões | Catálogos de produtos, recibos |
| Respostas Rápidas | Botões de resposta sugeridos | Conversas guiadas, inquéritos |
| Menu Persistente | Opções de navegação sempre disponíveis | Navegação do bot, ações comuns |
| Mensagens de Mídia | Imagens, vídeos, áudio e arquivos | Imagens do produto, tutoriais |
| Protocolo de Transferência | Transferência entre bots e agentes humanos | Escalonamento complexo de suporte |
A plataforma opera com uma arquitetura baseada em webhooks. Seu servidor recebe mensagens de entrada através de webhooks, processa-as e responde utilizando a Send API. Este modelo assíncrono permite que você crie experiências responsivas sem precisar manter conexões abertas.
Configurando a Sua App Meta
Antes de poder usar a API do Facebook Messenger, você precisa criar e configurar um App da Meta. Esse processo estabelece a identidade da sua aplicação e concede acesso à Plataforma Messenger.
Passo 1: Crie um Aplicativo Meta
Navegue até o Meta para Desenvolvedores portal e crie um novo aplicativo. Selecione "Negócios" como o tipo do seu aplicativo, que oferece acesso às funcionalidades do Messenger.
```typescript
// Variáveis de ambiente que você precisará após a configuração
interface MessengerConfig {
FACEBOOK_APP_ID: string;
FACEBOOK_APP_SECRET: string;
FACEBOOK_PAGE_ID: string;
FACEBOOK_PAGE_ACCESS_TOKEN: string;
MESSENGER_VERIFY_TOKEN: string;
}
// Valide sua configuração na inicialização
function validateConfig(): MessengerConfig {
const required = [
'FACEBOOK_APP_ID',
'FACEBOOK_APP_SECRET',
'FACEBOOK_PAGE_ID',
'FACEBOOK_PAGE_ACCESS_TOKEN',
'MESSENGER_VERIFY_TOKEN'
];
const missing = required.filter(key => !process.env[key]);
if (missing.length > 0) {
throw new Error(`Faltando variáveis de ambiente obrigatórias: ${missing.join(', ')}`);
}
return {
FACEBOOK_APP_ID: process.env.FACEBOOK_APP_ID!,
FACEBOOK_APP_SECRET: process.env.FACEBOOK_APP_SECRET!,
FACEBOOK_PAGE_ID: process.env.FACEBOOK_PAGE_ID!,
FACEBOOK_PAGE_ACCESS_TOKEN: process.env.FACEBOOK_PAGE_ACCESS_TOKEN!,
MESSENGER_VERIFY_TOKEN: process.env.MESSENGER_VERIFY_TOKEN!
};
}
```
Passo 2: Adicionar Produto Messenger
No painel do seu aplicativo, clique em "Adicionar Produto" e selecione Messenger. Isso ativa os recursos da Plataforma Messenger para o seu aplicativo.
Passo 3: Conectar uma Página do Facebook
Seu bot precisa de uma Página do Facebook para enviar e receber mensagens. Nas configurações do Messenger, clique em "Adicionar ou Remover Páginas" e selecione a página que deseja conectar. Gere um Token de Acesso da Página, que você usará para autenticar as solicitações da API.
Nota: Os Tokens de Acesso de Página podem ser de curta ou longa duração. Para aplicações em produção, troque seu token por uma versão de longa duração que dure aproximadamente 60 dias.
interface TokenExchangeResponse {
access_token: string;
token_type: string;
expires_in?: number;
}
async function exchangeForLongLivedToken(
shortLivedToken: string,
appId: string,
appSecret: string
): Promise {
const baseUrl = 'https://graph.facebook.com/v18.0';
const params = new URLSearchParams({
grant_type: 'fb_exchange_token',
client_id: appId,
client_secret: appSecret,
fb_exchange_token: shortLivedToken,
});
const response = await fetch(`${baseUrl}/oauth/access_token?${params.toString()}`);
if (!response.ok) {
const error = await response.text();
throw new Error(`Falha na troca de token: ${error}`);
}
const data = await response.json();
if (!data.access_token) {
throw new Error('Nenhum token de acesso retornado na troca de token');
}
const expiresInDays = data.expires_in
? Math.floor(data.expires_in / 86400)
: 'desconhecido';
console.log(`Token trocado com sucesso (expira em ${expiresInDays} dias)`);
return {
access_token: data.access_token,
token_type: data.token_type || 'bearer',
expires_in: data.expires_in,
};
}
Passo 4: Configurar Permissões do App
Solicite as permissões necessárias para o seu bot. No mínimo, você precisará de:
páginas_mensagens: Enviar e receber mensagenspages_gestionar_metadatos: Inscreva-se em webhookspáginas_leitura_engajamentoAcesse os dados de conversa
Configuração e Verificação de Webhook
Os webhooks são a espinha dorsal da API da plataforma de mensageiros. Eles permitem que o Facebook notifique seu servidor quando eventos ocorrem, como mensagens recebidas ou entregas de mensagens.
Configurando o Seu Endpoint de Webhook
Crie um endpoint que gerencie tanto requisições GET (para verificação) quanto requisições POST (para receber eventos).
import express, { Request, Response } from 'express';
import crypto from 'crypto';
const app = express();
// Analisar o corpo bruto para verificação de assinatura
app.use(express.json({
verify: (req: any, res, buf) => {
req.rawBody = buf;
}
}));
// Endpoint de verificação do webhook
app.get('/webhook', (req: Request, res: Response) => {
const mode = req.query['hub.mode'];
const token = req.query['hub.verify_token'];
const challenge = req.query['hub.challenge'];
const verifyToken = process.env.MESSENGER_VERIFY_TOKEN;
if (mode === 'subscribe' && token === verifyToken) {
console.log('Webhook verificado com sucesso');
res.status(200).send(challenge);
} else {
console.error('Falha na verificação do webhook');
res.sendStatus(403);
}
});
// Receptor de eventos do webhook
app.post('/webhook', (req: Request, res: Response) => {
// Verificar a assinatura da requisição
const signature = req.headers['x-hub-signature-256'] as string;
if (!verifySignature(req, signature)) {
console.error('Assinatura inválida');
return res.sendStatus(401);
}
const body = req.body;
if (body.object !== 'page') {
return res.sendStatus(404);
}
// Processar cada entrada
body.entry?.forEach((entry: any) => {
entry.messaging?.forEach((event: any) => {
handleMessagingEvent(event);
});
});
// Sempre responder rapidamente com 200 OK
res.sendStatus(200);
});
function verifySignature(req: any, signature: string): boolean {
if (!signature) return false;
const appSecret = process.env.FACEBOOK_APP_SECRET!;
const expectedSignature = 'sha256=' + crypto
.createHmac('sha256', appSecret)
.update(req.rawBody)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
}
async function handleMessagingEvent(event: any): Promise {
const senderId = event.sender.id;
if (event.message) {
await handleMessage
Inscrevendo-se em Eventos de Webhook
Após configurar seu endpoint, inscreva-se nos eventos que deseja receber. No Painel de Controle do Meta App, configure sua URL de webhook e selecione os campos de assinatura relevantes:
messages: Mensagens recebidasmensagens_postbacksCliques de botão e seleções de menuopções_de_mensagem: Consentimento do usuárioentregas_mensagensConfirmações de entregaleitura_mensagens: Confirmações de leitura
Recebendo Mensagens
Quando os usuários enviam mensagens para o seu bot, a API de chat do Facebook as entrega ao seu webhook. As mensagens podem conter texto, anexos ou ambos.
interface MessengerMessage {
mid: string;
text?: string;
attachments?: Array<{
type: 'imagem' | 'vídeo' | 'áudio' | 'arquivo' | 'localização' | 'fallback';
payload: {
url?: string;
coordinates?: {
lat: number;
long: number;
};
};
}>;
quick_reply?: {
payload: string;
};
reply_to?: {
mid: string;
};
}
async function handleMessage(
senderId: string,
message: MessengerMessage
): Promise {
console.log(`Mensagem recebida de ${senderId}:`, message);
// Lidar com respostas rápidas
if (message.quick_reply) {
await handleQuickReply(senderId, message.quick_reply.payload);
return;
}
// Lidar com anexos
if (message.attachments && message.attachments.length > 0) {
for (const attachment of message.attachments) {
await handleAttachment(senderId, attachment);
}
return;
}
// Lidar com mensagens de texto
if (message.text) {
await processTextMessage(senderId, message.text);
}
}
async function handleAttachment(
senderId: string,
attachment: MessengerMessage['attachments'][0]
): Promise {
switch (attachment.type) {
case 'imagem':
await sendTextMessage(senderId, `Obrigado pela imagem! Recebi: ${attachment.payload.url}`);
break;
case 'localização':
const { lat, long } = attachment.payload.coordinates!;
await sendTextMessage(senderId, `Recebi sua localização: ${lat}, ${long}`);
break;
default:
await sendTextMessage(senderId, `Recebi seu ${attachment.type}`);
}
}
async function processTextMessage(
senderId: string,
text: string
): Promise {
const lowerText = text.toLowerCase();
if (lowerText.includes('ajuda')) {
await sendHelpMessage(senderId);
} else if (lowerText.includes('produtos')) {
await sendProductCatalog(senderId);
} else {
await sendTextMessage(senderId, `Você disse: "${text}". Como posso ajudar você hoje?`);
}
}
Envio de Mensagens de Texto
A API Send é a sua ferramenta principal para interagir com os usuários através da API do bot de mensagens. As mensagens de texto são a forma mais simples de resposta.
const GRAPH_API_URL = 'https://graph.facebook.com/v18.0';
interface SendMessageResponse {
recipient_id: string;
message_id: string;
}
interface SendMessageError {
message: string;
type: string;
code: number;
error_subcode?: number;
fbtrace_id: string;
}
async function sendTextMessage(
recipientId: string,
text: string
): Promise {
const pageAccessToken = process.env.FACEBOOK_PAGE_ACCESS_TOKEN!;
const response = await fetch(
`${GRAPH_API_URL}/me/messages?access_token=${pageAccessToken}`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
recipient: { id: recipientId },
message: { text },
messaging_type: 'RESPONSE'
}),
}
);
const data = await response.json();
if (!response.ok) {
const error = data.error as SendMessageError;
throw new Error(`Falha ao enviar: ${error.message} (código: ${error.code})`);
}
return data as SendMessageResponse;
}
// Enviar indicador de digitação para melhor experiência do usuário
async function sendTypingIndicator(
recipientId: string,
isTyping: boolean
): Promise {
const pageAccessToken = process.env.FACEBOOK_PAGE_ACCESS_TOKEN!;
await fetch(
`${GRAPH_API_URL}/me/messages?access_token=${pageAccessToken}`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
recipient: { id: recipientId },
sender_action: isTyping ? 'typing_on' : 'typing_off'
}),
}
);
}
// Função auxiliar para enviar mensagens com indicador de digitação
async function sendMessageWithTyping(
recipientId: string,
text: string,
typingDuration: number = 1000
): Promise {
await sendTypingIndicator(recipientId, true);
await new Promise(resolve => setTimeout(resolve, typingDuration));
return sendTextMessage(recipientId, text);
}
Modelos de Mensagem (Genérico, Botão, Recibo)
Os modelos de mensagem criam experiências ricas e interativas que vão além do texto simples. A API do Facebook Messenger suporta vários tipos de modelos, cada um projetado para casos de uso específicos.
Modelo Genérico
O Modelo Genérico exibe um carrossel de itens, ideal para listas de produtos ou feeds de conteúdo.
```typescript
interface GenericTemplateElement {
title: string;
subtitle?: string;
image_url?: string;
default_action?: {
type: 'web_url';
url: string;
webview_height_ratio?: 'compact' | 'tall' | 'full';
};
buttons?: Array;
}
type TemplateButton =
| { type: 'web_url'; url: string; title: string }
| { type: 'postback'; title: string; payload: string }
| { type: 'phone_number'; title: string; payload: string }
| { type: 'account_link'; url: string }
| { type: 'account_unlink' };
async function sendGenericTemplate(
recipientId: string,
elements: GenericTemplateElement[]
): Promise {
const pageAccessToken = process.env.FACEBOOK_PAGE_ACCESS_TOKEN!;
// Limitar a 10 elementos por carrossel
const limitedElements = elements.slice(0, 10);
const response = await fetch(
`${GRAPH_API_URL}/me/messages?access_token=${pageAccessToken}`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
recipient: { id: recipientId },
message: {
attachment: {
type: 'template',
payload: {
template_type: 'generic',
elements: limitedElements
}
}
},
messaging_type: 'RESPONSE'
}),
}
);
if (!response.ok) {
const error = await response.json();
throw new Error(`Falha ao enviar o template: ${error.error?.message}`);
}
return response.json();
}
// Exemplo: Catálogo de produtos
async function sendProductCatalog(recipientId: string): Promise {
const products: GenericTemplateElement[] = [
{
title: 'Fones de Ouvido Sem Fio',
subtitle: '$149.99 - Qualidade de som premium',
image_url: 'https://example.com/headphones.jpg',
default_action: {
type: 'web_url',
url: 'https://example.com/products/headphones'
},
buttons: [
{ type: 'postback', title: 'Comprar Agora', payload: 'BUY_HEADPH
Modelo de Botão
O Modelo de Botão apresenta uma mensagem de texto com até três botões, ideal para escolhas simples.
```javascript
async function sendButtonTemplate(
recipientId: string,
text: string,
buttons: TemplateButton[]
): Promise {
const pageAccessToken = process.env.FACEBOOK_PAGE_ACCESS_TOKEN!;
// Limitar a 3 botões
const limitedButtons = buttons.slice(0, 3);
const response = await fetch(
`${GRAPH_API_URL}/me/messages?access_token=${pageAccessToken}`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
recipient: { id: recipientId },
message: {
attachment: {
type: 'template',
payload: {
template_type: 'button',
text,
buttons: limitedButtons
}
}
},
messaging_type: 'RESPONSE'
}),
}
);
if (!response.ok) {
const error = await response.json();
throw new Error(`Falha no template de botão: ${error.error?.message}`);
}
return response.json();
}
// Exemplo: Menu de ajuda
async function sendHelpMessage(recipientId: string): Promise {
await sendButtonTemplate(
recipientId,
'Como posso ajudar você hoje?',
[
{ type: 'postback', title: 'Navegar Produtos', payload: 'BROWSE_PRODUCTS' },
{ type: 'postback', title: 'Rastrear Pedido', payload: 'TRACK_ORDER' },
{ type: 'postback', title: 'Contactar Suporte', payload: 'CONTACT_SUPPORT' }
]
);
}
```
Modelo de Recibo
O Modelo de Recibo exibe confirmações de pedidos com detalhes itemizados.
interface ReceiptElement {
title: string;
subtitle?: string;
quantity?: number;
price: number;
currency?: string;
image_url?: string;
}
interface ReceiptSummary {
subtotal?: number;
shipping_cost?: number;
total_tax?: number;
total_cost: number;
}
interface ReceiptAddress {
street_1: string;
street_2?: string;
city: string;
postal_code: string;
state: string;
country: string;
}
async function sendReceiptTemplate(
recipientId: string,
receipt: {
recipientName: string;
orderNumber: string;
currency: string;
paymentMethod: string;
orderUrl?: string;
timestamp?: string;
address?: ReceiptAddress;
elements: ReceiptElement[];
summary: ReceiptSummary;
}
): Promise<SendMessageResponse> {
const pageAccessToken = process.env.FACEBOOK_PAGE_ACCESS_TOKEN!;
const response = await fetch(
`${GRAPH_API_URL}/me/messages?access_token=${pageAccessToken}`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
recipient: { id: recipientId },
message: {
attachment: {
type: 'template',
payload: {
template_type: 'receipt',
recipient_name: receipt.recipientName,
order_number: receipt.orderNumber,
currency: receipt.currency,
payment_method: receipt.paymentMethod,
order_url: receipt.orderUrl,
timestamp: receipt.timestamp,
address: receipt.address,
elements: receipt.elements,
summary: receipt.summary
}
}
},
messaging_type: 'RESPONSE'
}),
}
);
if (!response.ok) {
const error = await response.json();
throw new Error(`Falha ao enviar o template de recibo: ${error.error?.message}`);
}
return response.json();
}
// Exemplo: Confirmação de pedido
async function sendOrderConfirmation(
recipientId: string,
orderId: string
): Promise<void> {
await sendReceiptTemplate(recipientId, {
recipientName: 'João Silva
Respostas Rápidas e Menu Persistente
As Respostas Rápidas e o Menu Persistente guiam os usuários em conversas, apresentando opções claras.
Respostas Rápidas
As Respostas Rápidas aparecem como botões acima do teclado, oferecendo respostas sugeridas que desaparecem após a seleção.
interface QuickReply {
content_type: 'text' | 'user_phone_number' | 'user_email';
title?: string;
payload?: string;
image_url?: string;
}
async function sendQuickReplies(
recipientId: string,
text: string,
quickReplies: QuickReply[]
): Promise {
const pageAccessToken = process.env.FACEBOOK_PAGE_ACCESS_TOKEN!;
// Limitar a 13 respostas rápidas
const limitedReplies = quickReplies.slice(0, 13);
const response = await fetch(
`${GRAPH_API_URL}/me/messages?access_token=${pageAccessToken}`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
recipient: { id: recipientId },
message: {
text,
quick_replies: limitedReplies
},
messaging_type: 'RESPONSE'
}),
}
);
if (!response.ok) {
const error = await response.json();
throw new Error(`Falha nas respostas rápidas: ${error.error?.message}`);
}
return response.json();
}
// Exemplo: Pesquisa de satisfação
async function sendSatisfactionSurvey(recipientId: string): Promise {
await sendQuickReplies(
recipientId,
'Como você avaliaria sua experiência hoje?',
[
{ content_type: 'text', title: '😄 Ótimo', payload: 'RATING_5' },
{ content_type: 'text', title: '🙂 Bom', payload: 'RATING_4' },
{ content_type: 'text', title: '😐 Regular', payload: 'RATING_3' },
{ content_type: 'text', title: '😕 Ruim', payload: 'RATING_2' },
{ content_type: 'text', title: '😞 Péssimo', payload: 'RATING_1' }
]
);
}
async function handleQuickReply(
senderId: string,
payload: string
): Promise {
if (payload.startsWith('RATING_')) {
const rating = parseInt(payload.split('_')[1]);
await sendTextMessage(
senderId,
`Obrigado pelo seu feedback! Você nos avaliou com ${rating}/5.`
);
}
Menu Persistente
O Menu Persistente oferece uma navegação sempre disponível através do ícone de hambúrguer.
interface MenuItem {
type: 'postback' | 'web_url' | 'nested';
title: string;
payload?: string;
url?: string;
webview_height_ratio?: 'compact' | 'tall' | 'full';
call_to_actions?: MenuItem[];
}
async function setPersistentMenu(
menuItems: MenuItem[],
locale: string = 'default'
): Promise {
const pageAccessToken = process.env.FACEBOOK_PAGE_ACCESS_TOKEN!;
const response = await fetch(
`${GRAPH_API_URL}/me/messenger_profile?access_token=${pageAccessToken}`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
persistent_menu: [
{
locale,
composer_input_disabled: false,
call_to_actions: menuItems
}
]
}),
}
);
if (!response.ok) {
const error = await response.json();
throw new Error(`Falha na configuração do menu persistente: ${error.error?.message}`);
}
console.log('Menu persistente configurado com sucesso');
}
// Exemplo: Menu de bot para e-commerce
async function setupBotMenu(): Promise {
await setPersistentMenu([
{
type: 'postback',
title: '🛍️ Compre Agora',
payload: 'SHOP_NOW'
},
{
type: 'nested',
title: '📦 Meus Pedidos',
call_to_actions: [
{ type: 'postback', title: 'Rastrear Pedido', payload: 'TRACK_ORDER' },
{ type: 'postback', title: 'Histórico de Pedidos', payload: 'ORDER_HISTORY' },
{ type: 'postback', title: 'Devoluções', payload: 'RETURNS' }
]
},
{
type: 'postback',
title: '💬 Contatar Suporte',
payload: 'CONTACT_SUPPORT'
}
]);
}
Mensagens de Mídia (Imagens, Vídeos, Arquivos)
A API da plataforma de mensageiros suporta anexos de mídia rica, permitindo que você envie imagens, vídeos, arquivos de áudio e documentos.
type AttachmentType = 'imagem' | 'vídeo' | 'áudio' | 'arquivo';
interface AttachmentPayload {
url?: string;
is_reusable?: boolean;
attachment_id?: string;
}
async function sendMediaAttachment(
recipientId: string,
type: AttachmentType,
payload: AttachmentPayload
): Promise {
const pageAccessToken = process.env.FACEBOOK_PAGE_ACCESS_TOKEN!;
const response = await fetch(
`${GRAPH_API_URL}/me/messages?access_token=${pageAccessToken}`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
recipient: { id: recipientId },
message: {
attachment: {
type,
payload
}
},
messaging_type: 'RESPONSE'
}),
}
);
if (!response.ok) {
const error = await response.json();
throw new Error(`Falha ao enviar mídia: ${error.error?.message}`);
}
return response.json();
}
// Enviar imagem por URL
async function sendImage(
recipientId: string,
imageUrl: string,
reusable: boolean = true
): Promise {
return sendMediaAttachment(recipientId, 'imagem', {
url: imageUrl,
is_reusable: reusable
});
}
// Enviar vídeo por URL
async function sendVideo(
recipientId: string,
videoUrl: string
): Promise {
return sendMediaAttachment(recipientId, 'vídeo', {
url: videoUrl,
is_reusable: true
});
}
// Enviar arquivo/documento
async function sendFile(
recipientId: string,
fileUrl: string
): Promise {
return sendMediaAttachment(recipientId, 'arquivo', {
url: fileUrl,
is_reusable: true
});
}
// Fazer upload de anexo para reutilização
async function uploadAttachment(
type: AttachmentType,
url: string
): Promise {
const pageAccessToken = process.env.FACEBOOK_PAGE_ACCESS_TOKEN!;
const response = await fetch(
`${GRAPH_API_URL}/me/message_attachments?access_token=${pageAccessToken}`,
{
method: 'POST',
headers:
Nota: Os arquivos de mídia devem ser acessíveis via HTTPS. O Facebook baixa o arquivo do seu URL, portanto, certifique-se de que seu servidor consegue lidar com o tráfego. Para mídias usadas com frequência, faça o upload uma vez e reutilize o ID do anexo.
Protocolo de Transferência Humana
O Protocolo de Transferência permite transições suaves entre bots automatizados e agentes humanos. Isso é essencial para cenários de suporte complexos que exigem intervenção humana.
type ThreadOwner = 'primary' | 'secondary';
interface HandoverMetadata {
reason?: string;
conversationHistory?: string;
customData?: Record;
}
// Pass thread control to another app (e.g., human agent inbox)
async function passThreadControl(
recipientId: string,
targetAppId: string,
metadata?: HandoverMetadata
): Promise {
const pageAccessToken = process.env.FACEBOOK_PAGE_ACCESS_TOKEN!;
const response = await fetch(
`${GRAPH_API_URL}/me/pass_thread_control?access_token=${pageAccessToken}`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
recipient: { id: recipientId },
target_app_id: targetAppId,
metadata: metadata ? JSON.stringify(metadata) : undefined
}),
}
);
if (!response.ok) {
const error = await response.json();
throw new Error(`Falha na transferência de controle do thread: ${error.error?.message}`);
}
console.log(`Controle do thread transferido para o app ${targetAppId}`);
}
// Take thread control back from secondary receiver
async function takeThreadControl(
recipientId: string,
metadata?: HandoverMetadata
): Promise {
const pageAccessToken = process.env.FACEBOOK_PAGE_ACCESS_TOKEN!;
const response = await fetch(
`${GRAPH_API_URL}/me/take_thread_control?access_token=${pageAccessToken}`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
recipient: { id: recipientId },
metadata: metadata ? JSON.stringify(metadata) : undefined
}),
}
);
if (!response.ok) {
const error = await response.json();
throw new Error(`Falha ao retomar o controle do thread: ${error.error?.message}`);
}
console.log('Controle do thread retomado');
}
// Request thread control from primary receiver
async function requestThreadControl(
recipientId: string,
metadata?: HandoverMetadata
): Promise {
const pageAccessToken = process.env.FACEBOOK_PAGE_ACCESS_TOKEN!;
const response = await fetch(
`${GRAPH_API_URL}/me/request_thread_control?access_token=${pageAccess
Etiquetas de Mensagens para Acompanhamentos
Fora da janela de mensagens de 24 horas, você precisa de Etiquetas de Mensagem para enviar mensagens de acompanhamento. Essas etiquetas indicam o propósito da sua mensagem e devem ser usadas de forma adequada.
type MessageTag =
| 'CONFIRMED_EVENT_UPDATE'
| 'POST_PURCHASE_UPDATE'
| 'ACCOUNT_UPDATE'
| 'HUMAN_AGENT';;
async function sendTaggedMessage(
recipientId: string,
text: string,
tag: MessageTag
): Promise {
const pageAccessToken = process.env.FACEBOOK_PAGE_ACCESS_TOKEN!;
const response = await fetch(
`${GRAPH_API_URL}/me/messages?access_token=${pageAccessToken}`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
recipient: { id: recipientId },
message: { text },
messaging_type: 'MESSAGE_TAG',
tag
}),
}
);
if (!response.ok) {
const error = await response.json();
throw new Error(`Falha ao enviar mensagem marcada: ${error.error?.message}`);
}
return response.json();
}
// Exemplo: Atualização de envio de pedido
async function sendShippingUpdate(
recipientId: string,
orderId: string,
trackingNumber: string
): Promise {
await sendTaggedMessage(
recipientId,
`Ótimas notícias! Seu pedido #${orderId} foi enviado. Acompanhe aqui: https://example.com/track/${trackingNumber}`,
'POST_PURCHASE_UPDATE'
);
}
// Exemplo: Alerta de segurança da conta
async function sendSecurityAlert(
recipientId: string,
alertType: string
): Promise {
await sendTaggedMessage(
recipientId,
`Alerta de Segurança: ${alertType}. Se não foi você, por favor, proteja sua conta imediatamente.`,
'ACCOUNT_UPDATE'
);
}
// Exemplo: Acompanhamento de agente humano (dentro de 7 dias da mensagem do usuário)
async function sendHumanAgentFollowup(
recipientId: string,
message: string
): Promise {
await sendTaggedMessage(
recipientId,
message,
'HUMAN_AGENT'
);
}
Nota: As etiquetas de mensagem têm políticas de uso rigorosas. O uso inadequado pode resultar na restrição ou banimento do seu bot. A etiqueta HUMAN_AGENT está disponível apenas dentro de 7 dias após a última mensagem do usuário e requer a participação de um humano na resposta.
Limites de Taxa e Melhores Práticas
A API do Facebook Messenger impõe limites de taxa para garantir a estabilidade da plataforma. Compreender esses limites ajuda você a criar bots confiáveis.
Visão Geral do Limite de Taxa
| Tipo de Limite | Threshold | Window |
|---|---|---|
| Chamadas por página | 200 chamadas | Por hora por utilizador |
| Solicitações em lote | 50 solicitações | Por lote |
| Enviar API | 250 mensagens | Por segundo por página |
Gerenciando Limites de Taxa
interface EstadoLimiteTaxa {
contagemRequisicoes: number;
inicioJanela: number;
estaLimitado: boolean;
}
class LimitadorTaxa {
private estado: Map = new Map();
private readonly maxRequisicoes: number;
private readonly janelaMs: number;
constructor(maxRequisicoes: number = 200, janelaMs: number = 3600000) {
this.maxRequisicoes = maxRequisicoes;
this.janelaMs = janelaMs;
}
async verificarLimite(idPagina: string): Promise {
const agora = Date.now();
let estado = this.estado.get(idPagina);
if (!estado || agora - estado.inicioJanela > this.janelaMs) {
estado = { contagemRequisicoes: 0, inicioJanela: agora, estaLimitado: false };
}
if (estado.contagemRequisicoes >= this.maxRequisicoes) {
estado.estaLimitado = true;
this.estado.set(idPagina, estado);
return false;
}
estado.contagemRequisicoes++;
this.estado.set(idPagina, estado);
return true;
}
obterTempoReset(idPagina: string): number {
const estado = this.estado.get(idPagina);
if (!estado) return 0;
return Math.max(0, this.janelaMs - (Date.now() - estado.inicioJanela));
}
}
const limitadorTaxa = new LimitadorTaxa();
async function enviarComLimiteTaxa(
idPagina: string,
idDestinatario: string,
texto: string
): Promise {
const podeProsseguir = await limitadorTaxa.verificarLimite(idPagina);
if (!podeProsseguir) {
const tempoReset = limitadorTaxa.obterTempoReset(idPagina);
console.warn(`Limite de taxa atingido. Reset em ${Math.ceil(tempoReset / 1000)}s`);
return null;
}
return enviarMensagemTexto(idDestinatario, texto);
}
Melhores Práticas
- Responda rapidamenteReconheça as mensagens em até 20 segundos para evitar erros de timeout.
- Utilize indicadores de digitaçãoMostre aos usuários que seu bot está processando a solicitação deles.
- Gerencie erros de forma elegante: Forneça mensagens de erro úteis quando algo der errado
- Respeite as preferências dos usuáriosAtenda a pedidos de exclusão e ações de cancelamento de inscrição.
- Armazene os IDs dos anexos em cacheCarregue a mídia uma vez e reutilize o ID do anexo.
- Operações em loteAgrupe múltiplas solicitações sempre que possível.
- Implemente lógica de repetiçãoGerencie falhas transitórias com retrocesso exponencial
async function sendWithRetry(
recipientId: string,
text: string,
maxRetries: number = 3
): Promise<SendMessageResponse> {
let lastError: Error | null = null;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await sendTextMessage(recipientId, text);
} catch (error) {
lastError = error as Error;
// Verifica se o erro pode ser tentado novamente
const errorMessage = lastError.message.toLowerCase();
const isRetryable =
errorMessage.includes('timeout') ||
errorMessage.includes('temporariamente indisponível') ||
errorMessage.includes('limite de taxa');
if (!isRetryable || attempt === maxRetries) {
throw lastError;
}
// Atraso exponencial
const delay = Math.pow(2, attempt) * 1000;
console.log(`Tentativa de reenvio ${attempt}/${maxRetries} em ${delay}ms`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
throw lastError;
}
Usando o Late para Integração com o Messenger
Construir e manter uma integração com o Messenger requer o manuseio de webhooks, a gestão de tokens, o processamento de diversos tipos de mensagens e a atualização constante em relação às mudanças da API. Late simplifica todo esse processo com uma API unificada que funciona em várias plataformas de mensagens.
Por que usar o Late para Messenger?
Em vez de construir manipuladores de webhook personalizados, gerenciamento de tokens e formatação de mensagens para cada plataforma, o Late oferece:
- API UnificadaUm único ponto de acesso para enviar mensagens para Messenger, WhatsApp, Instagram e muito mais.
- Atualização automática de tokenTokens de longa duração geridos automaticamente
- Agregação de WebhooksUm endpoint de webhook para todas as plataformas
- Abstração de modelosEscreva uma vez, exiba corretamente em cada plataforma.
- Limitação de taxa integradaGerenciamento automático dos limites da plataforma
- Normalização de errosRespostas de erro consistentes em todas as plataformas
Comece Rápido com o Late
```javascript
import { LateClient } from '@anthropic/late-sdk';
const late = new LateClient({
apiKey: process.env.LATE_API_KEY!
});
// Enviar uma mensagem para o Messenger (ou qualquer plataforma)
async function sendMessage(
channelId: string,
recipientId: string,
content: string
): Promise {
await late.messages.send({
channel: channelId,
recipient: recipientId,
content: {
type: 'text',
text: content
}
});
}
// Enviar um template que funcione em várias plataformas
async function sendProductCard(
channelId: string,
recipientId: string,
product: { name: string; price: number; imageUrl: string }
): Promise {
await late.messages.send({
channel: channelId,
recipient: recipientId,
content: {
type: 'card',
title: product.name,
subtitle: `€${product.price.toFixed(2)}`,
imageUrl: product.imageUrl,
buttons: [
{ type: 'postback', title: 'Comprar Agora', payload: 'BUY' },
{ type: 'url', title: 'Detalhes', url: 'https://example.com' }
]
}
});
}
```
Late cuida automaticamente da formatação específica de cada plataforma. O mesmo código funciona para Messenger, Instagram Direct, WhatsApp Business e outras plataformas suportadas.
Gerenciando Mensagens Recebidas
Late agrega webhooks de todas as plataformas conectadas em um único ponto de extremidade:
// Manipulador de webhook único para todas as plataformas
app.post('/late-webhook', async (req, res) => {
const event = req.body;
// Manipulação de mensagens independente da plataforma
if (event.type === 'message') {
const { platform, channelId, senderId, content } = event;
console.log(`Mensagem de ${platform}: ${content.text}`);
// Responder usando a API unificada do Late
await late.messages.send({
channel: channelId,
recipient: senderId,
content: {
type: 'text',
text: `Obrigado pela sua mensagem!`
}
});
}
res.sendStatus(200);
});
Começando
- Inscreva-se em getlate.dev
- Conecte a sua Página do Facebook no painel de controle.
- Utilize a API unificada do Late para enviar e receber mensagens.
A documentação do Late inclui guias detalhados para Integração com o Messenger, configuração de webhook, e modelos de mensagem.
Construir experiências de conversação não deve exigir a manutenção de bases de código separadas para cada plataforma. Com o Late, você escreve a lógica do seu bot uma única vez e alcança os usuários onde quer que eles prefiram se comunicar.