Skip to content

Medical Practice Marketing  ·  Measurement

How do you track which ads produce booked patients without sending PHI?

Match booked appointments back to enquiries inside your own systems, then send the platform the click id, a timestamp and a value, with no condition, procedure or department attached. The click-id path sends no personal identifier at all, which is why a practice should reach for it before the hashed-data one.

I am a marketer, not a lawyer, and nothing here is legal advice. The design below is built to narrow what your compliance officer has to rule on, not to substitute for their ruling.

The gap this closes

A practice optimizing to form fills and phone calls is optimizing to the wrong thing, and the account will get worse at the real job while its reported numbers improve.

Here is the mechanism. Smart Bidding buys more of whatever you tell it counts. Tell it a phone call counts, and it will find you phone calls: records requests, existing patients rescheduling, billing questions, people who wanted a different specialty. The conversion count climbs, the cost per conversion falls, the report looks like progress, and the appointment book doesn’t move.

The searches that produce appointments and the searches that produce call volume aren’t the same searches. Until the outcome gets back into the account, the bidding can’t tell them apart.

The shape of the fix

Do the matching inside your own systems, and send out only a result.

Enquiries land with an identifier and a click id. Somebody calls or submits a form. Your call tracking or form handler holds their phone number or email, plus the click identifier that brought them. That click identifier is a random string the ad platform generated. It isn’t a patient identifier.

The practice knows who booked. Appointments exist in the practice management system. Some of those appointments correspond to enquiries from the previous step.

Match the two, on your side. Normalize phone numbers to a single format and lowercase the emails, then join booked patients to enquiries. This is the step where all the patient data lives, and it never leaves the practice’s own environment.

Send the platform the outcome and nothing else. One conversion action, named for the event rather than the reason. “Booked new patient” is a fine name. “Booked carpal tunnel consult” is a disclosure wearing a conversion action’s clothes.

Two mechanisms exist here and the difference matters more for a practice than for anyone else. Offline conversion import keyed to the click id sends the click identifier, a timestamp and a value, and no personal identifier at all. Enhanced conversions for leads is the one that sends a hashed email or phone. Both are widely described as “the compliant way,” and for a practice they aren’t equivalent: the click-id path hands the platform nothing about the person, so that’s the one to reach for first. Use the hashed path only where you can’t hold the click id through to booking.

The platform now knows a particular click eventually became an appointment. It doesn’t know the person, the condition, the procedure, or the department.

What must not be in the payload

If you are using the hashed path, the hashing isn’t the protection people assume it is. Hashing an identifier stops the platform reading the identifier; it does nothing about the fields next to it. And on the click-id path there’s no identifier in the payload to argue about.

So: no condition, no procedure code, no department, no provider name where the provider is a single-specialty signal, no free-text notes, and no conversion action or campaign name that encodes any of those. If your account has one campaign per condition and only one of them is importing bookings, the import itself is carrying the condition.

Value needs the same discipline. A revenue figure that differs per procedure can encode the procedure. This is a real tension, because value-based bidding is the reason to do any of this, and the honest answer is that most practices should not attach value at all in the first months.

Why I start at count only

A scored call isn’t a booked patient, and a booked patient isn’t a completed episode of care. Practices routinely have no measured idea what fraction of qualified enquiries become appointments, and attaching a revenue figure before that fraction is known is inventing a number and then bidding on it.

So the first phase imports bookings at count only, with no value. That alone fixes the biggest problem, which is that the account was optimizing to calls rather than appointments.

Then measure the booking rate against the practice’s own intake records for a few months. When you know it, and when the value you would attach can’t encode the reason for the visit, value-based bidding becomes a real option. On one practice I set this up for, the pipeline ships with no value map in the code at all and no method to set one, deliberately, so that nothing can start sending a number nobody has verified.

The failure modes worth designing for

The front desk keys a different number. A patient calls from a mobile and gives a home number at booking. No match. This is the single largest source of loss and there’s no clever fix, only matching on more than one field.

A family member books. A spouse calls, the patient attends. The identifier that clicked and the identifier that booked are different people.

The existing patient. An established patient rebooking isn’t a new patient, and importing them as one teaches the bidding to buy people who already know you.

The click window, and the upload deadline underneath it. Two separate ceilings, and the second one is the one that bites. The booking has to fall inside the click window your conversion action allows. On top of that Google enforces an upload deadline: conversions uploaded more than 90 days after the last click are not imported, and enhanced conversions for leads is tighter still at 63 days. Healthcare consideration can be long, so run the match on a schedule tied to booking date rather than enquiry date, and check both numbers before concluding the pipeline is underperforming.

Every one of these makes the import undercount rather than overcount, which is the right direction for an error to run. An account that undercounts bids conservatively. An account that overcounts buys more of something that isn’t happening.

Getting the data out of the practice management system

Usually the least technical and most difficult part.

The reliable path is a scheduled report the system already knows how to produce, delivered to a mailbox under a signed agreement, with the minimum fields: an identifier, the booked date, a new-patient flag, and nothing clinical. Most systems can email a scheduled report. Far fewer expose a usable appointment feed, and the vendor’s real-time interface is typically a paid product with a procurement timeline measured in months.

One scheduling note. Google is moving offline conversion imports and enhanced-conversion uploads to the Data Manager API, with the older Google Ads API path closing from 15 June 2026, so anything built now should be built against the current endpoint rather than a tutorial written before the change.

Start the vendor conversation early and run the scheduled export in the meantime. The export isn’t elegant and it works, and a working undramatic pipeline beats an elegant one that’s still in procurement when the quarter ends.

The general architecture for any business that closes on the phone, without the healthcare constraints, is The Lead Quality Stack. The vendor-agreement questions that sit underneath this are in do I need a BAA with my call tracking provider.

Want this diagnosed in your account?

Same diagnosis,
run on your account.

Thirty minutes on the phone. I look at your spend, your tracking, and your search-term reports before the call. You walk out with a clear list of what is leaking and what to fix first.