← Back to Blog
Blog Post
Automation

The Bug That Taught Me More Than the Build

Mira booked the appointment perfectly, then apologized for failing to book it. Chasing that ghost bug across a real healthcare API taught me more than the build itself.

GGuiller · July 2026 · 9 min read

I spent an entire afternoon fixing something that was never broken.

Here is the scene. I am testing Mira, the AI dental receptionist I built for a client. I open the chat on the site, type “I’d like to book a cleaning,” give my fake patient details, pick a time, and confirm. Mira thinks for a second. Then she says:

"I'm sorry, something went wrong trying to book your cleaning. I can try again, or you can call the office."

So I go check. I open NexHealth. The appointment is there. Correct patient, correct time, correct provider. It booked perfectly.

Mira booked the appointment and then apologized for not booking the appointment.

That bug ate hours of my life, and honestly, it taught me more about building real systems than any tutorial ever did. Let me back up and tell you the whole thing, because the road to that moment was full of small humbling lessons.

What I was actually building

Mira is an AI receptionist for a dental clinic in Oak Lawn. She lives on their website as a chat widget and handles the front desk work that usually gets missed: booking appointments, moving them, cancelling them, checking status. All day, all night.

The part I cared about most was that she had to be real. Not a chatbot that collects a form and emails someone. When Mira books you in, it lands in the clinic’s actual scheduling system.

That system is NexHealth, a healthcare scheduling API that dental offices genuinely use. Which meant before I could build anything clever, I had to learn to talk to it.

The tickets and wristbands phase

Here is the first thing I did right, and I only know it was right because of how much pain it saved me later.

I did not open n8n. I opened Postman.

I wanted to make every single API call by hand first. Authenticate. Pull patients. Pull providers. Pull open times. Book something. All manually, one click at a time, before automating a single step.

The auth flow clicked for me when I stopped thinking of it as code. NexHealth gives you an API key. You do not use the key to do things. You trade the key for a token, and the token is what actually opens doors. The token dies after an hour, so you go trade for a new one.

Ticket at the door. Wristband inside. Wristband expires, go get another.

Once I saw it that way, the whole thing stopped being intimidating. And when I later built it in n8n, I knew every workflow had to start by grabbing a fresh token, because I had felt that expiry in my hands.

Then the wall

My first real call came back with this:

404 Resource not found

No detail. No hint. Just, the thing you are asking for does not exist.

I had my API key. I had my location ID. I had my provider ID. Everything looked correct. I tried adjusting parameters. I read the docs again. I tweaked the date range. Nothing.

The problem was my subdomain. NexHealth looks up your clinic by subdomain first, and I had typed something that looked right instead of copying the exact one from my portal. It was close. Close is a 404.

The provider ID was wrong too. I had 503460753. The real one was 503460747. Six characters identical, one digit off, completely invisible unless you compare them side by side.

I fixed both. Slots came back. I still remember the small rush of seeing an actual list of open appointment times sitting there in the response.

That is the first lesson I actually internalized. A 404 in an API almost never means “your logic is broken.” It means “the thing you named does not exist.” Check your names before you check your code.

Mira's Check Dental Slots n8n workflow, showing the config step for subdomain, location_id, and provider_id before calling NexHealth's available slots endpoint.
The Postman session is long gone, but this is the exact tool: Mira's Check Dental Slots node, config'd with the subdomain and provider_id that were the whole bug.

The small cuts

After that came a string of little errors, each one annoying, each one teaching me something specific about how APIs actually behave.

Missing parameter start | Missing parameter end.

I was fetching a patient’s appointments without giving a date range. NexHealth will not just hand you everything. It wants to know the window. Fair enough.

Expected string, received number.

I typed an operatory ID into a test field and n8n decided it was a number. The tool wanted text. Two data types that look identical to a human, completely different to a machine.

Provided appointment_type_id was not found to be configured for the requested slot.

This one was interesting. I was passing an appointment type that was not set up for that specific provider’s availability. My fix was to just stop sending it and let NexHealth use the default. Sometimes the right move is to remove the thing, not fight it.

None of these were hard once I understood them. But each one cost me time, and each one only made sense after I stopped assuming and started reading exactly what the error said. APIs are usually telling you the truth. You just have to listen literally.

The ghost slots

Then a stranger one showed up.

This time is no longer available, please go back and choose another time.

Which is confusing, because Mira had just pulled that time from NexHealth seconds earlier. It was in the list. It was open. And now it was not.

I dug into the execution logs and compared what the availability endpoint returned against what the booking endpoint rejected. They matched. Same time, same room, same provider.

The mismatch was underneath. The availability call was handing back 15 minute openings. A real appointment needs a longer block. So a time could genuinely be “open” for 15 minutes and still be unbookable, because there was not a full appointment’s worth of room behind it.

