← Blog

Chat agents

Veterinary chat agent on Animana: a Nijmegen case study

A 22-person vet chain in Nijmegen runs 1,420 weekly bookings through a chat agent that triages spoedgevallen in 90 seconds and never books a euthanasie alone.

Jacob Molkenboer· Founder · A Brand New Company· 10 Dec 2025· 8 min
Brass bell, folded linen card with green ribbon, ceramic dish with collar tag, wooden appointment block on bone paper.

It is Friday at 19:42. The receptionist at the Nijmegen practice has gone home. A dog walker types into the chat widget on the homepage: "Mijn hond heeft net iets gegeten van de struik in het park, hij kwijlt extreem en kan niet stilstaan. Kan ik nu langskomen?" The phone is forwarded to a service that will charge €38 to relay the message in the morning. The owner does not want morning. The dog does not have morning.

That moment used to cost the chain three things at once: a lost client who drove to the emergency clinic in Arnhem, a missed €240 spoedconsult, and the slow grind of a praktijkmanager spending Saturday morning apologising on the phone. Eight weeks ago we shipped a chat agent that closes that gap. This post is what it actually does, what we got wrong, and the two design choices that keep it from killing a dog.

Why a vet chain is the worst test bed for a generic agent

Most chat-agent demos you see are stitched onto a SaaS dashboard or a Shopify storefront, where the worst possible outcome is a wrong refund and an angry email. A veterinary chain inverts that. The lower bound of a mistake is a missed seizure. The upper bound is a euthanasie booked by an upset family member without the dossier showing why the lead vet had been holding off.

The Nijmegen chain has four clinics, 22 staff (six vets, the rest assistants and a small front desk), and runs everything on IDEXX Animana, a PIMS the practice has used since 2013. Animana is competent at what it does. It also predates the modern API era. Their public endpoints surface the patient record and the appointment slots, and almost nothing else we would want for a triage workflow.

So we had two constraints from day one: the agent had to talk to clients in a tone that would not embarrass a praktijkmanager, and it had to write into Animana without breaking the practice's invoicing chain.

What the agent actually handles

In an average week the agent picks up 1,420 conversations through three surfaces: the website widget, the WhatsApp Business number, and an inbound email parser. About 71% of those are afspraak-vragen (booking, rescheduling, asking when the practice opens on a feestdag), 18% are repeat-prescription requests, and the remaining 11% split between insurance questions, behavioural questions ("my cat is hissing at the new baby"), and one specific category that warrants its own design: animal emergencies.

The agent answers everything in Dutch by default, English on toggle. It never invents medical advice. The first system instruction we wrote, before any of the orchestration code, was that the agent must always assume the human knows their animal better than it does.

A triage score, not a triage decision

The hard problem in any vet chat agent is reading the difference between "she's been a bit off her food" and "she's been off her food and her gums are pale." The first is a tomorrow problem. The second is a now problem.

We do not let the model make that call alone. The agent runs a structured triage interview that maps to a numeric score from 0 to 6, weighted on respiratory distress, neurological signs, trauma, ingestion, and pain markers. The scoring matrix is built on the public AAHA practice guidelines for acute presentation and tuned by the lead vet over two evenings with a stack of past cases.

Anything that scores above 4 is parked into a dierenarts queue within 90 seconds. Parked means three things at once: a yellow card in the Animana dashboard with the chat transcript attached, a push to the on-call vet's phone, and an automatic reply back to the owner that reads, in plain Dutch:

Een dierenarts kijkt nu naar uw bericht en belt u terug
binnen tien minuten. Als uw dier acuut achteruit gaat,
rijd dan direct naar {dichtstbijzijnde 24/7 kliniek}.

The closest 24/7 address is computed from the owner's postcode against a small lookup table of clinics we maintain manually. Postcode lookup, not a model call. We do not want the agent inventing an address on a Friday night.

We measured the 90-second SLA against 412 escalations in the first two months. It held in 408 of them. The four that missed it were all the same failure mode: a long initial message from the owner that the model spent too long re-reading. We fixed it by capping the triage interview to one follow-up question when the first message contains more than three of our high-weight tokens (kwijlt, krampt, bewusteloos, bloed, ademen, and so on).

Takeaway

We do not let the model triage. We let it interview. A scoring matrix the vet can audit is what makes the system defensible.

The euthanasie gate

This is the part of the build I think about most often, because it is where a small UX decision either honours a family or wounds them.

Euthanasie bookings used to flow through the same public scheduler as everything else. A pet owner could, in principle, click into the website on a Tuesday night and book a euthanasie slot for Wednesday morning. The chain had never had a disaster from this, but the lead vet had been quietly uncomfortable with the arrangement for years. There were cases where the dossier showed an active treatment plan that the booking owner did not know about. There were cases where one family member was making a decision that another family member did not yet know was being made.

So we removed the public route entirely. The agent will discuss euthanasie. It will explain what the consultation involves, what costs to expect, and how the practice handles cremation arrangements (in partnership with the local dierencrematorium). It will not place the booking.

