Pular para o conteúdo

Lovable

Apps do Lovable têm duas partes: o front-end (React, roda no navegador de quem usa o app) e o back-end (Lovable Cloud / Supabase, com banco e Edge Functions). A chave da TopTalking só pode ficar no back-end.

Navegador (React) → Edge Function (Supabase) → API da TopTalking
sem chave lê TOPTALKING_API_KEY https://platform.toptalking.com.br/v1

Em Configurações → Desenvolvedor, clique em Criar chave. Dê um nome como “App Lovable” e escolha:

  • Somente leitura se o app só vai consultar (painéis, relatórios);
  • Acesso total se vai enviar mensagens, criar contatos ou mover cards.

O nome do secret deve ser exatamente TOPTALKING_API_KEY.

  • Jeito mais fácil: mande o primeiro prompt abaixo. Quando o Lovable precisar da chave, ele mostra um formulário seguro para adicionar o secret — cole a chave ali, nunca na conversa.
  • Manual: adicione o secret nas configurações de secrets do backend do projeto (Lovable Cloud) ou, se o projeto usa uma conta Supabase própria, no painel da Supabase em Edge Functions → Secrets.

Cole este prompt no Lovable antes de pedir qualquer funcionalidade:

Vou integrar este app com a API da TopTalking (WhatsApp + CRM com Kanban).
Leia a documentação completa antes de escrever código: https://platform.toptalking.com.br/llms-full.txt
Configure a base da integração:
1. Crie um secret chamado TOPTALKING_API_KEY (me peça para colar o valor pelo formulário seguro de secrets; não peça a chave no chat).
2. Crie um módulo compartilhado para Edge Functions em supabase/functions/_shared/toptalking.ts com:
- const API = "https://platform.toptalking.com.br/v1"
- uma função toptalking(path, { method, body, idempotencyKey }) que faz fetch para `${API}${path}` com os cabeçalhos
"Authorization: Bearer " + Deno.env.get("TOPTALKING_API_KEY"), "Content-Type: application/json"
e, se informado, "Idempotency-Key".
- Se a resposta não for 2xx, o corpo é { "error": { "type", "code", "message", "param", "request_id" } }:
lance um erro com status, code, message e request_id. Em 429, inclua o valor do cabeçalho Retry-After.
3. Crie a Edge Function "toptalking-status" que chama GET /me e devolve { organizacao: organization.name, acesso: api_key.access }.
Ela deve exigir usuário logado (verificação de JWT do Supabase ligada) e tratar CORS (OPTIONS).
4. Adicione na tela de configurações do app um botão "Testar conexão com a TopTalking" que chama essa função
com supabase.functions.invoke e mostra o nome da organização ou o erro.
Regras obrigatórias para tudo o que fizermos com a TopTalking a partir de agora:
- NUNCA chame https://platform.toptalking.com.br a partir do front-end (React) e NUNCA escreva a chave no código.
A API bloqueia o navegador (sem CORS). Toda chamada sai de uma Edge Function usando o módulo _shared/toptalking.ts.
- Não crie uma função "proxy" genérica que repassa qualquer caminho/método vindo do front-end: crie uma Edge Function por ação, validando os dados recebidos.
- Envios de mensagem sempre com Idempotency-Key derivada do ID da operação.
- Use só endpoints e campos que existem na documentação.

Escolha um dos prompts prontos abaixo (ou adapte). Todos já repetem as regras de segurança — é proposital: o Lovable segue melhor quando a regra está no mesmo prompt.

Prompt: enviar WhatsApp quando alguém preencher o formulário

