September–October 2026
A SECRETARY ON WHATSAPP
Last updated 23 October 2026
Dr. Coronado Daza is a nephrologist in Cartagena. He had a secretary. In retirement he let that go, to save the cost, and thought he could take the phone himself. Patients wrote on WhatsApp and got no reply.
He does not carry the work phone. A retirement day is not a day for watching it. He wants a ping on his personal phone only when he actually has to act. I planned the secretary, I built her, and I tested her. She texts short, one thing at a time. That was his requirement. It was mine too.
Her job is the same as a human secretary. She receives information, she gives information, she completes the request, and she notifies him. Medical advice is forbidden. She does not try to be the doctor.
The questions were the usual ones, and the answers were the usual ones. Price. A conventional appointment or telemedicine. Where to go. An invoice. When is the next hour. Payment was still a transfer. After someone said yes, he still had to register the patient in SaludTools. She does that registration now. He is not in the thread repeating the same answers.
Before this, patients booked themselves on Nefrología en línea. That self-service app is gone. It worked, surprisingly well. In retirement the site became a static page about him. This is the evolution: the same job, done by chatting.
One line, if you want the stack: OpenAI Agents SDK, WhatsApp Cloud API, SaludTools API, MongoDB, Next.js, Pushover, and all written in TypeScript.
01
LET'S TALK ABOUT TIME SAVED
These times are estimates. I took how long one patient took him, and multiplied. A normal week is at least ten inquiries, and more since he is listed on Top Doctors. Those inquiries do not all become bookings. The ten-patient picture is a morning in which ten people go through the whole path. That morning is already a work day the old way.
BEFORE
- Patient writes
- About 24 hours of silence
- The usual questions and the usual answers, back and forth, about 10 minutes
- They send the transfer picture. He checks the content and the bank. Then it is fine.
- He sits in SaludTools: the patient, the appointment, a check that it is right. About 20–30 minutes. He was not fast at this.
AFTER
- Patient writes
- She answers at once. The frequent questions are already in her knowledge.
- They send the transfer, a picture or the details. The hour is not held yet.
- He checks the content and his bank statement. If the money is there, he types pago confirmado. About 2 minutes.
- She creates the patient and the appointment in SaludTools.
Estimated from one patient. A day of silence, about 10 minutes of the same questions, then 20–30 minutes in SaludTools. After: an answer at once, about 2 minutes for him to confirm the transfer, and she creates the patient and the appointment.
TEN PATIENTS
BEFORE
Ten times the back and forth is about 100 minutes. Ten times in SaludTools is 200–300 minutes. Together that is 300–400 minutes: five hours to six hours and forty minutes. One morning. That is a work day. Retirement is gone.
AFTER
Ten payment checks are about 20 minutes. The answers were already instant. She creates the patient and the appointment in SaludTools. He only confirms the payment happened, after he has checked the content and the bank.
02
TWO WAYS TO FINISH
Two secretaries. WhatsApp is this case. The website is the simple one. Top Doctors is a directory, with its own clients. The site links there. That link is not this integration.
This is the one that books. The patient writes the practice number. She asks conventional or telemedicine, then offers hours a tool returned. After they pick a slot, she asks for the document. The phone is who is writing. The document is who the appointment is for.
Payment is still a transfer. In Colombia that usually arrives as a picture, or as the details. Those files stay in WhatsApp. She does not read the photo. From the conversation she can tell a proof was sent, and she notifies administración. That is the word he asked for. He checks the content and the bank.
The hour is not reserved while he checks. Another patient can take it. When the money is there, he types pago confirmado in the Business app. Then she creates the patient and the appointment in SaludTools. If that hour was taken, the create fails and she offers another hour.
THE WEBSITE
The website secretary answers. There is no Meta app, no webhook, and no business number. The thread lives in localStorage, in that browser. The server does not store it. It is a record of that chat, on that device.
She does not book, register a patient, or take payment. She can take an inquiry: a very urgent request, or an invoice for a previous consultation. She sends one Pushover, confirms to the person that she did, and gives a reference id plus the email for documents. The doctor is pinged only when he has to act. She does not open a time. People who want the directory stay on the Top Doctors link.
03
BEFORE YOU START
Two requirements block this WhatsApp integration if you skip them. Have them before you treat the agent as the hard part.
META BUSINESS ACCOUNT
Have the legal documents, the checks Meta asks of a developer or partner, and the practice number ready. Then finish the app Live. That is how a patient’s own phone reaches the webhook. Dashboard Test is how I watched events arrive. On the real thread I tested replies, booking, analog pay, and the tool calls.
COEXISTENCE
The same number stays in the WhatsApp Business app and on the Cloud API. He still types in the Business app. pago confirmado arrives as that app’s message echo. The human and the agent share one number. Coexistence is required for this design, because the approval is him typing in the thread.
04
THE SYSTEM
The backend handles each incoming WhatsApp message on its own. A patient text arrives on messages. pago confirmado arrives on smb_message_echoes. That second event is what Meta sends when someone types in the WhatsApp Business app. A patient typing the same words is a different event. I tested that. They cannot approve their own payment.
The backend keeps the conversation state for that WhatsApp number, encrypted, for 7 days. The next message loads that state, so she still knows where the chat was. After 7 days the record is wiped and she starts new. A booking usually resolves inside the week, so that window fit. The phone is the thread. The document is who the appointment is for. One number can book for a father, and then for themselves.
Appointments are cached off the chat. SaludTools allows 15 calls a minute, for the whole account, so the tool does not ask the API on every turn.
A PATIENT WRITES
PATIENT
Texts the practice number
MESSAGES
The patient webhook
THREAD
Encrypted state, 7 days
SECRETARY
Policy, knowledge, tools
SALUDTOOLS
Patient, then appointment
REPLY
Back on WhatsApp
HE CONFIRMS THE PAYMENT
DOCTOR
Types in the Business app
ECHO
smb_message_echoes
PENDING
Only if the thread is waiting
TOOLS
create_patient, then the appointment
REPLY
The booking id, on WhatsApp
Policy. She has to sound like a person on WhatsApp. Short, one thing at a time. I tested her with weird questions, and with the questions out of order. If a detail is missing, she asks for that, the way a person would. She does not answer with a list of empty fields. Nobody reads a long text on a phone. A person steps in only when she really has to, and she does not invent a price, an hour, or a yes. When I want her to behave differently, I change this file.
Knowledge. The other file is just the facts. What he offers, what it costs, which days he sees people, and when he is away. I write it by hand. A vacation is a few lines. A bigger practice would want an admin for that.
Tools. A tool is the part she does, not the sentence she sends. Look up a time. Register someone. Note that a payment came in. If that step never runs, it did not happen.
The thread stores a payment status: pending, approved, or none. She writes pending when they say they paid. The phrase only approves while that status is pending. He types pago confirmado. That line is written into the thread, and it wakes her to finish the registration and the appointment. After the appointment is confirmed, the status returns to none, and the same chat can book again.
When someone writes, she is working from those two files. She still has to do the thing, not only say it.
THE TOOLS
- 01
check_appointment_by_date - 02
find_next_appointments - 03
read_patient - 04
create_patient - 05
create_appointment - 06
request_payment_information - 07
record_payment_pending - 08
request_human
Curious? After pago confirmado, and only while the thread is pending, she calls create_patient, then create_appointment, then confirms the patient with the booking id.
ONE BOOKING
The order of a booking. The names and the numbers are left out.
- 01
PATIENT
Asks for an appointment.
- 02
SECRETARY
Asks conventional or telemedicine.
- 03
PATIENT
Chooses one.
- 04
TOOL
find_next_appointmentsreturns the free hours. - 05
PATIENT
Picks an hour.
- 06
SECRETARY
Asks for the document.
- 07
TOOL
read_patient. If they already exist, she has their information and does not ask again. - 08
PATIENT
Sends the transfer. She calls
record_payment_pendingand notifies administración. - 09
DOCTOR
Types pago confirmado. That line wakes her.
- 10
TOOL
create_patientonly when they are new, thencreate_appointment. - 11
SECRETARY
Confirms with the booking id.
THE HOUR
SaludTools is the authority when the appointment is created. MongoDB holds a copy of the calendar, refreshed off the chat. After a successful create, that appointment is written into the copy. The hour is not held while he checks the content and the bank. If someone else took it, create fails and she offers another hour. A conventional visit and telemedicine are two prices on one agenda. They share his time.
TWO WRITES
create_patient and create_appointment are separate calls. A retry may call create_patient again. SaludTools rejects a duplicate patient. A patient row that already exists still stands if the appointment call fails. A 429 from SaludTools is retried 3 times, after a one-minute wait. That wait was their advice, and it happens on the turn she is already in. The reply is slower. On WhatsApp that still feels like a person.
THE PROOF
Photos and documents stay in WhatsApp. We do not save them. The model does not read the transfer image. From the conversation she treats a proof as sent, and she notifies administración. He checks the content and the bank.
A SECRETARY
On WhatsApp, a medical question, including an urgent one, gets a request to book. She does not advise. A first message that asks for a person still gets one try from her. The price, the hour, or the address might be the whole issue. If they insist, she sends one Pushover and she tells them she sent it. An inquiry that should reach him now belongs on the website secretary.
05
HOW IT GREW
ENVIRONMENT
This was not a usual LLM happy path. On the website, the browser keeps the thread. WhatsApp does not. I built a fake WhatsApp from day one, because the real channel is hard at the start. The mimic hid the real contract: each message on its own, no history, no staff role. LLM memory and the webhook are not good friends. The mimic is where I replayed the problems.
CONTRACT
A scenario suite. The field calls these evaluations. I learned that name here. Required tools, and lines she must not say. The one that taught me: she said the payment was pending review and never called record_payment_pending. The scenario failed. The sentence would have passed.
CHANNEL
The webhook is how she understands the patient, and how she receives his reply, on the same WhatsApp thread. Coexistence keeps that number in the Business app and on the Cloud API. The patient arrives on
messages. pago confirmado arrives onsmb_message_echoes.INTEGRATE
SaludTools, the tools, Pushover, and the analog payment check. The date search did not search a day. It matched one exact appointment, so busy hours came back free. And 15 calls a minute, for the whole account, so the agenda moved to a cache off the chat. After a create, that row is written into the same cache.
LIVE
Replies, booking, analog pay, and the tool calls were tested on the real thread. The main work is finished. It is live in production, and support continues.
06
MISTAKES WORTH STEALING
The useful part is the mistakes. Each one showed up in a real conversation or a scenario, and each one changed where the truth lives.
PRETTY TEXT CAN STILL BE A BROKEN PRODUCT
Open the trace. A kind sentence is not proof the tool ran.
What the patient wrote
“I already paid.”
CHAT
She says the payment is pending review.
TRACE
The record tool never ran.
THE MODEL MUST NOT OWN THE CALENDAR
She may know the open days, and she may not know which hours are free.
Patients ask when there is an opening. She may know the rules: which days are open, and when vacation is. She may not know the calendar. The booked hours live in the record, and a tool has to check them. If the hours live only in the prompt, she will offer Tuesday at 8:00 on a closed day, a vacation, or a full morning.
A conventional appointment and telemedicine are two prices on one agenda. They share his time. When the lookup fails, she apologizes and offers another date. If the tool did not run, she has no hour to offer.
“As soon as possible” once offered 8:00 on a morning that was already 9:40, then 10:00 the same day. The scenario only checked that a tool name appeared. It could not feel the clock in Colombia. The old patient portal already refused same-day booking, tomorrow at the earliest. That rule now lives in the tool. “Next week” is one mapping inside the tool: the next calendar week, the open weekdays, today’s date in Colombia, then the free hours. She offers those hours. She does not quiz them for a day.
- 01
RULES IN CODE
Open days and vacation, edited by hand.
- 02
THE TOOL READS MONGODB
Cached appointments. A copy.
- 03
SHE OFFERS ONLY THESE
The free hours the tool returned.
- 04
CREATE
Writes SaludTools, then the same row into MongoDB.
FIND A WAY AROUND THE API
An LLM will call the API many times. The API may not like that, or the method may not do what it says. You adapt.
She would check the day, then check again, and a scenario would check too. SaludTools allowed 15 calls a minute, for the whole account. One conversation could spend them and leave the next one unable to read the agenda. The tool does not ask the API on every turn. Patients and appointments stay in a MongoDB cache, refreshed off the chat. After a create, that appointment is written into the same cache, so the next question does not offer a slot just taken. The cache is a copy. SaludTools is the authority when the appointment is created.
The method was the other half. Search by date was not a search for the day. Those fields meant one exact appointment, so 8:00 and 9:00 came back free when those visits already existed. There was no method for what she needed. The tool loads the agenda, filters the day, and offers what is still free. She still only asks which day and which modality. The same shape fits any API you put behind an LLM.
A NUMBER IS A THREAD, NOT A PATIENT
The phone is who is writing, and the document is who the appointment is for.
The phone is who is writing. The document is who the appointment is for. A family phone, or “this one for my father, then one for me,” makes one number equal one patient, and that is too strict. The state of the chat is saved, encrypted, for 7 days. While it is there, she can see who they were talking about. After 7 days it is wiped, and she starts over. Nothing is summarized and kept.
WhatsApp does not hand you the chat. The backend handles each incoming message on its own. The patient arrives on messages. He arrives on smb_message_echoes, when he types pago confirmado in the Business app. That line is written into the thread, and it wakes her. The echo is the approval, and only while the thread is pending.
PATIENT WROTE
Arrives on messages.
DOCTOR WROTE
pago confirmado arrives on smb_message_echoes.
THE THREAD
Encrypted for 7 days. Then it is wiped.
TOO MANY RULES MAKE HER WORSE
Keep the prompt small. One subject at a time, and only the lines that moment needs.
I kept adding rules, and sometimes the replies got worse. A longer prompt was not a safer one. Keep each subject on its own, and give her only the lines she needs right then.
Payment instructions were the example. They lived in the general knowledge, so every chat carried them. It is better to take them out of there and make a tool that returns those instructions only when someone asks how to pay. I wanted to do the same with the other instructions. She was already reliable, so I left them. I encourage you to do it.
LET HER TRY BEFORE A HUMAN
A person has the right to a human, so that choice is always there. First, let the secretary try.
On WhatsApp, a medical question, including an urgent one, is still a request to book. She does not advise.
People ask for a person. Sometimes that is the first thing they write. I think that choice is their right, so a human has to be there. One way to reach someone, always.
I also wanted them to give her a chance. The issue might be a price, an hour, or where to go, and she can answer that. At the start she treated the first ask as the handoff. A human was called before she had tried. That was too soon.
So she stays and helps. If they insist, many times, and she recognizes that they still want a person, she sends one Pushover and she stops. She tells them she notified. She does not keep talking over a choice they already made.
07
HOW I TESTED
I tested her with scenarios. A scenario is a short conversation. It says which tools she must call, and which tools she must not call. I ran the same ones again after a change. I did not keep a single pass total.
Someone says they already paid. She must call record_payment_pending. She must not stop at a sentence that says the payment is pending review.
Someone asks for a person the first time. She must not call request_human. If they insist, many times, she must call it.
Someone asks for the next hour. She must call find_next_appointments. She must not offer an hour the tool did not return.
Three things sat at the core of the project. I kept coming back to them.
TRACING
OpenAI traces are the record of a run. A trace includes the text of the turn and the tool arguments. There is no scrubber. Photos and documents stay in WhatsApp. The saved conversation is encrypted and wiped after 7 days. That wipe does not delete the traces. Open the trace and look for the tool call. A kind sentence with an empty trace means that step did not happen.
GUARDRAILS
The guardrail is another AI. I like to break the assistants I find on the internet. I ask for something that has nothing to do with the job, and I watch them wander off. I know those questions, so I built this one to catch them, and I made it carefully. Another model catches a question that is not about the practice.
SCHEMAS
Every tool has an input schema and an output schema. It costs more tokens. It is worth it. The input says which fields the call needs. She can still use the tool while one of them is missing. The output then comes back as an error that names the field. She says what is missing, she asks, and only then does she call the tool again.
08
WHAT I WOULD REPEAT
The mistakes above are the long version. These four are what I would repeat.
- 01
STATE
The phone is a thread. The document is the patient. Payment status lives on that thread: pending, approved, or none. After the appointment is confirmed, it returns to none. Conversation state is encrypted for 7 days, then wiped. The model may know the rules. It does not know which hours are free.
- 02
DETERMINISTIC TOOLS
A kind sentence is not a finished booking. Open the trace. The calendar, the patient, and the appointment live in tools. SaludTools is the authority at create. The model collects what the person means. The tool keeps the other system’s exact names. create_patient and create_appointment are separate. A retry may call create_patient again. SaludTools rejects a duplicate patient. A 429 waits a minute and retries.
- 03
SAFETY
She is a secretary. On WhatsApp, a medical question, including an urgent one, is a request to book. A person can insist, and then one Pushover, and she tells them she sent it. The website secretary is the one that notifies him for an inquiry. Photos stay in WhatsApp. pago confirmado is the Business app echo, and only while the thread is pending. Another model catches questions that are not about the practice.
- 04
TESTING
Short scenarios. Name the tools that must run, and the tools that must not. Run them again after a change. I did not keep a single pass total. A scenario that only checks a tool name can still miss the clock in Colombia. Read the trace.