The GoHighLevel practice of Yotomations LLCAI development, custom software and websites at yotomations.com
New to GHL? Start here
clinic integration case study

A webhook bridge between a specialty clinic EMR and GoHighLevel

A specialty clinic runs scheduling and charting in its EMR and patient communication in GoHighLevel, with nothing connecting the two. The build is a single verified webhook endpoint that routes every EMR event to its own handler, so the clinic's follow-up reacts to what the EMR already knows.

Two clinicians in white coats reviewing a tablet together

Watch the one-minute version

One door from a clinic EMR into GoHighLevel

How a specialty clinic's EMR events reach GoHighLevel through a single verified endpoint, with a handler per event type and nothing dropped.

Watch it on its own page, with the transcript.

How it fits together

One door into GoHighLevel, one handler per eventONE DOOR INTO GOHIGHLEVEL, ONE HANDLER PER EVENTClinic EMRPatients, schedule,chartingWebhook entryOne endpoint, signaturecheckedEvent switchReads the event typePatient handlersCreated and modifiedAppointment handlersCreated, moved, removedFallbackAnything unrecognizedGoHighLevelIntake, reminders,follow-upSlackUnknown events and errorssends eventsverifiedpatientappointmentunknownupdatesreminderssurfacedwrite back laterEvery EMR event arrives at a single verified endpoint, then goes to the handler for its own type. Anything unrecognized is surfaced rather than dropped.

At a glance

Client
Specialty clinic running an EMR and practice management platform
Systems
The clinic EMR, GoHighLevel, n8n, Slack
Solution
One verified webhook endpoint, a switch that routes each event type to its own handler, and a fallback that surfaces anything unrecognized
Services
Integrations, Automation

The situation

The clinic runs its day inside an EMR and practice management platform. Patient records, the schedule, charting, the parts of a clinic that cannot be moved somewhere else without disrupting care. Patient communication lives in GoHighLevel: reminders, follow-up sequences, reactivation campaigns, the messages that go out between visits.

The two systems had nothing between them. Anything that happened in the EMR stayed in the EMR until a person noticed it and did something about it somewhere else. A new patient record was created and no intake follow-up began. A visit moved and the reminder still went out against the old time. A visit was removed and the sequence carried on as though it was still coming.

That gap is where patient communication quietly goes wrong, and it goes wrong in the direction that costs the most: messages that contradict what the patient was told. The staff were the integration, and staff are busy.

What made it hard

The EMR is the system of record and stays that way. This is not a migration, and nothing about the clinical workflow changes. The integration has to fit around a platform that was not designed to be extended, and it has to fail quietly rather than block anything the clinic does.

The platform emits webhooks, which is the right foundation. What it does not hand over freely is the detail needed to build against them: the catalogue of event types, a real payload for each one, and the signing scheme used to prove a request actually came from the platform. Building against an assumed payload shape means rebuilding when the real one arrives, and building without signature verification means accepting anything that reaches the endpoint. Both of those are refusals rather than delays.

The third difficulty is that events are not equal. A patient record being created is not the same job as an appointment being moved, and neither is the same as an appointment being removed. Treating them as one stream produces a workflow with a long chain of conditions inside it, which is the kind of thing nobody can safely change six months later.

The solution

One entry point, many small handlers. A single endpoint receives every event the EMR sends. Before anything else happens, the request is checked against the platform's signature so an unverified call goes no further. What survives that check reaches a switch that reads the event type and sends it to its own handler:

  • Patient created, which starts the intake follow-up in GoHighLevel and creates the contact if it does not exist yet.
  • Patient modified, which updates the contact rather than creating a second one, so the history stays in one place.
  • Appointment created, which puts the visit into the reminder sequence that matches the visit type.
  • Appointment modified, which moves the reminders with it instead of leaving the old ones queued.
  • Appointment removed, which stops the sequence rather than letting it run at a patient who is no longer coming.
  • A fallback, which catches anything the switch does not recognize.

Each handler owns one job and can be read, changed or replayed on its own. That is the point of the shape: when the EMR adds an event type or changes a field, the work is one handler rather than an audit of everything.

The fallback matters as much as the named branches. An event nobody planned for is posted into Slack with its payload attached rather than dropped on the floor. That turns the unknown into a queue somebody can read, and it is how the next handler gets specified: from a real event that actually arrived, not from a guess about what the platform might send.

Slack carries the errors too. An integration that fails silently is worse than no integration, because the clinic keeps believing the follow-up is running.

Why this pattern fits businesses like this

Clinics and practices almost always have a system they cannot leave, and it is almost never the one their marketing lives in. The instinct is to replace something. The better move is to let the clinical system stay exactly where it is and make its events available to the communication layer, which is what a verified webhook endpoint and a handler per event type does.

The same shape covers any platform that emits events and any business that has to answer them: one door, one check at the door, one handler per thing that can happen, and a visible place for whatever nobody anticipated. It survives the vendor changing their mind, which is the part that usually breaks a custom integration built as one large workflow.

Where it goes from here

The current build receives and routes. Writing back to the EMR, so a booking made in GoHighLevel lands in the clinic schedule, is the phase after this one and needs the same documentation work on the write endpoints. If your clinic runs an EMR that your follow-up cannot see, tell us which system runs your schedule.

Yaakov (Koby) Oranski

Yaakov (Koby) Oranski, Founder and CEO of Yotomations

Certified GoHighLevel Admin and Partner. Koby designs CRM migrations, two-way integrations, billing systems and AI agents for service businesses, and writes from the engagements described on this site.

Published

Questions about this engagement

Does this replace the clinic's EMR?

No. The EMR stays the system of record for patients, the schedule and charting. The integration listens to what the EMR reports and lets GoHighLevel react to it, which is the opposite of a migration.

Why one handler per event type instead of a single workflow?

Because a single workflow becomes a long chain of conditions that nobody can safely change later. One handler per event type can be read, edited and replayed on its own, and a new event type is a new handler rather than a rewrite.

What happens to an event nobody planned for?

It goes to the fallback, which posts it into Slack with its payload rather than discarding it. That is also how the next handler gets written: from an event that really arrived.

What has to be in place before this kind of build starts?

The platform's event catalogue, a real sample payload per event type, and the signing details used to verify requests. Without those you are building against assumptions and verifying nothing, and both mean doing the work twice.

More work

Have a similar problem?

Book a 30-minute call. We will tell you what we would do first and what it would cost.

No pitch deck. We look at your current setup and tell you what we would do and what it would cost.

Need more than GoHighLevel? Yotomations, our parent company, also builds AI agents, custom software and websites.