Seção intitulada “Prompt: enviar WhatsApp quando alguém preencher o formulário”
Quero que, quando um visitante preencher o formulário de contato deste site, ele receba uma mensagem de boas-vindas no WhatsApp pela API da TopTalking e já entre no Kanban da empresa na etapa "Novo lead".
Documentação da API (leia antes de codar): https://platform.toptalking.com.br/llms-full.txt
- URL base: https://platform.toptalking.com.br/v1
- Autenticação: cabeçalho "Authorization: Bearer <chave>", com a chave lida de Deno.env.get("TOPTALKING_API_KEY") (secret do Supabase).
Segurança (obrigatório):
- A chave NUNCA vai para o front-end nem para o código. O React chama só a nossa Edge Function; a Edge Function chama a TopTalking.
- A API da TopTalking não aceita chamadas do navegador (sem CORS). Não tente chamá-la do React.
O que construir:
1. Formulário com: nome, WhatsApp com DDD e uma caixa obrigatória "Aceito receber mensagens no WhatsApp".
Valide o telefone no front e no back: só dígitos; com DDD são 10 ou 11 dígitos; se não começar com 55, adicione 55.
2. Tabela "leads": id (uuid), nome, telefone, consentimento (boolean), status ('pendente' | 'enviado' | 'erro'),
toptalking_contact_id, toptalking_message_id, erro_codigo, erro_mensagem, created_at.
RLS: visitantes não leem nem escrevem direto; só a Edge Function (service role) grava. Usuários logados podem ler.
3. Edge Function "enviar-boas-vindas" (pública, sem exigir login, com CORS liberado só para o domínio do app):
a. Recebe { nome, telefone, consentimento }. Recuse se consentimento não for true ou se o telefone for inválido.
b. Anti-abuso: se já existe lead com o mesmo telefone nas últimas 24 h, não envie de novo (responda sucesso sem reenviar).
c. Grave o lead com status 'pendente'.
d. Busque as etapas: GET /v1/stages → { data: [{ id, name, is_final, ... }] }. Pegue o id da etapa com name "Novo lead"
(se não existir, siga sem etapa).
e. Crie/atualize o contato: POST /v1/contacts com { "phone": telefone, "name": nome, "stage_id": <id da etapa> }.
A resposta (201 criado ou 200 já existia) traz o contato com "id".
f. Envie a mensagem: POST /v1/messages com
{ "contact_id": <id do contato>, "text": "Olá, <nome>! 👋 Recebemos seu contato e logo vamos falar com você por aqui." }
e o cabeçalho "Idempotency-Key: lead-<id do lead>-boas-vindas".
A resposta 201 traz a mensagem com "id" e "status".
g. Atualize o lead com status 'enviado', toptalking_contact_id e toptalking_message_id.
h. Em erro, a TopTalking responde { "error": { "type", "code", "message", "request_id" } }:
salve erro_codigo/erro_mensagem, status 'erro', e devolva ao front uma mensagem amigável.
Exemplos de code: "channel_unavailable" (WhatsApp da empresa desconectado), "invalid_phone" (telefone inválido),
"rate_limit_exceeded" (muitas requisições; respeite o cabeçalho Retry-After).
4. Depois de enviar o formulário, mostre "Pronto! Enviamos uma mensagem no seu WhatsApp." ou o erro amigável.
Não faça nenhum envio em massa: exatamente uma mensagem por formulário enviado.

Prompt: painel com as conversas e as etapas do Kanban

