All case studies
Case Study

Offline Conversions: Google Ads to Real Patients

I built an offline conversion loop that ties Google Ads spend to patients who actually showed up, using a HIPAA-conscious click-id-only upload.

US medical practice·August 1, 2026·7 min read
Table of contentstap to expand

If your Google Ads account only knows about clicks and form fills, it is allocating budget to the wrong campaigns. It cannot see which keywords produced patients who actually showed up, so it optimizes for cheap clicks instead of real visits. This is a de-identified case study of how I closed that gap for a multi-location US medical practice: an end-to-end offline conversion loop that reports real patient outcomes back to Google Ads without ever moving patient data. The honest headline is not a big percentage lift. It is a proven mechanism and a privacy-safe architecture that survives a HIPAA-conscious review.

Quick answer

  • Google Ads was told about clicks and form submissions, so budget followed clicks, not patients.
  • I built a loop that captures the ad click id at booking (via GTM, no site-file edits), writes it to an isolated, PHI-free table keyed to the appointment, then reports the outcome once the patient is marked arrived.
  • A second loop matches inbound phone calls to appointments by hashed phone plus time window, and uploads through the Google Ads Data Manager API as a separate conversion action.
  • Only an anonymous click id, a value, and a timestamp leave the system. The phone number is hashed and no patient record is persisted.
  • The call loop is proven end-to-end (the first ad-driven patients were counted in the account with the correct value). The web-booking loop was capturing and scheduled to start reporting on plan. Both run as observation first, so they cannot disturb live bidding.

The problem: Google Ads only saw clicks, not patients

The practice ran Google Ads for two conversion types: phone calls and online bookings. Standard Google Ads conversion tracking fired on the form submit and the call connect. That is where the visibility stopped.

In a medical funnel that gap is expensive. A booking is not a patient. People book and never show. People call, get a quote, and never come in. When only about half of bookings turn into a completed visit (I am using an illustrative ratio here, not a client figure), optimizing for bookings means optimizing partly for no-shows. The auction was buying the cheapest form fills, which are not the same as the campaigns that produce patients who sit in the chair.

The fix is to report the real outcome, the completed visit, back to the click that started it. That is offline conversion tracking, and in a healthcare context the hard part is doing it without touching protected health information.

Ad clicks
4820
Opened the form
742
Booking done
388
Patient arrived
301
Visit paid
268
Schematic on synthetic data, proportions typical: the account optimized on step three, the booking, while the money is two steps later at the paid visit.

How I approached it: additive, fail-open, PHI-free

Three rules shaped the whole build, and they are the reason it passes review.

  1. Additive and fail-open. The marketing layer sits alongside the booking system, never inside its critical path. If the click id is missing or an upload fails, the booking still completes. Attribution is never allowed to break a patient booking.
  2. PHI-free by construction. The attribution table stores an anonymous click id, an appointment key, a value, a timestamp, and a hashed phone. No name, no email, no diagnosis, nothing that identifies a person. This is not redaction after the fact; the table is designed so PHI cannot enter it.
  3. Observation first. Every new conversion action reports into the account in observation mode. It counts and values appointments without feeding Smart Bidding, so I can verify against real visits before a single dollar of budget moves.

That third rule matters on a live account. You do not get to A/B test a bidding change on a clinic that depends on the phone ringing. Observation mode is how you prove the mechanism is correct before it is allowed to matter.

How the offline conversion loop works

There are two loops feeding two separate conversion actions. Keeping them separate is deliberate: it is how a call and a booking from the same patient are never double-counted.

Capture click id
from the landing URL
Booking
click id in a header
PHI-free store
keyed to appointment
Arrived
outcome + real value
Upload
to Google Ads
The web-booking loop. A parallel call loop matches by hashed phone number, kept as its own conversion action.

The web-booking loop

  1. A front-end module drops into the page through GTM. No site-file changes, which matters because the site is edited by more than one party. It reads the ad click id from the landing URL and holds it.
  2. At booking, the click id rides into the booking backend in its own HTTP header, separate from the form payload.
  3. The backend writes the click id to an isolated, PHI-free table, keyed to the appointment. Nothing patient-identifying goes in.
  4. When staff mark the patient arrived in the practice system, the outcome is captured. When billing later has the real paid amount, that value replaces the default.
  5. The row is uploaded to Google Ads as the web-booking conversion action.

