Appearance
Integração com a API NestJS
Contrato HTTP entre o
contrasync-whatsapp-bot(Lambda) e o móduloWhatsappModulenocontrasync-nest-api.
Visão geral
Toda a persistência fica no nest-api. O bot é stateless e consome endpoints HTTP em /whatsapp/bot/*. Esses endpoints estão atrás do BotApiKeyGuard: só aceitam requisições com o header x-bot-api-key correspondendo a process.env.BOT_API_KEY do servidor.
| Lado | Variável | Conteúdo |
|---|---|---|
Lambda (contrasync-whatsapp-bot) | BOT_API_KEY | Header enviado em cada request |
API (contrasync-nest-api) | BOT_API_KEY | Valor esperado pelo BotApiKeyGuard |
Os dois precisam ter o mesmo valor. Gerar com:
bash
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"Endpoints
GET /whatsapp/bot/account
Busca a WhatsappAccount pelo número.
Query: ?whatsappNumber=5511999998888
Resposta 200 (vinculada):
json
{
"id": "uuid",
"userId": "uuid",
"whatsappNumber": "5511999998888",
"defaultCompanyId": "uuid-or-null",
"defaultCompanyName": "Acme Consultoria",
"userName": "João Silva",
"userEmail": "[email protected]"
}Resposta 200 (não vinculada): null
POST /whatsapp/bot/otp/issue
Resolve o usuário por email/CPF/CNPJ e envia OTP por email.
Body:
json
{ "identifier": "[email protected]", "whatsappNumber": "5511999998888" }Resposta 201:
json
{ "emailSentTo": "jo***@acme.com", "expiresAt": "2026-05-12T10:30:00.000Z" }Erros possíveis:
404: identifier inválido ou não encontrado409: CNPJ com mais de um usuário ativo
A API resolve identifier na seguinte ordem:
- Se contém
@: lookup porUser.email - Se tem 11 dígitos: lookup por
User.cpf - Se tem 14 dígitos: lookup por
Company.document. Se exatamente 1 usuário ativo → usa. Mais que 1 → 409.
POST /whatsapp/bot/otp/verify
Valida OTP, marca como consumido, cria/atualiza a WhatsappAccount e retorna a lista de empresas do usuário.
Body:
json
{ "whatsappNumber": "5511999998888", "code": "123456" }Resposta 201:
json
{
"account": {
"id": "uuid",
"userId": "uuid",
"whatsappNumber": "5511999998888",
"defaultCompanyId": null
},
"companies": [
{ "id": "uuid", "name": "Acme", "document": "12345678000190", "role": "provider" },
{ "id": "uuid", "name": "Beta", "document": "98765432000111", "role": "borrower" }
]
}Se o usuário tem exatamente 1 empresa, account.defaultCompanyId já vem preenchido. Se tem múltiplas, fica null até a escolha via switch-company.
Erros possíveis:
404: nenhum OTP pendente ou expirado403: código inválido (tentativas incrementadas) ou limite (5) atingido
GET /whatsapp/bot/companies
Lista empresas disponíveis para um número já vinculado.
Query: ?whatsappNumber=5511999998888
Resposta 200:
json
[
{ "id": "uuid", "name": "Acme", "document": "12345678000190", "role": "provider" }
]Erros:
404: número não vinculado a nenhuma conta
POST /whatsapp/bot/switch-company
Troca a empresa padrão da conta.
Body:
json
{ "whatsappNumber": "5511999998888", "companyId": "uuid" }Resposta 201: WhatsappAccount atualizada.
Erros:
404: número não vinculado403: usuário não pertence à empresa selecionada
POST /whatsapp/bot/idempotency
Registra o messageId processado. Devolve duplicate: true se já existia.
Body:
json
{ "messageId": "MSG-001", "whatsappNumber": "5511999998888" }Resposta 201:
json
{ "duplicate": false, "processedAt": "2026-05-12T10:30:00.000Z" }Se duplicado: { "duplicate": true, "processedAt": "<ISO da primeira gravação>" }.
Tratamento de erros no ContrasyncApiClient
O cliente HTTP do bot (src/adapters/contrasync-api/contrasync-api.client.ts) traduz cada erro do upstream:
typescript
private fail(operation: string, error: unknown): never {
const status = error instanceof AxiosError ? error.response?.status : undefined
if (status && status >= 400 && status < 500) {
throw new ValidationError(upstreamMessage)
}
throw new BotError('Falha ao comunicar com a API do Contrasync', 502)
}- 4xx:
ValidationErrorcarregando a mensagem do upstream. OBotRouterServicecaptura e devolve essa mensagem ao usuário via Z-API. - 5xx, timeout, network:
BotError 502com mensagem genérica. Sobe pro boundary, vira HTTP 502, fica visível no log mas mascarado para o usuário.
Mensagens devolvidas pela API NestJS (ex.: "Nenhum usuário encontrado para esse email.") já são adequadas para mostrar diretamente ao usuário do WhatsApp.
Recursos relacionados
- Schema Prisma:
prisma/schema/whatsapp.prismano nest-api,WhatsappAccount,WhatsappOtp,WhatsappMessageIdempotency - Guard:
src/common/guards/bot-api-key.guard.tsno nest-api - Module:
src/modules/whatsapp/whatsapp.module.tsno nest-api - Fluxo de connect:
./connect-flow.md
Documento atualizado em Maio 2026