Skip to main content

Dynamic number pools (tracking pools) end-to-end

This walks the full lifecycle of a tracking pool: what it is, how to create one in the console, how to bind it to a marketing source, how to put the swap snippet on your website, and where to read attribution once callers dial. The follow-through below uses a Google Ads example you can adapt to any source. For the endpoint-level contract — resolver query parameters, quotas, every match rule — see Call tracking & DNI.

1. What tracking pools are and why they exist

A tracking pool is a named bundle of inbound numbers bound to one marketing source — google, a partner site, an email newsletter. When a visitor matching that source loads your site, a page snippet swaps the visible number for one from the pool, so their call lands on a number that maps back to the campaign that drove it. Every pool also names the source it attributes to and the match rules that decide which visitors see its numbers. This is the CallRail / Invoca category — a standard piece of the paid-media stack — built into your numbers inventory here rather than run as a separate vendor. The rest of the loop has three parts: a swap strategy (sticky gives each visitor session one number deterministically; fixed shows the pool’s first number to everyone), a preview resolver that checks which pool a given UTM set matches, and read-time attribution that joins each inbound call to its pool and source when you open the report. Tracking numbers are inbound-only — they terminate inbound calls; no outbound call or SMS path is created by a pool.

2. Create a pool of swappable tracking numbers

Open Numbers → Call tracking (/numbers/tracking-pools). The page lists every pool with its source, strategy, and assigned numbers, plus a snippet card at the bottom. Before you create a pool, own the numbers it will hold — buy local or toll-free voice numbers through the normal purchase flow (see Buy and provision numbers). Fill the New tracking pool form: Create the pool, then type an owned number in E.164 (+14155550142) into the pool card’s assignment field and add it. A pool with no numbers never swaps. Each number belongs to at most one pool — assigning a number already in another pool moves it atomically, so one number always maps to one source. Remove a number from the pool card the same way.

3. Bind a marketing source (UTM / referrer)

The match rules on the form above are the binding: when the snippet runs, it reads the visitor’s utm_source, utm_campaign, utm_medium, utm_term, referrer, and gclid, and picks the pool whose defined conditions all hold. Rules available per pool: case-insensitive UTM parameter exact match, a referrer-contains substring (e.g. google.com), and require-gclid for Google click traffic. Undefined conditions are “don’t care”. Two resolver rules to plan around:
  • Most specific wins. When several pools match, the one with the most defined match conditions wins (ties break deterministically). A pool with no match rules is a catch-all for traffic nothing else claims.
  • Preview before you ship. Use the Preview number swap card on the same page: enter a utm_source and utm_campaign, run it, and it reports either “Would show +14155550142 for source google” or “No pool matches — the page keeps its default number.” Run this before pasting the snippet anywhere.

4. Drop the swap snippet into your website

Scroll to the Website snippet card and click Copy snippet. The console builds a self-contained <script> with your enabled pools, their numbers, and match rules embedded — no API call and no key in the browser, so it runs on a fully static page. Paste it before </body>, then mark every phone-number element you want swappable with data-orbit-dni:
The snippet runs on page load. If a pool matches, it rewrites the element’s text and tel: href; if none matches, nothing touches the element and your hard-coded fallback number stays — which is also what search crawlers and visitors who never match a pool see. Under the sticky strategy the per-visitor choice persists in the browser’s local storage, so the same visitor keeps the same number across page loads. When you add or remove a pool or number, re-copy the snippet — the config is embedded at copy time. Re-deploying a script tag is not always practical. The API path instead swaps server-side per request: proxy GET /numbers/tracking-pools/resolve from your backend with your API key and render the returned number — the full snippet and proxy contract are in Call tracking & DNI.

5. Attribute inbound calls in Insights → Attribution → Call attribution

Inbound calls to any tracking number roll up by source and campaign under Insights → Attribution → Call attribution — calls, answered, and aggregate talk time per source, over your chosen window (in the worked example below, google / brand-2026 shows the calls those pool numbers took). One row per source/campaign pair, sorted by call volume. Because the join is read-time (number → pool → source), two behaviors follow:
  • Calls to a number not assigned to any pool don’t attribute.
  • Re-assign a number and historical calls attribute to the new pool on the next read — you can fix a wrong binding retroactively.
The same report is available over the API with GET /insights/call-attribution (with days, from/to, or pool_id filters) if you want attribution in your own dashboards — details in Call tracking & DNI.

Troubleshooting