All posts
Measurement

Which Location Got the Lead? Stamp It at Capture

To track which location a booking or lead came from, take the code from the booking record and stamp it on the conversion row at capture, never later.

September 6, 2026·13 min read·by Olexander Cheberko
Table of contentstap to expand

One lead row, three answers: the landing page says Northgate, the number dialed says Cedar Ridge, and the booking record says Harbor Point. Only one of those decides where the money goes, and it is the booking record, stamped onto the conversion row when the appointment is created rather than inferred later from a report. What follows is the ranking that puts it there.

Which signal is allowed to decide, when five of them disagree?

Six leads from one month at a three-office practice: NGT-11 (Northgate), HBP-24 (Harbor Point), CDR-37 (Cedar Ridge).

LeadLanding pathNumber dialedCampaign nameBooking recordIP cityStamped as
LD-4471/locations/northgateweb formBrand / GroupHBP-24NorthgateHBP-24, authoritative
LD-4472/book555-0142, Cedar Ridge lineSearch / Cedar Ridge / ImplantsNGT-11Harbor PointNGT-11, authoritative
LD-4473/locations/cedar-ridgeweb formSearch / Cedar Ridge / CleaningsCDR-37Cedar RidgeCDR-37, authoritative
LD-4474/offers/new-patient-129web formPMax / Group Feednone yetNorthgateUNSTAMPED, held
LD-4475/locations/harbor-point555-0198, group main lineSearch / Harbor Point / EmergencyNGT-11Cedar RidgeNGT-11, authoritative
LD-4476/locations/northgateweb formSearch / Group / Whitening (renamed Mar 3)NGT-11Harbor PointNGT-11, authoritative

Illustrative table. Synthetic figures, proportions typical.

Exactly one row has all five signals agreeing, LD-4473, and that is the row that makes people trust the other five. A report built on any other column still sums to the right group total. It just puts patients on the wrong office's line.

Rank the five location signals by the way each one fails

Reliability here is a question of what each signal is a fact about, and when it stops being true.

  1. The location id or resource id on the appointment record. The only authoritative signal: it is the field the practice itself uses to decide which building a person walks into, and it cannot drift from reality without the schedule drifting with it.
  2. The landing path. Derived, and good until someone ships a "change office" dropdown inside the booking widget. After that, /locations/northgate means the patient started at Northgate, not that Northgate got the appointment, and nothing errors to tell you. The path may write the field only while there is no switcher.
  3. The number dialed. A true fact about the caller and a false fact about the appointment. Good as a provisional stamp before the booking exists, evidence of nothing once a shared answering service is in the path. It also fails silently when a patient dials an old fridge magnet number the practice has since reassigned.
  4. The campaign name. Never. It is a string an agency owns, and the agency will rename it.
  5. The visitor's IP city. Worse than worthless, because it looks plausible. GA4's built-in City and Region describe an approximate network location: where a phone was, not which office a person picked. Delete that column.

Two more things look like location truth and are not. Google Ads Local Actions count actions on a Business Profile listing: calls from it, direction requests, website clicks. A listing can log 42 direction requests while six of those people book at another office. GA4's catch is narrower: it reports a location value only if you send it as an event parameter and register it as an event-scoped custom dimension. Registration is not retroactive, and standard properties cap those dimensions at 50. Register location_code before the first ad dollar hits office two. Platform behavior checked September 2026.

Stamp the booking that arrived through one shared widget

The shared widget is the normal case, not the edge case. See LD-4471: the landing path says Northgate, the record says Harbor Point, and the record wins, because the patient switched offices in the widget.

So the stamp is written where the appointment is created, not on the page:

const MAP = { 11: "NGT-11", 24: "HBP-24", 37: "CDR-37" };
const PATH_MAP = {
  "/locations/northgate": "NGT-11",
  "/locations/harbor-point": "HBP-24",
  "/locations/cedar-ridge": "CDR-37",
};
const SITE_HAS_SWITCHER = true;
 
function resolveLocation(appt, click) {
  if (appt.location_id) {
    return { code: MAP[appt.location_id], confidence: "authoritative" };
  }
  if (!SITE_HAS_SWITCHER && PATH_MAP[click.landing_path]) {
    return { code: PATH_MAP[click.landing_path], confidence: "derived" };
  }
  return { code: "UNSTAMPED", confidence: "none" };
}
 
function conversionRow(appt, click) {
  const stamp = resolveLocation(appt, click);
  return {
    dedupe_key: sha256(`${appt.id}|${click.gclid || click.gbraid || "none"}`),
    click_id: click.gclid || click.gbraid || click.wbraid || null,
    location_code: stamp.code,             // NGT-11 | HBP-24 | CDR-37 | UNSTAMPED
    location_confidence: stamp.confidence, // authoritative | derived | none
    occurred_at: appt.created_at_utc,
    value: null,                           // set at attendance, not here
  };
}

