WhatsApp Oficial API
Envie mensagens pelo WhatsApp Business Platform (Cloud API da Meta) usando o número oficial verificado da sua empresa.
access_token válido
(veja a documentação de autenticação) e de um
número de WhatsApp Oficial já conectado na sua conta Atys. A conexão do número e a
criação de templates são feitas dentro da plataforma, em
Configurações → Canais — não pela API.
Como Funciona
O envio é feito com uma requisição a POST /api/api-send-message,
informando platform: ofc_whatsapp. O que você pode enviar depende das
regras da Meta: a conversa estar ou não dentro da janela de 24 horas
determina se cabe texto livre ou se é preciso usar um template.
Mensagem de sessão
Texto ou arquivo livre. Só é aceita dentro da janela de 24h.
Template
Modelo aprovado pela Meta. Funciona sempre, dentro ou fora da janela.
A janela de 24 horas
É a regra central do WhatsApp Oficial, e a principal diferença em relação ao canal não oficial. A janela abre quando o contato envia uma mensagem para o seu número e dura 24 horas a partir dessa última mensagem dele.
| Situação | Texto/arquivo livre | Template |
|---|---|---|
| Contato escreveu nas últimas 24h | Permitido | Permitido |
| Contato nunca escreveu, ou faz mais de 24h | Recusado | Permitido |
409 com uma mensagem explicando o motivo —
e nada é cobrado. Você não recebe um erro genérico da Meta.
Para descobrir se a janela está aberta antes de tentar, consulte a conversa em
GET /api/contact-channels incluindo a relação da janela:
?with=["metaOpenedWindow"]. O campo expires_at indica até quando
a sessão é válida. Na dúvida, enviar um template é sempre seguro.
1) Listar Templates Aprovados
Devolve os templates aprovados pela Meta para o canal. Use o
id retornado como meta_waba_template_id no envio.
https://api.atys.pro/api/meta-waba-templates/for-channel
Templates disponíveis para um canal oficial
Headers Obrigatórios
| Header | Valor | Descrição |
|---|---|---|
Authorization | Bearer SEU_ACCESS_TOKEN | Token de autenticação |
Accept | application/json | Tipo de resposta esperada |
Query Params
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
company_channel_id | integer | ✅ Sim | Id do canal oficial. Obtenha em GET /api/company-channels |
format dos
components (IMAGE, VIDEO ou
DOCUMENT). Eles são enviados normalmente: por padrão vai a mídia
registrada no próprio template, e você pode trocá-la a cada disparo com
header_media_url. Veja
Enviar Template.
Exemplos de Código
const response = await fetch(
'https://api.atys.pro/api/meta-waba-templates/for-channel?company_channel_id=700',
{
headers: {
'Authorization': 'Bearer SEU_ACCESS_TOKEN',
'Accept': 'application/json'
}
}
);
const { official, templates } = await response.json();
console.log(templates);
import requests
response = requests.get(
'https://api.atys.pro/api/meta-waba-templates/for-channel',
params={'company_channel_id': 700},
headers={
'Authorization': 'Bearer SEU_ACCESS_TOKEN',
'Accept': 'application/json'
}
)
data = response.json()
for template in data['templates']:
print(template['id'], template['name'])
<?php
$ch = curl_init('https://api.atys.pro/api/meta-waba-templates/for-channel?company_channel_id=700');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer SEU_ACCESS_TOKEN',
'Accept: application/json'
]);
$data = json_decode(curl_exec($ch), true);
curl_close($ch);
print_r($data['templates']);
?>
curl -X GET \ "https://api.atys.pro/api/meta-waba-templates/for-channel?company_channel_id=700" \ -H "Authorization: Bearer SEU_ACCESS_TOKEN" \ -H "Accept: application/json"
Resposta de Sucesso
{
"official": true,
"templates": [
{
"id": 144,
"name": "address_update",
"language": "pt_BR",
"has_buttons": false,
"category": "UTILITY",
"value": {
"name": "address_update",
"language": "pt_BR",
"components": [
{ "type": "HEADER", "format": "TEXT", "text": "Atualização de endereço" },
{
"type": "BODY",
"text": "Olá {{first_name}}, seu endereço foi atualizado para {{address}}."
}
]
}
}
]
}
components para saber quais variáveis o template
espera e em que formato — é o que define como preencher o envio (veja
Variáveis do Template).
O campo official confirma que o canal consultado é de WhatsApp Oficial.
Vem false, com a lista vazia e status 200, se o
company_channel_id for de outro tipo de canal — não é erro.
Erros Possíveis
| Código | Mensagem | Causa |
|---|---|---|
| 400 | company_channel_id é obrigatório | Parâmetro ausente |
| 404 | Canal não encontrado | O canal não existe ou é de outra empresa |
| 403 | Sem permissão para acessar este recurso | O usuário não tem a permissão de visualizar templates |
2) Enviar Mensagem de Sessão
Texto ou arquivo livre, para conversas dentro da janela de 24h. É o caso típico de responder a quem acabou de escrever.
https://api.atys.pro/api/api-send-message
Texto ou arquivo, dentro da janela de 24 horas
Headers Obrigatórios
| Header | Valor | Descrição |
|---|---|---|
Authorization | Bearer SEU_ACCESS_TOKEN | Token de autenticação |
Content-Type | application/json ou multipart/form-data | multipart quando enviar arquivo |
Accept | application/json | Tipo de resposta esperada |
Parâmetros do Body
| Parâmetro | Tipo | Obrigatório | Descrição | Exemplo |
|---|---|---|---|---|
platform | string | ✅ Sim | Use ofc_whatsapp para o canal oficial |
ofc_whatsapp |
sender_key | string | ✅ Sim | Número oficial da sua empresa, já conectado no Atys | 5521999999999 |
contact_key | string | ✅ Sim | Telefone do destinatário, com código do país | 5521988888888 |
text | string | ⚠️ Sim* | Texto da mensagem | Seu pedido saiu para entrega! |
file | file | ⚠️ Sim* | Arquivo (imagem, PDF, vídeo, áudio) | nota.pdf |
contact_is_group | boolean | ❌ Não | Marque true se o destino for um grupo. Default false |
false |
enqueue | boolean | ❌ Não | Default false (síncrono). Veja Resposta e Status |
false |
text OU file. Fora da janela de 24h
nenhum dos dois é aceito — nesse caso use um template.
Exemplos de Código
const response = await fetch('https://api.atys.pro/api/api-send-message', {
method: 'POST',
headers: {
'Authorization': 'Bearer SEU_ACCESS_TOKEN',
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify({
platform: 'ofc_whatsapp',
sender_key: '5521999999999',
contact_key: '5521988888888',
text: 'Seu pedido saiu para entrega!'
})
});
const data = await response.json();
// 409 = janela de 24h fechada: reenvie usando um template
if (response.status === 409) {
console.warn(data.message);
}
import requests
response = requests.post(
'https://api.atys.pro/api/api-send-message',
headers={
'Authorization': 'Bearer SEU_ACCESS_TOKEN',
'Accept': 'application/json'
},
json={
'platform': 'ofc_whatsapp',
'sender_key': '5521999999999',
'contact_key': '5521988888888',
'text': 'Seu pedido saiu para entrega!'
}
)
# 409 = janela de 24h fechada: reenvie usando um template
if response.status_code == 409:
print(response.json()['message'])
<?php
$ch = curl_init('https://api.atys.pro/api/api-send-message');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer SEU_ACCESS_TOKEN',
'Content-Type: application/json',
'Accept: application/json'
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'platform' => 'ofc_whatsapp',
'sender_key' => '5521999999999',
'contact_key' => '5521988888888',
'text' => 'Seu pedido saiu para entrega!'
]));
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
// 409 = janela de 24h fechada: reenvie usando um template
print_r(json_decode($response, true));
?>
curl -X POST https://api.atys.pro/api/api-send-message \
-H "Authorization: Bearer SEU_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"platform": "ofc_whatsapp",
"sender_key": "5521999999999",
"contact_key": "5521988888888",
"text": "Seu pedido saiu para entrega!"
}'
3) Enviar Template
Modelo previamente aprovado pela Meta. É a única forma de iniciar uma conversa ou de escrever fora da janela de 24h — e funciona dentro dela também.
https://api.atys.pro/api/api-send-message
Mesmo endpoint, informando meta_waba_template_id
Parâmetros adicionais
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
meta_waba_template_id | integer | ✅ Sim | Id vindo de Listar Templates. Substitui text |
variable_values | array | ❌ Não | Valores para variáveis posicionais ({{1}}, {{2}}), na ordem |
named_variable_values | object | ❌ Não | Valores para variáveis nomeadas ({{nome}}), por chave |
header_media_url | string | ❌ Não |
Troca a mídia do cabeçalho neste envio. URL http ou
https pública. Omitido, vale a mídia registrada no template
|
header_media_filename | string | ❌ Não |
Nome do arquivo mostrado ao destinatário. Só para cabeçalho
DOCUMENT e só junto com header_media_url;
sem ele, o nome é deduzido da URL
|
Exemplos de Código
// Template: "Olá, {{1}}, seu pedido {{2}} foi enviado."
await fetch('https://api.atys.pro/api/api-send-message', {
method: 'POST',
headers: {
'Authorization': 'Bearer SEU_ACCESS_TOKEN',
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify({
platform: 'ofc_whatsapp',
sender_key: '5521999999999',
contact_key: '5521988888888',
meta_waba_template_id: 143,
variable_values: ['Ana', 'A-1234']
})
});
# Template: "Olá {{first_name}}, seu endereço foi atualizado para {{address}}."
import requests
requests.post(
'https://api.atys.pro/api/api-send-message',
headers={
'Authorization': 'Bearer SEU_ACCESS_TOKEN',
'Accept': 'application/json'
},
json={
'platform': 'ofc_whatsapp',
'sender_key': '5521999999999',
'contact_key': '5521988888888',
'meta_waba_template_id': 144,
'named_variable_values': {
'first_name': 'Ana',
'address': 'Rua das Flores, 100'
}
}
)
<?php
$ch = curl_init('https://api.atys.pro/api/api-send-message');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Bearer SEU_ACCESS_TOKEN',
'Content-Type: application/json',
'Accept: application/json'
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'platform' => 'ofc_whatsapp',
'sender_key' => '5521999999999',
'contact_key' => '5521988888888',
'meta_waba_template_id' => 143,
'variable_values' => ['Ana', 'A-1234'],
]));
print_r(json_decode(curl_exec($ch), true));
curl_close($ch);
?>
curl -X POST https://api.atys.pro/api/api-send-message \
-H "Authorization: Bearer SEU_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"platform": "ofc_whatsapp",
"sender_key": "5521999999999",
"contact_key": "5521988888888",
"meta_waba_template_id": 143,
"variable_values": ["Ana", "A-1234"]
}'
Variáveis do Template
Um template pode ter variáveis de dois formatos, e o formato é definido quando o template
é criado. Descubra qual usar lendo os components em
Listar Templates.
| Formato | No template | Como enviar |
|---|---|---|
| Posicional | Olá, {{1}}, pedido {{2}} |
"variable_values": ["Ana", "A-1234"] — na ordem |
| Nomeado | Olá {{first_name}} |
"named_variable_values": {"first_name": "Ana"} |
_ são reservadas e ignoradas em
named_variable_values. Elas são o canal interno da mídia do cabeçalho — para
trocá-la, use o campo header_media_url, que é validado.
Mídia do cabeçalho
Templates com cabeçalho IMAGE, VIDEO ou DOCUMENT
têm uma mídia registrada na Meta quando o template é criado. Você tem duas opções:
| Situação | O que enviar | Resultado |
|---|---|---|
| Mídia fixa | Nada — omita header_media_url |
Vai a imagem/vídeo/documento registrado no template |
| Mídia por envio | "header_media_url": "https://..." |
Vai o arquivo que você indicou, só neste disparo |
A URL precisa ser http ou https e estar acessível publicamente —
nós baixamos o arquivo e o repassamos à Meta. Se você não tem onde hospedá-lo, use o
endpoint de upload abaixo e envie a URL que ele devolve.
curl -X POST https://back1.atys.pro/api/meta-waba-templates/upload-send-media \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-F "meta_waba_id=900" \
-F "file=@fatura.pdf"
# resposta
{ "data": {
"url": "https://...",
"filename": "fatura.pdf",
"expires_at": "2026-08-26T00:19:26-03:00"
} }
Aceita image/jpeg, image/png, image/webp,
video/mp4, video/3gpp e application/pdf, dentro dos
limites do WhatsApp: 5 MB para imagem, 16 MB para vídeo
e 100 MB para documento. O tipo precisa combinar com o
format do cabeçalho do template.
expires_at da resposta traz o
prazo. Suba o arquivo e dispare em seguida — não guarde a URL para reusar depois.
Depois de enviado, o arquivo deixa de ter prazo e acompanha a mensagem normalmente.
header_media_filename define o nome
que o destinatário vê no WhatsApp, e só vale acompanhado de
header_media_url — quando a mídia é a fixa do template, quem manda
é o nome registrado nele. Sem header_media_filename, o nome é deduzido da URL.
Resposta e Status
Com enqueue=false (o default) a requisição aguarda o envio real e devolve a
resposta da Meta:
{
"Status": true,
"Message": "OK",
"Data": {
"ExternalID": "wamid.HBgNNTUyMTk2OTAyMzYyMBUCABEYEjk1RjJGQkFG..."
}
}
A resposta traz ainda Data.raw com o retorno bruto da Meta. É material de
diagnóstico e não faz parte do contrato — não construa lógica em cima
dele. Use Data.ExternalID.
Guarde o ExternalID (o wamid): é o identificador da mensagem no
WhatsApp e o que permite correlacionar os recibos posteriores. A evolução do status é:
| Status | Significado |
|---|---|
| Enviada | A Meta aceitou o pedido de envio |
| Entregue | Chegou ao aparelho do destinatário |
| Lida | O destinatário abriu a conversa |
| Falha | A Meta recusou a entrega (número inválido, bloqueio, opt-out…) |
Com enqueue=true a resposta é imediata e confirma apenas que a mensagem foi
aceita para processamento — o envio acontece em segundo plano:
{
"message": "Whatsapp message enqueued successfully"
}
Erros Comuns
Causa: tentou enviar texto ou arquivo livre para uma conversa cujo contato não escreve há mais de 24 horas (ou nunca escreveu).
Solução: reenvie usando meta_waba_template_id. Nada foi cobrado nem enviado.
{
"status": false,
"message": "A janela de 24 horas desta conversa está fechada. Fora dela o
WhatsApp Oficial só aceita mensagens de template — envie
meta_waba_template_id."
}
Causa: o meta_waba_template_id não existe ou pertence a outra empresa.
Solução: use um id devolvido por Listar Templates com o seu próprio token.
Causa: o template não foi aprovado pela Meta, ou a mídia do cabeçalho excede o limite do WhatsApp (5 MB para imagem, 16 MB para vídeo, 100 MB para documento).
Solução: confira o status do template em Listar Templates — só os aprovados podem ser disparados. Se a causa for o tamanho, comprima o arquivo ou aponte header_media_url para uma versão menor.
Causa: o número informado em sender_key não está cadastrado como canal de WhatsApp Oficial na sua conta.
Solução: confira o número em Configurações → Canais. Ele precisa estar conectado e verificado junto à Meta.
Causa: a Meta recusou o envio. O campo details traz o motivo original.
Solução: causas frequentes são número sem WhatsApp, contato que bloqueou a empresa, ou limite de mensagens de marketing atingido para aquele contato.
Causa: falta algum campo obrigatório. As mensagens possíveis são:
Platform is required—platformvazio ou ausenteSender key is required/Contact key is requiredText, file or meta_waba_template_id is required— nenhum conteúdo informadometa_waba_template_id só é aceito com platform: ofc_whatsapp— template enviado a um canal não oficial
Causa: o limite deste endpoint é de 600 requisições por minuto por empresa (não por token — todos os seus usuários API somam no mesmo limite).
Solução: espalhe os disparos ao longo do minuto. Para volumes grandes e programados, considere enqueue=true.
Causa: a cota de envios do seu plano acabou.
Solução: fale com o atendimento Atys para ampliar o plano. A resposta é texto puro, não JSON.
Causa: token inválido, expirado ou ausente.
Solução: obtenha um novo token em Autenticação.
Limitações
Regras da Meta
- Fora da janela de 24h, só template
- Templates precisam de aprovação prévia da Meta
- Há limite diário de destinatários únicos, conforme o nível do seu número
- A qualidade do número cai se houver muitos bloqueios ou denúncias
Não disponível nesta API
- Conectar números e criar templates (feito na plataforma)
- Editar ou apagar mensagens já enviadas
- Verificar se um número tem WhatsApp