WhatsApp chatbot · n8n
Ajuda Animal: animal abuse reports over WhatsApp
A WhatsApp channel for reporting animal abuse, in an IFSP extension project with the Jacareí city government. I built the n8n bot and its integration with the system's API.
Private code: university extension project in partnership with the city government. Happy to walk through it in an interview.
- WhatsApp channel: n8n bot, API integration and conversation sessions
- 2026 · IFSP Jacareí extension project
- in progress
- n8n · Evolution API · PostgreSQL · Docker
30-second summary
- Problem
- The city's Animal Protection department got reports through calls and loose messages, with no location, no photo and no way to follow up.
- Solution
- A WhatsApp bot walks the citizen through the report (type, animal, location, photo and anonymity), saves it through the system's API and replies with a protocol number.
- Result
- A working prototype, demoed end to end on 10/19/2026: the report lands on the team's map and the citizen follows it by protocol. Not in production yet.
Context and problem
Ajuda Animal ("Animal Help") is an extension project of IFSP Jacareí (a federal institute) with the Jacareí city government, for its Animal Protection department. It's a team project: a system to receive and follow up reports of animal abuse and abandonment.
The department needs reports with useful data to act on: where the animal is, what is happening and a photo. Calls and loose messages arrive incomplete and can't be followed up. WhatsApp is the channel people already use, but a free-form chat doesn't produce structured data.
My role
I was responsible for the WhatsApp channel: the n8n chatbot, its connection to the system's back end (core-api) and the conversation session database. I also carried the data and photos from WhatsApp into the system, where the report is created. The dashboard and the API belong to the rest of the team.
How it works
The conversation is a state machine. Each phone number has a row in the whatsapp_sessions table, with the current step and the partial answers.
- Menu1, 2 or 3
- Type5 options
- AnimalFree text
- LocationPin or address
- PhotoOr SKIP
- PrivacyAnonymous or with contact
- ProtocolPOST /reports
In the database, the steps are INICIO, MENU, DENUNCIA_TIPO, DENUNCIA_ANIMAL, DENUNCIA_LOCAL, DENUNCIA_FOTO and DENUNCIA_LGPD. The menu's other branch is CONSULTA_PROTOCOLO.
- Menu and exits. 1. Report · 2. Check a protocol · 3. General guidance.
menu,voltar(back) orcancelar(cancel) go back to the menu at any point. - Normalized input. The workflow only handles incoming messages (it ignores groups, status updates and its own messages) and extracts the text, photo caption, location and phone number.
- Location. The citizen shares the spot on WhatsApp and the coordinates go into the report, which lands on the map. A typed address works too.
- Mapping to the API contract. A code node builds the
POST /reportsbody from the session. The bot's 5 options becomeABUSEorABANDONMENT(the detail goes in the description), and the free-text animal ("caramel dog", "rabbit") becomes a species through keywords. - Protocol. The API generates a readable code like
DEN-2026-7K3QX9, with no 0/O or 1/I/L so citizens don't mix them up, and the bot sends it right away. - Lookup. The citizen types the protocol (case, spaces and dashes don't matter) and gets the type, status and last update date. No address, description or contact details.
- Errors. If the API fails, the bot says so, keeps the answers and lets the citizen try again.
Try it
Chat with a replica of the bot I built in n8n: file a report, get the protocol and check its progress. Alongside, see each message's path through the workflow, the session in the database, the API call and the report on the city team's dashboard.
Simulation with fictional data · no WhatsApp, no database
What matches the bot and what is simulated
- Same as the bot: the state machine (including what the workflow accepts at each step), the menu, back, cancel and reset commands, how the POST /reports body is built, the protocol format and the normalization in the lookup.
- Simulated: there is no WhatsApp, n8n or database. Everything runs in your browser and is gone when the page reloads. The phone number, places and photo are fictional.
- The "Simulate an API failure" box shows the workflow's error messages: the answers are kept so you can try again.
- The "Advance status" button plays the team's role on the dashboard; the protocol lookup shows the change.
- The original bot is in Portuguese. This version translates its messages, the commands and the species keywords, and drops the emojis.
Stack
- My work: n8n, Evolution API (WhatsApp), PostgreSQL, REST integration with a service key, and Docker.
- The system: monorepo (pnpm + Turborepo), NestJS + Prisma + PostgreSQL/PostGIS API, React + Vite + Leaflet dashboard, Docker Compose and CI on GitHub Actions.
Decisions and trade-offs
The bot doesn't write to the system's database
Only the API touches the business database. n8n uses the same REST endpoints an official gateway would, authenticated with a service key. That keeps the rules that matter (protocol, anonymity, validation) in the API, which is tested. The conversation state lives in a separate table.
The n8n workflow sits outside CI (lint and tests), so no important rule can live in it.
n8n and Evolution API for the prototype, recorded in an ADR
The official channel (Meta's WhatsApp Business) depends on the city government. To demo the full flow without waiting for Meta's verification, n8n plays the gateway's role. Since the API contract is the same, switching channels later doesn't change the back end or the dashboard.
Evolution uses an unofficial WhatsApp connection: test SIM only, never the city's number.
Privacy law (LGPD) from the start
Reports are anonymous by default: contact details are only kept if the citizen picks option 2. The protocol lookup exposes no personal data, the photo is stored as a reference (never as base64 in the session), and there is a ready query to delete sessions abandoned for over 30 days, still to be scheduled.
Result
A working prototype, demoed end to end on 10/19/2026 (the video at the top):
- a full report over WhatsApp (type, animal, location, photo and privacy);
- a protocol generated and sent right away;
- the report on the dashboard map, with the citizen's photo;
- progress updated on the dashboard shows up in the WhatsApp lookup.
The project's next steps: the official channel (Meta's WhatsApp Business), the city's server and an automatic alert to inspectors for every new report. The node for that alert is already in the workflow, but disabled.
What I learned
- Integrating through a contract (API + service key) instead of going straight to the database lets you swap the channel without touching the rest of the system.
- A public service chatbot has to be predictable: a numbered menu, exit commands and error messages that don't lose what the citizen already answered.
- Privacy is an architecture decision, not an interface detail.