It never reads the campaign name, never reads the number dialed, never reads the IP. I key the dedupe on a deterministic hash of booking id plus click id, because a generated row id does not survive a re-run. Capturing the click id is its own job, covered there, not here.

What makes the UNSTAMPED tier safe: fail open on the conversion, fail closed on the location. The row still uploads, because the bidder needs the conversion. It just does not enter a per-location report until a human resolves it.

Lead arrives
web booking or phone call
Does the appointment record carry a location or resource id?
yes
Stamp from the record
confidence: authoritative
not yet, it is a call
Stamp from the number
provisional, re-stamped later
no record field at all
One location on the landing path, and no office switcher on the page?
yes
Stamp from the path
derived
no
Stamp UNSTAMPED
never guess a building
Three sources of a location stamp, ranked by how much they can be trusted. The unstamped path is the point: a lead with no confident location still uploads as a conversion, it just does not claim a building.

Every path writes the same row at capture time: click id, location code, the confidence level that produced it, and the dedupe hash. The rule that keeps this safe in both directions is worth stating once and applying everywhere: fail open on the conversion, fail closed on the location. An unstamped lead still uploads, because a missing building is not a reason to lose a conversion. What leaves is a click id, a value, a timestamp and a facility code that names a building, never a person.

One threshold for the runbook: more than 3% of rows landing UNSTAMPED in a rolling 14-day window means the widget is the bug, not the pipeline. Fix the office selector first. Wiring the stamp into the booking flow is part of a patient conversion tracking build.

Where the location code actually rides

A Google Ads offline click conversion payload has no native location field. The code rides one of two places: a conversion action per location, or a conversion custom variable attached to the upload through the Data Manager API upload path. Check which your account has enabled first: a custom variable that does not exist yet can drop silently rather than error.

Which one is a volume question. Below 30 attended appointments per location per 30 days, keep a single conversion action and carry the location as a segment: under that volume a location cannot steer its own bid. At or above 30, splitting earns its keep.

Stamp the call the answering service sent to the wrong office

On a multi-location account I worked on, the per-location tracking numbers matched the booked office almost perfectly on weekday rows and came apart on evenings and Saturdays. The after-hours service answered all three lines and booked into whichever office had the first opening. The number dialed stayed a true fact about the caller's intent. It became a false fact about the appointment.

That is why intent and outcome live in different fields. The number dialed may write a provisional stamp so the row is not blank before the appointment exists, and the record overwrites it the moment it appears. See LD-4475: the group main line, a Harbor Point landing page, a Northgate appointment. Matching a call to a booking, with a hashed number and a time window, is covered there, not here.

Per-location tracking numbers are worth paying for only when phone is at least 25% of that location's booked appointments and no shared answering service sits in the path. Once a service answers every line after hours, you match on the appointment record instead.

Who gets credit when a patient books at one office and is treated at the other?

This happens constantly and it is not an error, so do not resolve it by picking one office. Keep two fields: acquired_location_code and treated_location_code. The acquiring office keeps the marketing credit, because that is what the ad produced. The treating office carries the revenue and the chair time.

Collapsing them into one field looks tidy for about a quarter, until the schedule moves a block of appointments to the office with the open room and one campaign's numbers quietly improve. The value arrives later either way, when you count the patient who showed up rather than the one who booked.

Why does a campaign rename move last quarter's revenue between offices?

Because a report that derives location from a campaign name is reading a label, and labels get rewritten backwards. An agency restructures the account, per-location names collapse into group names, and every historical month re-derives on the new pattern. See LD-4476: same conversion, same office, a campaign name that stopped saying Northgate on March 3. The group total does not move a cent, which is why nobody catches it.

Run this monthly. It compares what was stamped at capture against what a name-derived rule says today:

select date_trunc(c.occurred_at, month) as month,
       c.location_code as stamped,
       coalesce(n.code, 'no-match') as name_derived,
       count(*) as rows_
from conversions c
left join campaign_name_rules n
  on regexp_contains(c.campaign_name, n.pattern)
where c.occurred_at >= date_sub(current_date(), interval 12 month)
group by 1, 2, 3
having stamped <> name_derived
order by month desc, rows_ desc;

Any month in that result that was not there last time is a rename, not a change in patient behavior.

The same problem has a modeling answer: treat location as a dimension with validity windows, and join each fact to the version that was true then.

select f.month, d.display_name, f.attended
from monthly_attended f
join dim_location d
  on d.location_code = f.location_code
 and f.month >= d.valid_from
 and f.month <  coalesce(d.valid_to, date '2999-01-01')