Two systems, both correct, disagreeing because they were measuring different things.

I aligned them so availability only returns slots long enough to actually hold an appointment. And I gave Mira a fallback: if a time gets taken between the moment she offers it and the moment she books it, she does not panic or fail. She quietly checks again and offers new times, exactly like a receptionist who says “oh, that one just got taken, how about Thursday?”

That fallback ended up being one of my favorite parts of the build. Not because it is clever, but because it is honest about how the real world works. Things change between the asking and the doing.

Back to the apology

Which brings me back to Mira apologizing for a booking she successfully made.

I pulled the execution log for that run and read it line by line. Get token, fine. Find or create patient, fine. Create appointment, code: true, real appointment ID returned. Perfect.

And then the run just… stopped.

Not an error. No red node. It reached the Google Sheets logging step, wrote the row, and ended right there. The steps after it, the Slack notification and the final step that tells Mira “this worked,” never ran.

The logging node had done its job and passed nothing forward. Empty output. And in n8n, when a node hands the next node nothing, there is nothing left to do, so the chain quietly ends.

So Mira never received the success signal. From her point of view, she asked for a booking and got silence back. And what does a careful assistant do when she gets silence? She assumes it failed and tells the patient the truth as she understands it.

She was not malfunctioning. She was being honest with incomplete information.

Mira's shared Notify and Log n8n workflow, showing the Log to Sheet step feeding into the Notify Slack step.
The original execution log is gone, but this is the shared Notify & Log workflow it exposed: Log to Sheet feeding Notify Slack, the exact handoff that was quietly passing nothing forward.

The fix took about ten seconds once I understood it. Tell those nodes to always pass data through, so the chain always reaches the end.

Hours of debugging. Ten second fix. That ratio is basically what building systems feels like.

The real lesson though is about where I was looking. I spent so long staring at the booking logic, convinced the API integration was broken, when the API integration had been working the entire time. The failure was in how the result traveled back. It is not always the loud part that breaks. Sometimes it is the quiet handoff between two steps that both look fine on their own.

The part I did not want to get wrong

While all this was happening, there was a whole other layer I was thinking about, and it is the one that actually matters most in healthcare: patient data.

This is a dental clinic. Real names, birthdays, phone numbers, appointment histories. That is protected health information, and you do not get to be casual about it.

So I made a rule early and never bent it. Patient information stays inside NexHealth.

When Mira books someone, the clinic gets a Slack alert and a row in a log sheet. But those only ever carry the appointment ID, the time, the provider, and where the booking came from. No names. No emails. No birthdays. If someone got into that Slack channel or that spreadsheet, they would learn that an appointment exists. They would not learn who it belongs to. The identity lives in the system that is actually built to protect it.

I also got specific about language, because in healthcare the words matter. There is a real difference between “HIPAA compliant” and “HIPAA ready,” and pretending otherwise is how you lose a client’s trust.

Compliance is not something a demo can claim. It comes from signed agreements, the right infrastructure, real policies. What I can say honestly is that I built the architecture so it is ready to be compliant: swap the language model to Azure OpenAI, which signs a business associate agreement and runs the same models inside HIPAA eligible infrastructure, self host n8n, and keep patient data where it belongs. Same system, compliant stack.

I could have written “fully HIPAA compliant” on my demo. It would have sounded stronger. It also would have been the kind of claim that falls apart the second someone who actually works in healthcare asks a follow up question.

Saying the accurate thing is not a weaker pitch. It is the thing that makes the rest of your pitch believable.

What I actually took away

If you are about to integrate an API you have never touched, here is what I would tell you, having just lived it.

  1. 1Do it by hand first. Every call, manually, before you automate anything, so when something breaks later you know whether it is the API or your automation.
  2. 2Read the error literally. 404 means the name is wrong, 400 means the shape is wrong, 401 means your credentials are wrong.
  3. 3Check your IDs character by character. One digit cost me an hour.
  4. 4When something works but reports failure, stop looking at the part that works. Look at the handoff.

The build itself was maybe two days of actual construction. The debugging was longer, and I would not trade it. Anyone can wire nodes together when everything cooperates. You only really learn a system when it refuses to work and you have to figure out why.

Mira is live now. She books real appointments, at 2am, without anyone at the front desk. And every time I see a new row appear in that log, I think about the afternoon she booked an appointment perfectly and then apologized for it.

From the case study

24/7 AI Dental Receptionist

See the full Mira build: every NexHealth booking tool, the HIPAA-ready architecture, and how it all fits together.

NexHealthn8nAPIHealthcare AI
Ready to scale

Your systems should close, not cost.

Book a free 15-minute discovery call. I'll map where your process leaks and what to build first, no pitch, just a plan.

Free · No obligation · 24h response