AI Voice Agent Call Tracking: Fix Broken Attribution

automation
Google Ads, Retell, and Meta logos connected across a network graph representing AI voice agent call tracking attribution
The click ID has to survive the whole trip: ad click, number swap, AI conversation, and conversion upload.

If you run ads that generate phone calls and an AI voice agent answers them, your attribution is almost certainly broken right now — and it breaks quietly. The click ID and the call outcome end up in two systems that never talk to each other: your call tracking platform knows which ad produced a call, your voice AI platform knows whether that call was any good, and nothing joins the two records. The fix is a five-field handoff that carries the ad click ID through the number swap, into the conversation, and back out to Google Ads and Meta attached to a real outcome. It's roughly a day of work and it's the difference between optimizing on call volume and optimizing on booked revenue.

We build both halves of this for clients — the Retell agents that answer and the attribution plumbing underneath — and this gap is the single most common thing we find already deployed and silently miscounting.

Why AI-answered calls break call tracking

Traditional call tracking works because one system sees the whole journey. Someone clicks an ad, lands on your site, dynamic number insertion swaps in a tracking number tied to that session, they call it, and the tracking platform connects the call to the click. It then scores the call by duration or by spotting keywords in the transcript, and pushes a conversion back to the ad platform.

Drop an AI voice agent into that flow and the chain snaps at the last link. The call now routes through the tracking number to your agent's number, and the conversation — the part that determines whether the call was worth anything — happens somewhere the tracking platform can't see. It still logs a call. It just has no idea what happened on it, so it falls back to the crudest possible proxy: was this call longer than sixty seconds?

That proxy was mediocre with human receptionists. With AI agents it's actively misleading. A well-built agent qualifies and disqualifies a tire-kicker in forty seconds, which your tracker scores as a failed call. A caller who spends four minutes arguing with the agent about something it can't help with gets scored as a win. Optimize a Google Ads campaign against that signal for six weeks and you will systematically bid up the keywords that produce long, useless conversations.

Meanwhile the voice platform has the opposite problem. It knows precisely what happened — booked, qualified, wrong number, existing customer — and has no idea which campaign paid for it. Two systems, each holding exactly half the answer. It's the same structural failure we've written about in the context of Google Ads and GA4 attribution mismatches: nothing errors, the dashboards look populated, and the numbers are wrong.

The five-field handoff

The whole fix is getting one identifier to survive a trip it was never designed to make. Five steps, and only the click ID has to make it all the way through.

1. Capture the click IDs on landing

Read gclid, gbraid, wbraid, and fbclid off the URL the moment someone lands, and write them to a first-party cookie and your own session store. Do it server-side wherever you can.

The two braid parameters matter more than most people realize. Google issues them instead of gclid on iOS traffic where cross-app tracking is restricted, and if your capture logic only looks for gclid you'll silently drop a large slice of mobile clicks — exactly the population most likely to tap a phone number rather than fill out a form. A click ID you fail to capture here cannot be recovered anywhere downstream.

2. Bind the click ID to the number swap

Let your call tracking platform do dynamic number insertion normally. Then store your own mapping: this tracking number, in this session, at this timestamp, belongs to this click ID.

Store it yourself rather than relying on the tracker to hold it, because in the next step you need to query that mapping from a webhook with a couple of hundred milliseconds of budget. A Redis key with a short TTL keyed on the tracking number is plenty. The number pool recycles, so always resolve the most recent assignment rather than the only one.

3. Pass the click ID into the agent as metadata

When the call connects, your voice platform fires an inbound webhook before the agent starts talking. Look up the click ID by the number that was dialed and return it as call metadata, not just as a prompt variable.

That distinction is the one people get wrong. Metadata is attached to the call record and comes back in the post-call payload regardless of what the conversation did. A dynamic variable that only ever gets interpolated into a prompt may or may not survive, and there are documented reports of dynamic_variables and collected_dynamic_variables coming back undefined in webhook payloads while displaying correctly in the dashboard. Set it as metadata, then verify it round-trips in your own logs before you trust it.

4. Read the outcome off the post-call webhook

Consume the call_analyzed event, which Retell fires once the call analysis object is populated. That payload carries the same content as the get-call API, which means both your injected metadata and the structured outcome arrive together.

Define the outcome as an enum your agent is explicitly instructed to set, not something you infer from the transcript afterwards. Four or five values is usually enough: booked, qualified, not-qualified, existing-customer, wrong-number. Have the agent classify the call as part of its job. Post-hoc transcript classification is a second model call, a second failure mode, and reliably worse than just asking the agent to state what happened.

