Integração
API de WhatsApp para Supabase
Dispare WhatsApp a partir de uma Edge Function do Supabase, reagindo a mudanças no banco.
// supabase/functions/avisar-whatsapp/index.ts
Deno.serve(async (req) => {
// payload de um Database Webhook do Supabase
const { record } = await req.json();
const res = await fetch("https://zapixo.com.br/api/v1/messages/text", {
method: "POST",
headers: {
Authorization: `Bearer ${Deno.env.get("ZAPIXO_API_KEY")}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
instance: "app",
number: record.telefone,
text: `Olá ${record.nome}, seu cadastro foi confirmado!`,
}),
});
if (!res.ok) {
console.error("zapixo:", res.status, await res.text());
return new Response("erro ao enviar", { status: 500 });
}
const { messageId } = await res.json();
return Response.json({ messageId });
});
// 1. supabase secrets set ZAPIXO_API_KEY=zpx_...
// 2. supabase functions deploy avisar-whatsapp
// 3. crie um Database Webhook na tabela, apontando para a funçãoNo Supabase, o caminho natural é uma Edge Function: ela guarda a chave do Zapixo como secret, envia pela API e também recebe o webhook das mensagens para gravar no Postgres.
Para disparar a partir do banco — cadastro novo, pedido pago —, um Database Webhook chama a função a cada insert ou update. A função traduz o evento do banco para a chamada do Zapixo: os formatos são diferentes, e é nela que a chave e o tratamento de erro ficam.
O que dá para fazer com Supabase e WhatsApp
Confirmação de cadastro
Linha nova na tabela de usuários dispara a mensagem de boas-vindas.
Aviso de mudança de status
Pedido que passa para pago ou enviado vira mensagem para o cliente, uma vez só.
Caixa de entrada no próprio app
As mensagens recebidas vão para uma tabela, e o Realtime do Supabase mostra cada uma no app assim que chega.
Bot com dado do banco
A função consulta o Postgres e responde com o dado real: pedido, agenda, saldo.
Enviar mensagens pelo Supabase
Guarde a chave como secret
O secret fica disponível só nas Edge Functions, por
Deno.env.get. Nunca coloque a chave no app cliente: com a anon key pública, qualquer usuário a leria.supabase secrets set ZAPIXO_API_KEY=zpx_...
Uma função de envio compartilhada
Pasta que começa com
_não vira função: é o lugar do código comum entre elas.// supabase/functions/_shared/zapixo.ts export async function enviarWhatsApp(numero: string, texto: string) { const res = await fetch("https://zapixo.com.br/api/v1/messages/text", { method: "POST", headers: { Authorization: `Bearer ${Deno.env.get("ZAPIXO_API_KEY")}`, "Content-Type": "application/json", }, body: JSON.stringify({ instance: "app", number: numero.replace(/\D/g, ""), text: texto }), }); if (!res.ok) throw new Error(`Zapixo ${res.status}: ${await res.text()}`); return res.json() as Promise<{ success: true; messageId?: string }>; }Dispare pelo Database Webhook
Em Database › Webhooks, crie o gatilho na tabela, do tipo Supabase Edge Functions, apontando para a função. Ela recebe
type,table,recordeold_record— compare os dois para avisar só quando o status mudar de fato:import { enviarWhatsApp } from "../_shared/zapixo.ts"; Deno.serve(async (req) => { const { type, record, old_record } = await req.json(); if (type === "UPDATE" && record.status === "pago" && old_record?.status !== "pago") { await enviarWhatsApp(record.telefone, `Pagamento do pedido ${record.id} confirmado.`); } return new Response("ok"); });
Receber mensagens no Supabase
Publique a função sem verificação de JWT
Por padrão, toda Edge Function exige um JWT do Supabase no header. O Zapixo não manda esse header, então a função que recebe o webhook é publicada sem essa verificação — e passa a se proteger pelo segredo na URL:
supabase secrets set ZAPIXO_WEBHOOK_SECRET=um-segredo-longo supabase functions deploy receber-whatsapp --no-verify-jwt
Cole a URL no painel
Em Webhook de mensagens, com o segredo no fim:
https://SEU-PROJETO.supabase.co/functions/v1/receber-whatsapp?chave=SEU_SEGREDO.Grave sem duplicar
Com
message_idcomo coluna única, a repetição do webhook vira nada.SUPABASE_URLeSUPABASE_SERVICE_ROLE_KEYjá existem nas Edge Functions, sem configurar:import { createClient } from "npm:@supabase/supabase-js@2"; const supabase = createClient( Deno.env.get("SUPABASE_URL")!, Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!, ); Deno.serve(async (req) => { const segredo = Deno.env.get("ZAPIXO_WEBHOOK_SECRET"); if (!segredo || new URL(req.url).searchParams.get("chave") !== segredo) { return new Response(null, { status: 401 }); } const { event, data } = await req.json(); if (event === "messages.upsert" && !data?.key?.fromMe) { await supabase.from("mensagens_whatsapp").upsert( { message_id: data.key.id, numero: data.key.remoteJid.split("@")[0], texto: data.message?.conversation ?? data.message?.extendedTextMessage?.text ?? "", }, { onConflict: "message_id", ignoreDuplicates: true }, ); } return new Response(null, { status: 200 }); });
Erros comuns e como resolver
- 401 em toda chamada do Zapixo
- A função foi publicada com a verificação de JWT ligada. Publique de novo com
--no-verify-jwt— e confira que ela confere o segredo. - Função pública sem segredo
- Sem JWT, qualquer um que souber a URL chama a função. A conferência do segredo no começo do código não é opcional.
- Service role no lugar errado
- A service role ignora o RLS. Ela fica só dentro da função: nunca no app, nunca em log, nunca devolvida na resposta.
- Aviso disparado em todo update
- O Database Webhook dispara a cada alteração da linha, inclusive de campos que não importam. Compare
recordcomold_recordantes de enviar. - O bot responde a si mesmo
- Mensagens enviadas pela própria instância também chegam, com
fromMe: true. O filtro do exemplo evita que a resposta do bot gere outra. - Erros 401, 404 e 502 no envio
401: chave errada ou secret com outro nome.404:instancenão bate com o nome no painel.502: a entrega ao WhatsApp falhou, e o motivo vem emdetails.
Perguntas frequentes
Dá para chamar o Zapixo direto de um trigger do Postgres?
Não é o recomendado. O Database Webhook envia o formato do Supabase, não o do Zapixo, e a Edge Function é o lugar certo para a chave e para tratar erro.
Consigo mostrar as mensagens no app em tempo real?
Sim. Com a tabela gravada pela função, o Realtime do Supabase avisa o app a cada insert, respeitando o RLS da tabela.
O webhook do Zapixo tem assinatura?
Não. Por isso a função confere um segredo na URL, já que ela é publicada sem verificação de JWT.
Consigo enviar imagem pela Edge Function?
Sim, pelo endpoint /api/v1/messages/media, com mediaUrl (link público do arquivo, que pode vir do Storage) e caption opcional.
O trial tem limite de mensagens?
Tem. Ao atingir o limite, a API responde 402 com o link para assinar.
Leia também
- Webhook WhatsApp: como receber mensagens na sua aplicação
Aprenda a configurar webhooks para receber mensagens, atualizações de status e eventos de conexão do WhatsApp via Zapixo.
- Notificações transacionais no WhatsApp: pedido, boleto e entrega
Quais avisos automatizar no WhatsApp, quando disparar cada um, como escrever a mensagem e a estrutura técnica que garante que ninguém receba duas vezes.
- Como proteger a chave da sua API de WhatsApp
Onde guardar a chave, como reagir a um vazamento, por que nunca chamar a API do navegador ou do app, e o que fazer antes de dar acesso a um terceiro.