Seção intitulada “Prompt: painel com as conversas e as etapas do Kanban”
Quero uma página "Atendimento" (só para usuários logados) que mostre as conversas do WhatsApp da minha empresa organizadas pelas etapas do Kanban da TopTalking, com histórico de mensagens e a opção de mover um card de etapa.
Documentação da API (leia antes de codar): https://platform.toptalking.com.br/llms-full.txt
- URL base: https://platform.toptalking.com.br/v1
- Autenticação: cabeçalho "Authorization: Bearer <chave>", chave em Deno.env.get("TOPTALKING_API_KEY").
Segurança (obrigatório):
- Nunca chame a TopTalking do front-end e nunca exponha a chave. Crie Edge Functions que exigem usuário logado
(verificação de JWT do Supabase ligada) e chamam a TopTalking pelo servidor.
- Não crie uma função genérica que repassa qualquer caminho: uma função por ação.
Edge Functions:
1. "toptalking-quadro" (GET):
- GET /v1/stages → lista de etapas em ordem: { data: [{ id, name, color, position, is_final }] }.
- Para cada etapa: GET /v1/contacts?stage_id=<id>&archived=false&limit=100 → { data: [contatos], has_more, next_cursor }.
Campos do contato: id, name, phone, stage { id, name, is_final }, ai_enabled, unread_count,
last_message_at, last_message_preview, resolved_at.
- Devolva [{ etapa, contatos }]. Guarde o resultado em cache por 60 segundos (limite da API: 120 requisições/minuto).
2. "toptalking-conversa" (GET ?contact_id=<uuid>):
- GET /v1/contacts/{contact_id}/messages?limit=50 → mensagens da mais recente para a mais antiga
(campos: id, direction "inbound"|"outbound", type, text, media { url, mime_type, filename } ou null, status, created_at).
3. "toptalking-mover" (POST { contact_id, stage_id }):
- Confira que stage_id está entre os ids de GET /v1/stages (ou é null para tirar do Kanban).
- PATCH /v1/contacts/{contact_id} com { "stage_id": stage_id } → devolve o contato atualizado.
Front-end:
- Uma coluna por etapa (nome e cor da etapa). Cards com nome (ou telefone, se não houver nome), prévia da última mensagem,
horário relativo em português ("há 5 min") e um selo com unread_count quando > 0.
- Arrastar e soltar entre colunas (ou menu "Mover para…") chama toptalking-mover. Atualize de forma otimista e desfaça se der erro.
Se a etapa de destino tiver is_final = true, peça confirmação: "Mover para esta etapa finaliza a conversa".
- Clicar no card abre um painel lateral com o histórico (toptalking-conversa), em ordem cronológica:
inbound à esquerda, outbound à direita, mostrando texto, imagem/arquivo (media.url) e status de entrega.
- Botão "Atualizar" para recarregar o quadro.
- Trate erros pelo error.code da resposta ({ "error": { "type", "code", "message", "request_id" } }).
Esta página não envia mensagens.

Prompt: receber mensagens via webhook e salvar numa tabela

