Ir para conteúdo principal

WhatsApp Oficial API

Envie mensagens pelo WhatsApp Business Platform (Cloud API da Meta) usando o número oficial verificado da sua empresa.

Antes de começar: você precisa de um 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çãoTexto/arquivo livreTemplate
Contato escreveu nas últimas 24h Permitido Permitido
Contato nunca escreveu, ou faz mais de 24h Recusado Permitido
A API recusa antes de enviar. Se a janela estiver fechada e você não informar um template, a resposta é 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.

GET

https://api.atys.pro/api/meta-waba-templates/for-channel

Templates disponíveis para um canal oficial

Headers Obrigatórios

HeaderValorDescrição
AuthorizationBearer SEU_ACCESS_TOKENToken de autenticação
Acceptapplication/jsonTipo de resposta esperada

Query Params

ParâmetroTipoObrigatórioDescrição
company_channel_idinteger✅ Sim Id do canal oficial. Obtenha em GET /api/company-channels
Cabeçalhos de mídia. Alguns templates têm cabeçalho de imagem, vídeo ou documento — identifique-os pelo campo 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

JavaScript (Fetch API)
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);
Python (Requests)
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 (cURL)
<?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 (Terminal)
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

200 OK
{
  "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}}."
          }
        ]
      }
    }
  ]
}
Leia os 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ódigoMensagemCausa
400company_channel_id é obrigatórioParâmetro ausente
404Canal não encontradoO canal não existe ou é de outra empresa
403Sem permissão para acessar este recursoO 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.

POST

https://api.atys.pro/api/api-send-message

Texto ou arquivo, dentro da janela de 24 horas

Headers Obrigatórios

HeaderValorDescrição
AuthorizationBearer SEU_ACCESS_TOKENToken de autenticação
Content-Typeapplication/json ou multipart/form-datamultipart quando enviar arquivo
Acceptapplication/jsonTipo de resposta esperada

Parâmetros do Body

ParâmetroTipoObrigatórioDescriçãoExemplo
platformstring✅ Sim Use ofc_whatsapp para o canal oficial ofc_whatsapp
sender_keystring✅ Sim Número oficial da sua empresa, já conectado no Atys 5521999999999
contact_keystring✅ Sim Telefone do destinatário, com código do país 5521988888888
textstring⚠️ Sim* Texto da mensagem Seu pedido saiu para entrega!
filefile⚠️ Sim* Arquivo (imagem, PDF, vídeo, áudio) nota.pdf
contact_is_groupboolean❌ Não Marque true se o destino for um grupo. Default false false
enqueueboolean❌ Não Default false (síncrono). Veja Resposta e Status false
* Forneça text OU file. Fora da janela de 24h nenhum dos dois é aceito — nesse caso use um template.

Exemplos de Código

JavaScript (Fetch API)
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);
}
Python (Requests)
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 (cURL)
<?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 (Terminal)
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.

POST

https://api.atys.pro/api/api-send-message

Mesmo endpoint, informando meta_waba_template_id

Parâmetros adicionais

ParâmetroTipoObrigatórioDescrição
meta_waba_template_idinteger✅ Sim Id vindo de Listar Templates. Substitui text
variable_valuesarray❌ Não Valores para variáveis posicionais ({{1}}, {{2}}), na ordem
named_variable_valuesobject❌ Não Valores para variáveis nomeadas ({{nome}}), por chave
header_media_urlstring❌ 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_filenamestring❌ 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

JavaScript — variáveis posicionais
// 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']
  })
});
Python — variáveis nomeadas
# 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 (cURL)
<?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 (Terminal)
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.

FormatoNo templateComo enviar
Posicional Olá, {{1}}, pedido {{2}} "variable_values": ["Ana", "A-1234"] — na ordem
Nomeado Olá {{first_name}} "named_variable_values": {"first_name": "Ana"}
Uma variável sem valor vira texto vazio — a mensagem é enviada mesmo assim, com um espaço em branco no lugar. Confira que você está preenchendo todas antes de disparar em volume.
Chaves iniciadas por _ 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çãoO que enviarResultado
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.

POST /api/meta-waba-templates/upload-send-media
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.

A URL do upload é temporária. Ela existe para o disparo, não como hospedagem: se o arquivo não for usado num envio em 72 horas, é apagado e a URL para de responder. O campo 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.
Cabeçalho de documento: 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:

200 OK — mensagem aceita pela Meta
{
  "Status": true,
  "Message": "OK",
  "Data": {
    "ExternalID": "wamid.HBgNNTUyMTk2OTAyMzYyMBUCABEYEjk1RjJGQkFG..."
  }
}
Este 200 significa "aceito", não "entregue". A Meta confirma que recebeu o pedido de envio; a entrega ao aparelho e a leitura acontecem depois, de forma assíncrona, e chegam como recibos.

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 é:

StatusSignificado
EnviadaA Meta aceitou o pedido de envio
EntregueChegou ao aparelho do destinatário
LidaO destinatário abriu a conversa
FalhaA Meta recusou a entrega (número inválido, bloqueio, opt-out…)
Para receber essas mudanças no seu servidor em tempo real, configure os Webhooks de saída.

Com enqueue=true a resposta é imediata e confirma apenas que a mensagem foi aceita para processamento — o envio acontece em segundo plano:

200 OK — enfileirada
{
  "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 requiredplatform vazio ou ausente
  • Sender key is required / Contact key is required
  • Text, file or meta_waba_template_id is required — nenhum conteúdo informado
  • meta_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
Editar, revogar e verificar números não são limitações do Atys: a Cloud API da Meta simplesmente não oferece essas operações.