5. Upload the conversion keyed on the click ID

Push qualified outcomes back to Google Ads keyed on the gclid and to Meta keyed on fbc. Google resolves that click ID to the exact campaign, ad group, and keyword on its side, so you never store or match keyword data yourself — the GCLID-based offline conversion flow exists precisely for this.

Send a conversion value, not a flat count. A booked appointment and a disqualified caller are both real outcomes, and feeding them in with the same weight throws away the entire advantage of having structured data. If you have historical close rates, value the booking at expected revenue rather than at one.

On the Meta side this is a standard offline event through the Conversions API, and the setup is the same one we walk through in our Meta CAPI guide — the only difference is that the event source is a phone call instead of a page.

The deadlines that make this urgent

Two things make this worth doing this quarter rather than next year.

June 15, 2026 is a hard cutover. Offline conversion imports and enhanced conversions for leads uploads migrate to the Data Manager API and are blocked in the Google Ads API. If your pipeline pushes through the older endpoints it stops on that date. Anyone building call attribution now should build against Data Manager directly rather than writing code with a known expiry.

The upload windows are shorter than most sales cycles. Google keeps the gclid for ninety days, and conversions uploaded more than ninety days after the associated click are never imported. Enhanced conversions for leads is tighter still at sixty-three days. Neither one fails loudly — the upload is simply dropped and the conversion never appears.

For voice agents this bites in a specific way. The outcome you actually care about is often the closed deal, which lands well after the call. If your cycle runs long, upload the nearest reliable event inside the window — the qualified booking — and carry revenue attribution in your own reporting. A conversion signal that arrives inside the window beats a more accurate one that arrives after the window closed, because a dropped upload teaches the bidding algorithm nothing at all.

What actually breaks in production

Five failures we've hit on real client traffic, in rough order of how often:

  • Number pool collisions. Two sessions get assigned the same tracking number within the pool's recycle interval and the second call inherits the first caller's click ID. Symptom: a handful of conversions attributed to campaigns that make no sense. Fix: shorter TTL on the mapping, resolve to the most recent assignment, and drop the attribution rather than guess when two assignments fall inside the same minute.
  • Metadata that doesn't round-trip. You set it, the dashboard shows it, the webhook payload doesn't have it. Always assert on your own logged payloads before trusting the field.
  • Direct calls with no click ID at all. Someone saves the tracking number and calls it three weeks later. There is no click to attribute. Tag these explicitly as unattributed instead of letting them dilute your numbers silently.
  • Duplicate uploads on webhook retries. Post-call webhooks retry. Without an idempotency key on the call ID you upload the same conversion repeatedly and inflate the campaign that generated it. This one is quiet and expensive.
  • Callbacks and transfers losing the thread. A call that transfers to a human, or a follow-up outbound, is a new call record with no metadata unless you deliberately carry it forward. Thread the click ID through the transfer the same way you threaded it in.

Measure cost per qualified call, not cost per call

Once the loop is closed, the reporting question changes. Call volume stops being interesting, because an AI agent will happily answer every call including all the worthless ones, and volume goes up whether or not anything improved.

The number worth putting on the dashboard is cost per qualified outcome by campaign — ad spend divided by calls the agent itself classified as qualified or booked. That single metric surfaces the thing duration-based scoring hides: campaigns that look identical on cost per call routinely differ by a large multiple on cost per booked appointment, and you cannot see that gap until the outcome data reaches the ad platform.

It also changes what the voice agent costs mean. A qualifying call that ends in forty seconds is a good call, and it's cheap — worth reading alongside what AI voice agents actually cost per minute, because the per-minute rate only tells you something useful once you know which minutes produced revenue. That's the same principle behind everything in our attribution work: the platform optimizes toward whatever signal you feed it, so the signal has to describe the outcome you actually want.

Where to start

If you're running AI voice agents against paid traffic today, do the cheap diagnostic first. Pull last month's calls from your voice platform, pull the same period from your call tracker, and try to join them. If you can't — if there's no shared identifier — every dollar of call-driven spend you've optimized in that window was optimized against duration.

That's usually the moment it clicks. The plumbing is a day of work; the six weeks of bidding against the wrong signal is the expensive part.

If you'd rather not build it yourself, get a free automation audit. We'll map your current click-to-call chain, show you exactly where the identifier drops, and tell you what it would take to close the loop — whether or not you end up working with us.