The call loop

Inbound calls already carry a gclid from the click that generated them. A second loop matches each call to a booked appointment by hashed phone number within a time window, then uploads it as the calls conversion action. Same PHI-free discipline: the phone is hashed before it is compared, and the raw number never leaves.

Here is the shape of one attribution row and the outcome payload. Note what is absent as much as what is present.

{
  "appointment_key": "appt_2f9c41",
  "click_id": "<opaque-click-id>",
  "value": 180.00,
  "currency": "USD",
  "outcome": "arrived",
  "occurred_at": "2026-07-14T15:22:00Z",
  "phone_sha256": "9f2b...e17a"
}

No name. No email. No reason for the visit. Just enough to answer one question: did this click become a patient, and what was the visit worth.

The upload itself goes through the Data Manager API as two named conversion actions:

Conversion actionMatch keyValue sourceBidding mode at launch
Web booking (arrived)click id from booking headerbilling amount, default until knownobservation
Call (arrived)gclid + hashed phone + time windowbilling amount, default until knownobservation

What it means for budget and for privacy

Cost per booked visitRevenue per visit
Primary visit
Dermatology
Physiotherapy
Women's health
Schematic on synthetic data, proportions typical: the cost to book barely moves across service lines, while revenue per visit swings threefold. That gap is the entire case for bidding on paid outcomes instead of raw bookings.

The honest result is not a percentage lift, and I would distrust anyone who handed you one this early. The win is architectural and it is real.

The call loop is proven end-to-end. The first ad-driven patients were counted in the account with the correct value, matched from an inbound call to a completed visit, with no patient data leaving the system. The web-booking loop was capturing click ids and set to begin reporting on schedule. Because both actions launched in observation, they informed reporting without risking a live account's bids.

Once these actions come out of observation, budget can follow completed visits instead of clicks. That is the whole point: the campaigns that produce patients who show up get funded, and the ones that produce cheap form fills and no-shows get cut. Getting an offline conversion tracking setup right is what makes that shift trustworthy enough to act on.

The privacy design is the part I am proudest of. In a HIPAA-conscious build, the marketing layer has to earn its place next to the clinical system. Here it does: it is additive, fail-open, and it only ever emits an anonymous click id, a value, and a timestamp. A patient's identity never touches Google.

If you run ads for calls and bookings and you cannot yet tell which campaigns produce real customers, this is the gap worth closing first. Tell me how your bookings and calls are captured today and whether outcomes live in a CRM or a practice system, and I will map the shortest privacy-safe path from click to real patient for your setup.

Tags

offline conversion trackinggoogle adsattributiondata manager apihealthcare

Frequently asked questions

What is offline conversion tracking in Google Ads?

It is a way to report a real-world outcome, like a patient who showed up, back to the ad click that produced it. You capture the click id at booking, wait for the outcome, then upload the click id plus a value and timestamp to Google Ads. The platform matches it to the original click so bidding can optimize for outcomes, not just form fills.

Can you do Google Ads offline conversions without exposing patient data?

Yes. Only three things ever leave the system: an anonymous click id, a value, and a timestamp. The phone number used for call matching is hashed, and no patient record is persisted in the marketing layer. That keeps the upload HIPAA-conscious while still telling Google Ads which campaigns produce real visits.

How do you match phone calls to Google Ads clicks?

Each call carries a gclid from the click that generated it. A second loop matches that call to a booked appointment by hashed phone number and a time window, then uploads it as its own conversion action. Keeping calls and web bookings as separate actions means one patient is never counted twice.

Why upload conversions as observation only at first?

Observation mode lets a new conversion action report into the account without feeding Smart Bidding. You confirm the counts and values are correct against real appointments before you ever let them move budget. It is the safe way to prove the mechanism on a live account.

Does an offline conversion loop change the booking flow?

It should not. The marketing layer here is additive and fail-open: if the click id is missing or the upload fails, the booking still completes normally. Nothing about capturing attribution is allowed to block a patient from booking.

Want this loop closed on your account?

I connect your EHR, booking system, and calls to Google Ads so it counts patients who actually showed up. HIPAA-conscious, fixed price, verified in the account.

More case studies