Every euthanasie request triggers a four-eyes review. The praktijkmanager opens the dossier in Animana, the lead vet for that patient confirms the booking is consistent with the treatment history, and only then does the practice call the owner back to schedule. In eight weeks the gate has blocked three bookings that, on review, the lead vet wanted to talk through with the family first. One of those conversations led to two more weeks of palliative care that the family later said they were grateful for.

Warning

If you build any agent that can write into a clinical or legal system, walk every irreversible action through a human before the agent can complete it. The cost of the extra step is one phone call. The cost of skipping it is not yours to pay.

Talking to a 12-year-old PIMS

Animana exposes a documented REST API for appointments, patient records, and invoicing. It also has surfaces that the public API does not cover and that the practice depends on every day, like the dossier pinboard where vets leave free-text notes for each other.

We built the integration layer in two pieces. The first is a thin Node service that calls the Animana API for the documented routes (booking, patient lookup, invoice line creation). The second is a queue: anything that needs to land in Animana but does not have an API for it gets written into a Postgres table with a pending_human flag, and the praktijkmanager works through it once a day.

create table animana_pending (
  id          bigserial primary key,
  kind        text not null,        -- 'dossier_note' | 'recall' | ...
  patient_id  text not null,
  payload     jsonb not null,
  source_chat uuid references chats(id),
  status      text not null default 'pending_human',
  created_at  timestamptz not null default now(),
  resolved_at timestamptz,
  resolved_by text
);

create index on animana_pending (status, created_at);

We resisted the urge to scrape the Animana UI to fake API access where it doesn't exist. We have done that on other projects and have lived to regret it every time the vendor pushes a UI tweak. The queue table is uglier in the architecture diagram. It has not broken once.

If you build against Animana, treat the documented endpoints as truthful but slow: budget around 1.4 seconds per call on the slot reader. We cache the next 14 days of open slots in Redis and revalidate every 90 seconds. The agent reads from cache. Writes go straight through.

What the numbers look like after two months

I will give you the ones that mattered to the praktijkmanager.

Booked appointments per week: up from a baseline of 1,180 to 1,420. About two-thirds of the lift is captured demand that previously walked to the answering service and dissolved. One-third is rescheduling friction that the chat agent removes (an owner can now move an appointment in 30 seconds on a Sunday).

Phone minutes inbound: down 38% across the four clinics. The receptionist team gained back roughly nine hours per week, which they have used to run the recall-reminder workflow that was perpetually behind.

Triage escalations: 412 in 60 days. 27 of those were genuine spoedgevallen that arrived faster than they would have through the phone tree. The lead vet estimates that two of them were probably outcome-changing for the animal. I am not going to publish a number that calls itself "lives saved by chat agent." I will tell you that the lead vet wrote it on a whiteboard in the practice.

Euthanasie bookings: zero placed by the agent. Three blocked by the four-eyes gate. Total euthanasie consultations performed: unchanged from the prior period.

The thing we got wrong twice

Both times we tried to let the agent answer behavioural questions ("my cat suddenly hates the litter box") with a "this might be the reason" pattern, the lead vet pulled us back. The agent now answers behavioural questions with "this is worth a behavioural consult, here is what one costs, here are two times this week," and stops. The model wants to be helpful. The clinic does not need it to be diagnostic.

The shape of an agent that can be trusted with stakes

The lead vet said something in the kickoff that I have been chewing on ever since: "I do not need the chat to be smart. I need it to be careful." Careful is a discipline, not a model parameter. It is a triage scoring matrix that an auditor can read. It is a queue table that is honest about what cannot be automated. It is an irreversible action that is always routed through a human.

When we built the chat agent for the Nijmegen chain, the thing we ran into was that the safer the agent got, the more the practice trusted it with adjacent workflows we had not scoped. By month two they were asking us to plug the same triage spine into their wildlife rescue intake form. That is the right kind of scope creep. We do this work under AI agents, and the integration shape (queue plus four-eyes plus scoring matrix) is portable. If you operate any practice on a legacy PIMS and can describe one workflow that ought to be careful, that is enough to start.

The five-minute thing to do today is write down the one action your team performs that you would never want an agent to take alone. That is your four-eyes gate. Everything else can ship around it.

Key takeaway

We do not let the model triage. We let it interview. A scoring matrix the vet can audit is what makes the system defensible.

FAQ

Can a chat agent legally handle veterinary triage in the Netherlands?

The agent does not triage. It runs a structured interview and produces a score that a registered dierenarts reviews. The clinical decision stays with the vet, which is what Dutch veterinary practice rules require.

Why integrate with Animana instead of replacing it?

Replacing a PIMS that holds twelve years of patient dossiers, invoicing history, and lab integrations is a multi-year project. Wrapping it with a careful agent layer ships in weeks and preserves every existing workflow.

What stops the agent from booking a euthanasie consultation directly?

A hard rule in the orchestration layer. Euthanasie intents never reach the Animana booking endpoint. They land in a review queue that requires the praktijkmanager and the patient's lead vet to both sign off before scheduling.

How do you handle out-of-hours emergencies the agent cannot solve?

Any triage score above 4 triggers a fallback message with the closest 24/7 clinic, computed from postcode. The on-call vet also gets pushed within 90 seconds, but the owner is told to drive immediately if the animal is deteriorating.

chat agentsai agentscase studyintegrationsworkflowoperations

Building something?

Start a project