WhatsApp da loja
Rotas que a Meta chama para verificar a URL, entregar mensagens e status do WhatsApp Cloud API e trocar dados dos Flows de uma loja.
O canal WhatsApp Commerce recebe tudo o que acontece no número da loja (mensagens do cliente, confirmações de entrega e de leitura) por webhooks da Meta. Cada loja tem a sua URL, identificada pelo tenant_slug (a primeira parte do domínio da loja, por exemplo minhaloja). Você cola essa URL e o token de verificação no painel de desenvolvedores da Meta. A loja precisa ter o módulo WhatsApp Commerce ativo.
Verificar a URL
Quando você salva a URL na Meta, ela faz um GET com um desafio. A loja confere o token e devolve o desafio como texto.
/api/v1/webhooks/whatsapp/{tenant_slug}PúblicaVerificação da Meta (hub.challenge)
Parâmetros de caminho
tenant_slugstringobrigatóriominhaloja em minhaloja.io.venddor.com.br).Parâmetros de consulta
hub.modestringobrigatóriosubscribesubscribe.hub.verify_tokenstringobrigatóriohub.challengestringobrigatórioExemplo de requisição
curl "$BASE_URL/api/v1/webhooks/whatsapp/minhaloja?hub.mode=subscribe&hub.verify_token=$VERIFY_TOKEN&hub.challenge=1158201444"Resposta
1158201444Erros
| Status | Quando | Corpo |
|---|---|---|
| 400 | hub.mode diferente de subscribe | {"error":"invalid hub.mode, expected 'subscribe'"} |
| 403 | Token de verificação não cadastrado | {"error":"webhook verify token not configured"} |
| 403 | Token errado | {"error":"invalid verify token"} |
| 403 | Módulo whatsapp-commerce desligado | {"error":"module_disabled","module":"whatsapp-commerce"} |
Receber mensagens e status
A Meta envia aqui cada mensagem recebida e cada atualização de status (enviada, entregue, lida). A loja confere a assinatura, processa a mensagem em até 10 segundos e sempre confirma com 200, mesmo que o processamento interno falhe, porque a Meta repete por vários dias qualquer resposta diferente de 2xx. A rota tem um limite de 20 segundos; passado esse tempo a resposta é 503.
/api/v1/webhooks/whatsapp/{tenant_slug}PúblicaReceber mensagens e status do WhatsApp
Parâmetros de caminho
tenant_slugstringobrigatóriominhaloja em minhaloja.io.venddor.com.br).Cabeçalhos
X-Hub-Signature-256stringobrigatóriosha256= mais o HMAC-SHA256 do corpo bruto em hexadecimal, calculado com o segredo do app da Meta (App Secret).Corpo da requisição
(JSON)objectobrigatórioobject, entry[].changes[].value).Exemplo de requisição
BODY='{"object":"whatsapp_business_account","entry":[{"id":"102290129340398","changes":[{"field":"messages","value":{"messaging_product":"whatsapp","metadata":{"display_phone_number":"551140041234","phone_number_id":"106540352242922"},"statuses":[{"id":"wamid.HBgM","status":"delivered","timestamp":"1791748900","recipient_id":"5511987654321"}]}}]}]}'
SIG="sha256=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$META_APP_SECRET" | sed 's/^.* //')"
curl -X POST "$BASE_URL/api/v1/webhooks/whatsapp/minhaloja" \
-H "Content-Type: application/json" \
-H "X-Hub-Signature-256: $SIG" \
-d "$BODY"Resposta
{"status":"ok"}Erros
| Status | Quando | Corpo |
|---|---|---|
| 400 | Não foi possível ler o corpo | {"error":"failed to read request body"} |
| 401 | Assinatura inválida ou ausente | {"error":"invalid webhook signature"} |
| 403 | Segredo do app não cadastrado na loja | webhook secret not configured (texto simples) |
| 403 | Módulo whatsapp-commerce desligado | {"error":"module_disabled","module":"whatsapp-commerce"} |
| 503 | A rota excedeu o limite de 20 segundos | (corpo de timeout) |
Troca de dados de um Flow
Os Flows são formulários interativos do WhatsApp (cadastro, endereço, pagamento) que mostram dados da loja em cada tela. A cada passo a Meta chama esta rota com o pedido de dados criptografado e espera a resposta também criptografada. A autenticidade vem da própria criptografia: só a Meta consegue cifrar para a chave pública que a loja publicou. Por isso a rota não usa assinatura.
/api/v1/webhooks/whatsapp/{tenant_slug}/flowPúblicaTroca de dados criptografada de um Flow
Parâmetros de caminho
tenant_slugstringobrigatóriominhaloja em minhaloja.io.venddor.com.br).Parâmetros de consulta
phone_number_idstringCorpo da requisição
encrypted_aes_keystringobrigatórioencrypted_flow_datastringobrigatórioinitial_vectorstringobrigatórioExemplo de requisição
curl -X POST "$BASE_URL/api/v1/webhooks/whatsapp/minhaloja/flow?phone_number_id=106540352242922" \
-H "Content-Type: application/json" \
-d '{"encrypted_aes_key":"<base64>","encrypted_flow_data":"<base64>","initial_vector":"<base64>"}'Resposta
q3XyZ0p1Qm9...==A resposta é o texto base64 da resposta do Flow, cifrada com a mesma chave AES e o vetor invertido, sem envelope JSON.
Erros
| Status | Quando | Corpo |
|---|---|---|
| 400 | Corpo sem os três campos, ou que não decifra | {"error":"encrypted_aes_key, encrypted_flow_data, and initial_vector are required"} ou {"error":"failed to decrypt flow body"} |
| 401 | Loja sem chave de Flow ativa | {"error":"no active flow key registered for this store"} |
| 421 | A chave RSA não decifra a chave AES (chave desatualizada na Meta) | {"error":"failed to decrypt AES key (key may need rotation)"} |
| 403 | Módulo whatsapp-commerce desligado | {"error":"module_disabled","module":"whatsapp-commerce"} |
Chave pública do Flow
Devolve a chave pública que a loja usa nos Flows de um número, para você comparar com a que a Meta tem cadastrada. A chave pública é pública por definição.
/api/v1/webhooks/whatsapp/{tenant_slug}/public_keyPúblicaChave pública ativa do Flow
Parâmetros de caminho
tenant_slugstringobrigatóriominhaloja em minhaloja.io.venddor.com.br).Parâmetros de consulta
phone_number_idstringobrigatórioExemplo de requisição
curl "$BASE_URL/api/v1/webhooks/whatsapp/minhaloja/public_key?phone_number_id=106540352242922"Resposta
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
-----END PUBLIC KEY-----Erros
| Status | Quando | Corpo |
|---|---|---|
| 400 | Falta phone_number_id | {"error":"phone_number_id is required"} |
| 404 | Nenhuma chave ativa para o número | {"error":"no active flow key for phone_number_id"} |
| 403 | Módulo whatsapp-commerce desligado | {"error":"module_disabled","module":"whatsapp-commerce"} |