The display name is the only thing allowed to change. NGT-11 stays NGT-11 when Northgate is rebranded to Northgate Dental Group, so last quarter's report still renders last quarter's world. A naive join to the current name rewrites history the moment marketing changes a sign, and the row count comes back identical. Reports that already argue with each other are most of an attribution rebuild for a multi-location practice.

What does one wrong stamp cost, in dollars you can compute?

Take a clean month across three offices, then stamp it from the campaign name instead of the record. Eight of Harbor Point's attended appointments came through a group campaign and switched office in the widget, so they land on Northgate. Three of Cedar Ridge's land on Harbor Point.

OfficeSpendTrue attendedReported attendedTrue cost per attendedReported cost per attended
NGT-11$7,6005260$146.15$126.67
HBP-24$6,8004439$154.55$174.36
CDR-37$4,1002421$170.83$195.24
Group$18,500120120$154.17$154.17

Illustrative table. Synthetic figures, proportions typical.

The group line is right to the cent in both columns, and that is the trap. The reported spread between best and worst office is $68.57, a ratio of 1.54. The true spread is $24.68, a ratio of 1.17. A quarterly review reading the reported spread moves 15% of Cedar Ridge's budget ($615 a month) and 10% of Harbor Point's ($680 a month) into Northgate: $1,295 a month and $15,540 a year, moved on a stamping artifact. The arithmetic is the argument, not a promise about anyone's account.

Two guards fall out of that. Do not move budget on a location-to-location cost gap under 20%, or on fewer than 8 weeks of stamped history, whichever binds later. And keep derived stamps out of a board deck: reporting revenue by location and channel on a guess is an argument nobody can settle. When office two is what made this urgent, roll the same setup out to a second location.

When is a location code not worth building a pipeline for?

Two readers should stop at the field and skip the machinery.

The first: two offices in one metro, one shared budget, and you have never once moved a dollar between them. The location code is a reporting nicety. Put the field on the booking record and on the conversion row, then skip the pipeline and the review queue, which is engineering you maintain for a decision you do not make.

The second, and far more common: a booking system with no location concept at all, one calendar with rooms as resources. The authoritative signal does not exist, so every row falls to the derived tier forever. The first job is creating the location code inside the appointment system, not buying measurement on top of a system that cannot answer the question. I would rather say that than sell a rollout that resolves to a guess.

If one line from this survives into your next build, make it the line you hand to whoever wires up office two: the location code is a value the appointment system gives you at capture, not a value a report infers later. Write it on the ticket in exactly those words, because the version that gets built when nobody wrote it down is always the version that reads the campaign name, and that version keeps the group total right to the cent while it quietly moves one office's patients onto the other office's line.

Tags

multi-location-trackinglocation-attributionoffline-conversionsgoogle-adsconversion-trackingmedical-practice

Frequently asked questions

How do I tell which office a Google Ads lead belongs to?

Take the location code from the booking record and write it onto the conversion row at the moment the appointment is created. Every other signal, the landing page, the number dialed, the campaign name and the IP city, is either a fallback or a guess, and none of them survives a location switcher, an answering service or an account restructure.

Do I need a separate conversion action for each location, or one action with a location field?

Below roughly 30 attended appointments per location per 30 days, keep one conversion action and carry the location as a segment on the row. A location under that volume cannot support its own bidding signal, so splitting the action starves both sides. At or above it, a per-location action starts to earn its keep.

Can I use the campaign name to assign a lead to a location?

No, and this is the single most expensive shortcut in multi-location tracking. A rename or a restructure reassigns historical months backwards between offices, and nobody notices because the group total stays exactly right while the per-office lines move.

What happens when a patient books at one office and is treated at the other?

Split the credit: the acquiring location keeps the marketing credit because that is what the ad actually produced, and the treating location carries the revenue and the capacity. Keep both codes on the row, acquired_location_code and treated_location_code, so a mid-quarter schedule move never silently rewrites which campaign looked good.

Does a location field on a booking form count as patient data?

A code for your own office identifies a building you lease, not a person, so it is not patient geography. A patient ZIP, a city derived from the patient's IP, or a nearest-office-to-home field is a different thing entirely, and that last one is the trap because it looks like a location field.

Do Google Ads Local Actions tell me which of my offices got the booking?

No. Local Actions count interactions with a Business Profile listing, such as calls from the listing and direction requests, so they describe what happened on Google rather than what happened in your schedule. A listing can generate 42 direction requests while six of those people book at a different office.

Need something like this built?

Free 15-min discovery call. I'll listen, ask honest questions, and tell you if I can help.

More in Measurement