Automação de Reservas Hoteleiras
Um recepcionista que trabalha 24h no WhatsApp, com o banco de dados como fiador de cada reserva.
Meu papel
Projeto e implementação, do schema do banco ao agente
O problema
Pousada pequena atende reserva no WhatsApp, na mão. As perguntas são sempre as mesmas — tem vaga nesse feriado, quanto fica, como eu pago — e chegam a qualquer hora, inclusive quando não tem ninguém pra responder. Uma resposta que demora duas horas é uma reserva que foi pro concorrente.
O outro problema é mais caro: sem um lugar único que mande na disponibilidade, dá pra vender o mesmo quarto duas vezes. Planilha e caderno não seguram isso, e sistema de gestão hoteleira pronto resolve o cadastro mas não resolve o atendimento — que é justamente onde a reserva se ganha ou se perde.
A solução
Montei um agente de IA no n8n que conversa pelo WhatsApp da pousada via Evolution API. Ele entende a intenção da mensagem e executa de verdade: cadastra o hóspede, consulta disponibilidade, cria a reserva, gera a cobrança e confirma o pagamento — tudo sem sair da conversa.
Cada capacidade do agente é um sub-workflow isolado que fala direto com o PostgreSQL por SQL parametrizado. O modelo não inventa dado nem escreve query: ele escolhe a ferramenta, o banco faz o resto. Isso mantém o comportamento previsível e auditável execução por execução.
Em paralelo roda um painel web (NestJS + React) que lê o mesmo banco, pra equipe humana ver o que o bot fez e assumir a conversa quando precisa. O banco é o contrato entre os dois — não existe estado escondido em nenhum dos lados.
Como funciona
- 01
Mensagem chega
A Evolution API recebe o WhatsApp do hóspede e dispara o evento MESSAGES_UPSERT no webhook do n8n.
- 02
Rajada vira uma mensagem só
Mensagem picada em quatro linhas seguidas não vira quatro respostas: um buffer com debounce acumula e só aciona o agente se ninguém escreveu por cima, com compare-and-swap por timestamp.
- 03
O agente decide a intenção
O AI Agent (gpt-4o-mini) lê a mensagem já com o número do remetente embutido e escolhe qual ferramenta chamar. A memória por número de WhatsApp mantém o contexto entre mensagens.
- 04
A ferramenta executa no banco
cadastrar_cliente, consultar_disponibilidade, criar_reserva, consultar_reservas, gerar_cobranca ou escalar_humano — cada uma é um sub-workflow com a query já parametrizada.
- 05
Cobrança e confirmação
gerar_cobranca cria o link no InfinitePay (Pix ou cartão) e o webhook de pagamento, exposto por túnel Cloudflare, confirma e atualiza a reserva sozinho.
- 06
Equipe acompanha ao vivo
O painel refaz a consulta em intervalo curto nas telas que refletem escrita do bot, então reserva criada no WhatsApp aparece pra recepção quase na hora.
Decisões técnicas
O banco barra o overbooking, não o código
Uma constraint EXCLUDE USING gist em reservas torna fisicamente impossível gravar duas reservas sobrepostas no mesmo quarto. Como o bot e a equipe escrevem ao mesmo tempo, checar disponibilidade na aplicação abriria uma corrida clássica: dois "tem vaga" simultâneos viram duas reservas. Deixando a garantia no banco, o agente recebe o erro e já oferece outro quarto na mesma conversa.
Webhook no lugar de polling
A confirmação de pagamento nasceu consultando o InfinitePay a cada 2 minutos. Depois que o túnel Cloudflare fixo deixou um endpoint público estável, virou webhook: confirmação na hora e zero requisição desperdiçada. O fluxo antigo ficou desativado no banco, documentado como fallback — não apagado.
Erro de Postgres vira português
Um filtro global no NestJS traduz o código do banco pra HTTP e mensagem legível — overbooking (23P01) e duplicidade (23505) viram 409, violação de check (23514) vira 400. A regra mora num lugar só, em vez de espalhada por cada controller.
A aplicação não é dona do schema
TypeORM roda com synchronize:false. O schema é compartilhado com o n8n, então nenhuma mudança pode vir do ORM: tudo passa por migration versionada, aditiva e idempotente. Isso evita que um deploy do painel derrube a automação.
Idempotência em duas camadas
O workflow deduplica por cliente + quarto + período antes de inserir e reaproveita reserva pendente existente. Por baixo, um índice único no banco é a rede de segurança contra corrida — porque o WhatsApp reentrega mensagem e a mesma intenção pode chegar duas vezes.
Escalar pra humano é estado no banco
Quando o assunto sai do script, o agente vira o status do cliente de Bot pra Humano e para de responder. A equipe assume pelo painel, no mesmo número — sem transferir o hóspede pra outro canal e sem perder o histórico.
Dashboard sai de SQL, não de ferramenta de BI
As métricas de operação vivem em views versionadas no próprio Postgres. Sem BI no stack: uma dependência a menos pra manter, e o mesmo número serve pro painel e pro psql.
O que o sistema faz
- Cadastra o hóspede na primeira mensagem, sem formulário
- Consulta disponibilidade por período e tipo de quarto
- Cria a reserva e devolve o link de pagamento na conversa
- Aceita Pix e cartão de crédito via InfinitePay
- Confirma o pagamento por webhook e atualiza a reserva sozinho
- Expira automaticamente reserva pendente que não foi paga
- Passa o atendimento pra equipe quando o assunto sai do script
- Painel com reservas, quartos, clientes, produtos e consumos extras