Seção intitulada “Prompt: receber mensagens via webhook e salvar numa tabela”
Quero salvar no banco deste app as mensagens que os clientes mandam para o WhatsApp da empresa, recebendo os webhooks da TopTalking em uma Edge Function.
Documentação (leia as seções de Webhooks, Eventos e Verificar a assinatura): https://platform.toptalking.com.br/llms-full.txt
1. Tabela "mensagens_whatsapp":
id (uuid, primary key = data.message.id), evento_id (uuid, unique = id do evento), contato_id (uuid), contato_nome (text),
telefone (text), direcao (text), tipo (text), texto (text), midia_url (text), recebida_em (timestamptz = data.message.created_at),
payload (jsonb com o evento inteiro), created_at (default now()).
RLS: só usuários logados podem ler; ninguém escreve pelo front-end.
2. Edge Function pública "toptalking-webhook":
- Desligue a verificação de JWT dela em supabase/config.toml ([functions.toptalking-webhook] verify_jwt = false),
porque a TopTalking não envia token do Supabase.
- Leia o corpo CRU com await req.text() ANTES de qualquer JSON.parse.
- Valide o cabeçalho X-TopTalking-Signature, no formato "t=<unix em segundos>,v1=<hex>":
v1 = HMAC-SHA256 em hexadecimal, com chave = Deno.env.get("TOPTALKING_WEBHOOK_SECRET") (o valor inteiro, começando com whsec_)
e mensagem = t + "." + corpo cru.
Use Web Crypto (crypto.subtle.importKey com HMAC/SHA-256 e crypto.subtle.verify, que compara em tempo constante).
Rejeite com 401 se a assinatura não bater ou se a diferença entre agora e t for maior que 300 segundos.
- Só então faça JSON.parse. O evento tem o formato { id, object: "event", type, created_at, organization_id, data }.
- Se type = "message.received": grave em mensagens_whatsapp usando data.message (id, direction, type, text, media?.url, created_at)
e data.contact (id, name, phone), com o cliente Supabase usando a service role (SUPABASE_SERVICE_ROLE_KEY) só dentro da função.
Use upsert/on conflict do nothing: o mesmo evento pode chegar mais de uma vez.
- Se type = "ping" (teste do painel) ou qualquer outro tipo: apenas responda 200.
- Responda 200 rapidamente (em menos de 10 segundos). Qualquer resposta fora de 2xx faz a TopTalking tentar de novo.
3. Página "Mensagens recebidas" (só usuários logados) listando a tabela, mais recentes primeiro, com atualização em tempo real.
4. No fim, me diga:
- a URL da função (https://<projeto>.supabase.co/functions/v1/toptalking-webhook) para eu cadastrar no painel da TopTalking
em Configurações → Desenvolvedor → Webhooks → Novo webhook, assinando o evento message.received;
- que preciso adicionar o secret TOPTALKING_WEBHOOK_SECRET com o "segredo de assinatura" (whsec_...) que o painel mostra.
Não use a chave de API da TopTalking no front-end e não escreva nenhum segredo no código.

Prompt: lembrete automático no WhatsApp (mensagem agendada)

Seção intitulada “Prompt: lembrete automático no WhatsApp (mensagem agendada)”
Quero que, ao criar um agendamento neste app, o cliente receba um lembrete no WhatsApp 24 horas antes, usando as mensagens agendadas da TopTalking (elas aparecem na Agenda do painel da empresa).
Documentação (leia antes de codar): https://platform.toptalking.com.br/llms-full.txt
- URL base: https://platform.toptalking.com.br/v1
- Autenticação: "Authorization: Bearer <chave>", chave em Deno.env.get("TOPTALKING_API_KEY"). Nunca no front-end nem no código.
1. Na tabela de agendamentos, adicione a coluna toptalking_lembrete_id (uuid, opcional).
2. Edge Function "agendar-lembrete" (exige usuário logado), recebe { agendamento_id }:
- Leia nome, telefone (com DDD; adicione 55 se faltar) e data_hora do agendamento.
- Calcule send_at = data_hora − 24 horas, em ISO 8601 COM fuso (ex.: "2026-12-01T09:00:00-03:00").
Se send_at já passou, não agende (send_at precisa estar no futuro e em até 365 dias).
- POST /v1/scheduled-messages com
{ "to": telefone, "send_at": send_at, "title": "Lembrete de agendamento",
"text": "Oi {{nome}}! Passando para lembrar do seu horário amanhã às HH:MM. Qualquer coisa, é só responder aqui." }
e o cabeçalho "Idempotency-Key: agendamento-<id>-lembrete-24h".
Atenção: {{nome}} é uma variável da própria TopTalking — mantenha as chaves duplas literalmente no texto. Troque só HH:MM.
- A resposta 201 traz { id, status: "pending", ... }. Salve o id em toptalking_lembrete_id.
3. Edge Function "cancelar-lembrete", recebe { agendamento_id }:
- DELETE /v1/scheduled-messages/{toptalking_lembrete_id}. Resposta 200 = cancelado (status "canceled").
- Se vier 409 com error.code "invalid_state", o lembrete já foi enviado ou não está mais pendente: só registre.
- Limpe toptalking_lembrete_id.
4. Chame "agendar-lembrete" ao criar um agendamento; ao remarcar, chame "cancelar-lembrete" e depois "agendar-lembrete"
(use uma Idempotency-Key nova na remarcação, por exemplo com a nova data: agendamento-<id>-lembrete-<data>).
Ao cancelar o agendamento, chame "cancelar-lembrete".
Trate erros pelo error.code da resposta ({ "error": { "type", "code", "message", "request_id" } }).

Prompt: tela para conectar um WhatsApp por QR Code

Seção intitulada “Prompt: tela para conectar um WhatsApp por QR Code”
Quero uma página "Conectar WhatsApp" (só para usuários logados) onde eu crio uma conexão de WhatsApp na TopTalking
e leio o QR Code com o celular, sem precisar abrir o painel da TopTalking.
Documentação (leia antes de codar): https://platform.toptalking.com.br/llms-full.txt
Passo a passo de referência: https://platform.toptalking.com.br/referencia/canais/
- URL base: https://platform.toptalking.com.br/v1
- Autenticação: "Authorization: Bearer <chave>", chave em Deno.env.get("TOPTALKING_API_KEY"). Nunca no front-end nem no código.
Edge Functions (exigem usuário logado; o front-end chama SÓ estas funções, nunca a TopTalking):
1. "whatsapp-conectar" (POST { nome }):
- POST /v1/channels com { "name": nome } e o cabeçalho "Idempotency-Key: <uuid gerado no clique>".
- Resposta 201: { object: "channel_connection", channel: { id, name, phone, status }, status, qr_code, qr_code_generated_at, pairing_code }.
- Se vier 422 com error.code "limit_reached", devolva a error.message (o plano não permite mais conexões).
- Devolva { id: channel.id, status, qrCode: qr_code, telefone: channel.phone }.
2. "whatsapp-status" (GET ?id=<channel_id>):
- GET /v1/channels/{id}/qr → mesmo formato acima. Devolva { id, status, qrCode, telefone }.
- Se status for "disconnected" e qr_code for null, chame POST /v1/channels/{id}/connect com {} para gerar um novo QR Code
e devolva o resultado dele.
Front-end:
- Botão "Conectar WhatsApp" → chama whatsapp-conectar.
- Enquanto status for diferente de "connected", chame whatsapp-status a cada 4 segundos.
- qrCode é uma imagem PNG em data URL ("data:image/png;base64,..."): use direto em <img src={qrCode}> (264×264).
Se qrCode vier null, mostre "Gerando o QR Code…" (é normal nos primeiros segundos).
- Instruções abaixo da imagem: "No celular: WhatsApp → Aparelhos conectados → Conectar um aparelho".
- Quando status for "connected", pare de consultar e mostre "WhatsApp <telefone> conectado!".
- Desista depois de 3 minutos sem leitura e mostre um botão "Gerar novo QR Code" (que chama whatsapp-status de novo).
Trate erros pelo error.code da resposta ({ "error": { "type", "code", "message", "request_id" } }).

Resumo do que o prompt acima faz, para você conferir:

  1. A função toptalking-webhook fica pública (verify_jwt = false), porque quem chama é a TopTalking, não um usuário logado. A segurança vem da assinatura.
  2. Ela lê o corpo cru, valida X-TopTalking-Signature com o secret TOPTALKING_WEBHOOK_SECRET e só então processa. O código de referência está em Verificar a assinatura (aba “Deno / Supabase Edge Function”).
  3. No painel da TopTalking, em Configurações → Desenvolvedor → Webhooks → Novo webhook, cadastre https://<seu-projeto>.supabase.co/functions/v1/toptalking-webhook e escolha os eventos.
  4. Copie o segredo de assinatura (whsec_...) e adicione como secret TOPTALKING_WEBHOOK_SECRET no Lovable.
  5. Clique em Enviar teste e veja em Ver entregas se a resposta foi 200.
Sintoma Causa e solução
Erro de CORS no console do navegador O app está chamando a TopTalking direto do React. Peça: “Mova a chamada para uma Edge Function; o front-end só chama a Edge Function”.
401 invalid_api_key / missing_api_key O secret TOPTALKING_API_KEY não existe, tem outro nome ou foi colado com espaços.
403 read_only_key A chave é Somente leitura. Crie outra com Acesso total para enviar ou alterar.
422 channel_unavailable O WhatsApp da empresa está desconectado. Reconecte no painel em Configurações → Conexões.
O webhook recebe 401 da própria Supabase A função está com verificação de JWT ligada. Desligue para toptalking-webhook.
Assinatura do webhook nunca bate A função fez req.json() antes de validar. Valide com await req.text().
A chave apareceu no código ou no chat Revogue a chave no painel na hora e crie outra.