POST /send-message dispara uma comunicação cadastrada no painel para um destinatário — em todos os canais ativos dela ou só nos que você escolher. O conteúdo de cada canal vem da versão publicada do template da comunicação; a sua integração só informa quem recebe e os valores das variáveis.
Antes de começar, obtenha um access token em Autenticação.
channels do exemplo restringe o envio a esses quatro canais, cada um com o dado de recipient que ele usa: e-mail (email), WhatsApp (phone), Web Push e In-App (user_id). Sem channels, a mensagem sai em todos os canais ativos da comunicação para os quais o recipient tem dado.
Parâmetros do body
Campos do destinatário
Canais disponíveis
Todo canal, exceto In-App, precisa estar configurado em Configurações → Canais. Sem configuração, o envio por aquele canal nem começa: o resultado volta como
skipped, com o erro Canal não configurado e reason_code: "channel_not_configured", sem log_id e sem registro em Logs. Os outros canais da comunicação seguem normalmente. O In-App não tem configuração, mas respeita a chave Canal habilitado da página do canal: desligada, o resultado volta como skipped com o erro Canal in-app desabilitado. Veja os guias de WhatsApp, E-mail, Web Push, Slack e Telegram.
Regras de envio
Quais canais recebem
- A comunicação é procurada pelo
communication_slugentre as comunicações ativas da conta. Se não existir ou estiver inativa, a resposta é404 Communication not found. Se você desativar a comunicação no painel, as chamadas com o slug dela passam a receber esse404; se trocar o slug, as chamadas com o slug antigo também. - Sem
channels, o envio sai em todos os canais ativos da comunicação. Comchannels, só nos canais que estão na lista e ativos na comunicação — um canal pedido que a comunicação não tem ativo é ignorado. - Destinatário sem os dados de um canal:
- sem
channels, o canal é pulado em silêncio — não aparece emresultsnem nos logs. Assim uma comunicação multicanal pode ser enviada a quem só tem e-mail, por exemplo; - com
channels, o canal aparece emresultscom statusskippede umerrorque diz qual campo faltou — exceto o Slack: se o canal Slack não tiver canal padrão nem Incoming Webhook e o destinatário vier semslack_channel,slack_user_ideemail, ele é pulado em silêncio mesmo quando pedido emchannels.
- sem
Se nenhum canal tiver dados do destinatário e você não informou
channels, a resposta volta com results vazio: nada foi enviado nem registrado.Versão publicada do template
O envio usa sempre a versão publicada de cada template. No editor do painel, o que você grava com Salvar rascunho não sai pela API até você clicar em Publicar. Uma versão de idioma que ainda não foi publicada não entra na escolha do idioma. O Enviar teste do editor é o único envio que usa o rascunho.Idioma do template
Cada canal da comunicação pode ter um template por idioma. A plataforma escolhe o primeiro que existir nesta ordem:- o idioma enviado em
language(ex.:pt-BR); - a base desse idioma (ex.:
pt); - o Idioma padrão da conta;
- o Idioma de fallback da conta;
en.
skipped com o erro No template found for any language — a plataforma não escolhe um idioma ao acaso. O idioma padrão e o de fallback ficam em Configurações.
Variáveis
- Use
{{nome_da_variavel}}no template. Nomes com ponto também funcionam (ex.:{{pedido.numero}}). - Variável que o template usa mas não veio em
variablesfica no texto como está (ex.:{{nome}}) — confira os nomes antes de enviar. - No corpo HTML do e-mail, o valor das variáveis é tratado como texto: HTML enviado dentro de uma variável aparece como texto, e quebras de linha viram quebras de linha no e-mail.
- No WhatsApp e no SMS, o texto final (template com as variáveis já preenchidas) acima de 3.800 caracteres é cortado e termina com
[…continua].
Telefone
Envierecipient.phone sempre com + e o código do país (ex.: +5511999990000). Um número sem o + é lido assim no WhatsApp, no cadastro do contato e na conferência do descadastro:
No SMS, o número vai ao provedor como você enviou: use o formato internacional.
Contatos
Os envios para um destinatário com e-mail ou telefone criam ou atualizam o contato correspondente em Contatos. Quandorecipient.email é enviado, o nome do contato é preenchido com o valor da variável nome (ou name), se ela vier no envio, e o telefone, com recipient.phone na regra da seção Telefone. Um contato removido no painel não volta por causa de um envio: ele continua removido.
Resposta
Envio em lote
Envie de 1 a 100 mensagens numa única chamada com o campomessages. Cada item aceita os mesmos campos do envio único (communication_slug, recipient, variables, language, channels, tag, respect_opt_out, external_id) e pode usar uma comunicação diferente.
channels, o item sai em todos os canais ativos da comunicação para os quais o recipient tem dado.
Exemplos por canal
Um item para cada canal, com orecipient que ele exige (veja Canais disponíveis). Os itens podem ser misturados no mesmo lote. Os exemplos supõem que a comunicação boas-vindas tem todos esses canais configurados; sem a configuração, o item daquele canal volta como skipped.
- Todo item precisa de
communication_slug. Se algum não tiver, o lote inteiro é recusado com400e nada é enviado. O mesmo vale para umrespect_opt_outque não sejatruenemfalse. - Um slug inexistente ou inativo não derruba o lote: aquele item volta com
"error": "Communication not found"e os demais seguem. - Com
respect_opt_out: true, uma falha ao consultar o descadastro também fica só no item: ele volta comerrore"error_code": "opt_out_check_failed", nada é enviado para ele e os demais seguem. Reenvie só esse item. - Para listas maiores que 100, divida em lotes e faça várias chamadas.
Resposta do lote
Limite de envio por minuto
Cada conta tem um orçamento de mensagens por minuto — 60 por padrão. Cada mensagem conta 1, qualquer que seja o número de canais; num lote, cada item conta 1. Para aumentar o limite, fale com a OctaSend. O orçamento é da conta inteira: além das chamadas à API, as mensagens disparadas por automações, recuperação de carrinho e eventos das lojas integradas consomem do mesmo limite. O excedente não é recusado — ele é adiado:- Envio único: a resposta é
{"success": true, "deferred": true, "status": "queued"}, semresults. - Lote: os primeiros itens, até o que cabe no minuto, são processados na hora; os demais são adiados e aparecem no fim de
resultscomstatus: "queued"edeferred: true.
message_id (o registro só nasce quando a mensagem é reprocessada) e devolve o external_id enviado. As mensagens adiadas saem nos minutos seguintes, conforme o orçamento libera, e o resultado de cada canal aparece em Logs — exceto os canais pulados por falta de dado do destinatário, que não geram registro, como no envio imediato. Use tag para encontrá-las depois em Consultar logs. Uma mensagem adiada que não consegue sair em cerca de 24 horas é descartada.
Limite diário da conta
Se a conta tiver um Limite diário de mensagens em Configurações, cada envio a um canal conta 1: uma mensagem que sai por e-mail e WhatsApp conta 2. O dia vira à meia-noite, no fuso horário da conta; se o fuso da conta for inválido, vale o deAmerica/Sao_Paulo. Com o campo vazio, não há limite.
O limite vale para a conta inteira: envios pela API (único e lote), campanhas, automações, eventos das lojas integradas, mensagens adiadas pelo limite por minuto e o Enviar teste do painel. Respostas do chatbot ou do atendente nas conversas não contam. Mensagens da plataforma, como o código de acesso ao painel, os convites de usuário, as boas-vindas e os alertas do Token Control, também não contam nem são recusadas pelo limite.
Acima do limite, o canal não é chamado. A resposta continua HTTP 200 e o canal volta assim — no lote, dentro do item:
- O envio recusado fica em Logs com status Falhou e esse motivo.
- Canais pulados antes do envio (por falta de dado do destinatário, sem template, sem configuração ou por descadastro) não contam.
- Aumentar o limite no mesmo dia libera o saldo novo na hora.
- Os envios do dia são contados mesmo sem limite. Se você definir o limite no meio do dia, os envios que já saíram hoje entram na conta.
- Em Configurações, o painel mostra quanto já foi usado no dia e avisa quando o limite é atingido.
- Campanhas não viram uma fila de falhas: quando a conta atinge o limite no meio do envio, a campanha fica Pausada e volta a enviar sozinha à meia-noite, no fuso da conta. Veja Campanhas.
Descadastro de contatos
O contato pode se descadastrar por canal (e-mail, WhatsApp e SMS). O descadastro vale para as mensagens de marketing. As mensagens transacionais, sobre um pedido ou a conta do próprio contato, continuam chegando.
Quando o descadastro é respeitado, o canal descadastrado não é enviado e fica em Logs com o status
skipped e o motivo Contato descadastrado. Os outros canais da mesma mensagem seguem normalmente. Os e-mails de campanha e de automação de marketing levam no rodapé o link de descadastro.
Respeitar o descadastro pela API
Envierespect_opt_out: true no corpo do send-message (no lote, em cada item de messages). O contato é reconhecido pelo recipient.email, sem diferenciar maiúsculas, e pelo recipient.phone. O canal descadastrado volta assim:
- Use
reason_codepara identificar o pulo sem depender do texto deerror. - Telegram, Slack, Web Push, In-App e Push mobile não têm descadastro e não são afetados.
- O campo vale também para mensagens adiadas pelo limite por minuto.
- Se a consulta ao descadastro falhar, nada é enviado para aquela mensagem e a resposta traz
"error_code": "opt_out_check_failed": no envio único, com HTTP503; no lote, só no item afetado, e os outros itens seguem. Reenvie a mensagem.
Número do destinatário
- O envio de WhatsApp pela API aceita somente celular brasileiro: DDI 55, DDD e número de 9 dígitos começando por 6, 7, 8 ou 9. Número fixo ou de outro país volta como
failedcom o erroInvalid phone number. - Informe o número com
+55(ex.:+5511999990000). Um celular de 11 dígitos sem DDI, com DDD e o9na frente do número, com ou sem máscara (ex.:(11) 99999-0000), recebe o55automaticamente. Veja Telefone. - Número que não está registrado no WhatsApp volta como
skippedcom o erroNúmero não está registrado no WhatsApp.
Método de envio
O método de envio (Meta Cloud API ou Evolution API) é definido na conexão do canal dentro da plataforma — a integração pela API não precisa especificá-lo. Veja o guia de WhatsApp. Pela Evolution API com a fila ligada, a mensagem entra numa fila e sai em intervalos: o resultado imediato équeued, e o resultado final aparece em Logs.
- A fila respeita o Intervalo mínimo entre envios da conta, em Configurações → Canais → WhatsApp → Configurações Gerais: de 1 a 60 segundos entre uma mensagem e a próxima. Em branco, a fila segue só o limite por minuto. O intervalo de uma conta não atrasa a fila das outras. Ao mudar o intervalo, as mensagens que já esperam na fila passam a seguir o ritmo novo.
- Quando o envio falha por um problema passageiro, como o número desconectado, a fila tenta de novo até o máximo de tentativas da conta. Sem novas tentativas configuradas, a primeira falha é final.
- Depois que a mensagem sai, o registro em Logs passa para Entregue e Lida conforme o WhatsApp confirma. Veja Consultar logs.
Sequência de mensagens
O template de WhatsApp pode ter uma sequência de mensagens, com uma espera configurável depois de cada uma (padrão: 1 segundo). A resposta traz um único resultado para o canal:failed se algum item falhou; senão queued se algum foi para a fila; senão skipped se nenhum item saiu porque o número não tem WhatsApp; senão sent. Cada pausa entre mensagens enviadas na hora é limitada a 30 segundos, e a soma das pausas de uma sequência, a 60 segundos — depois disso, os itens restantes seguem sem pausa.
Mensagens ricas
Botões, listas, mídia, áudio e carrossel são configurados no template da comunicação, pelo editor do painel — o payload da API não muda: continue enviandocommunication_slug, recipient.phone e variables.
As
{{variáveis}} funcionam em todos os campos de texto do conteúdo rico (legendas, títulos, textos de botões e itens de lista).
No método Meta (Cloud API oficial), uma mensagem de texto simples, sozinha, usa o template aprovado configurado na conexão (campo Template Name), se houver um. Nesse caso:
- o destinatário recebe o texto do template aprovado na Meta, não o texto do template da comunicação;
- o idioma é o do campo Template Language da conexão (
pt_BRquando vazio); - os valores de
variablespreenchem os parâmetros{{1}},{{2}}… do template aprovado na ordem numérica das chaves — use"1","2"… como nomes. Chaves não numéricas entram depois, em ordem alfabética. Todas as chaves enviadas emvariablesviram parâmetros, então envie só as que o template aprovado usa; - o envio pode sair fora da janela de 24 horas.
In-App
O canal In-App não tem configuração de credenciais: a notificação é gravada para orecipient.user_id e aparece no widget instalado no seu produto. Veja Widget In-App.
Disparar uma automação pela API
Se a sua conta usa Automações — módulo opcional, fale com a OctaSend para ativar —, um fluxo cujo início é Chamada de API pode ser disparado pelo endpointPOST /flow-trigger, com a mesma autenticação:
trigger_slugé o Identificador do gatilho definido no início do fluxo. Só fluxos ativos são disparados.recipientprecisa de ao menos um identificador:email,phone,user_id,telegram_chat_id,slack_user_id,slack_channeloufcm_token.idempotency_keyé opcional. Chamadas repetidas com a mesma chave para o mesmo gatilho não iniciam uma nova execução — elas voltam contadas emduplicated. Sem a chave, cada chamada inicia uma execução nova.- A resposta informa quantas execuções começaram e quantas foram ignoradas por repetição, ex.:
{"started": 1, "duplicated": 0}. Mais de um fluxo ativo pode usar o mesmo identificador. Se nenhum fluxo ativo usa otrigger_slug, a resposta é404. - Cada chamada consome 1 do limite por minuto. Aqui o excedente não é adiado: a resposta é
429com{"error": "rate_limited", "retry_after": 60}. - As mensagens dessas automações são tratadas como marketing: o canal de que o contato se descadastrou é pulado, e o e-mail leva o link de descadastro no rodapé. Para uma mensagem transacional, use o
send-message. Veja Descadastro de contatos.
Erros
Erros que recusam a requisição inteira:
Erros por canal, que chegam em
results[].error com HTTP 200: