Setting Up Static Call Tracking on 1C-Bitrix
Static call tracking means each advertising channel is permanently assigned a dedicated phone number: one for Yandex.Direct, another for Google Ads, a third for offline advertising. There is no real-time substitution — the number is hardcoded to the channel and does not change between visitors.
This is cheaper than dynamic tracking (fewer numbers required), but accuracy is lower: attribution is at the channel level, not down to the keyword or ad. For businesses with a small number of channels and a high call value (B2B, healthcare, real estate) — it is a justified solution.
Organization Scheme
Rent N numbers from a provider — one per tracked channel. Each number forwards to the main line. The call tracking system records which static number was called and logs it in statistics.
| Channel | Number | Forwarding |
|---|---|---|
| Yandex.Direct | +7 (495) 111-11-11 | → main |
| Google Ads | +7 (495) 222-22-22 | → main |
| VKontakte | +7 (495) 333-33-33 | → main |
| Offline / business cards | +7 (495) 444-44-44 | → main |
| Website (default) | +7 (495) 555-55-55 | → main |
Implementation on the Bitrix Side
The logic is straightforward: read utm_source from the GET parameter or cookie, select the corresponding number from the config, and inject it into the template.
Store the channel-to-number mapping in module settings:
// /local/modules/local.calltracking/lib/Config.php
namespace Local\Calltracking;
class Config
{
public static function getPhoneMap(): array
{
return [
'yandex' => [
'display' => '+7 (495) 111-11-11',
'href' => 'tel:+74951111111',
],
'google' => [
'display' => '+7 (495) 222-22-22',
'href' => 'tel:+74952222222',
],
'vk' => [
'display' => '+7 (495) 333-33-33',
'href' => 'tel:+74953333333',
],
'offline' => [
'display' => '+7 (495) 444-44-44',
'href' => 'tel:+74954444444',
],
'default' => [
'display' => '+7 (495) 555-55-55',
'href' => 'tel:+74955555555',
],
];
}
}
Channel detection and storage in a cookie:
// /local/php_interface/init.php
\Bitrix\Main\EventManager::getInstance()->addEventHandler(
'main',
'OnProlog',
function () {
$request = \Bitrix\Main\Application::getInstance()->getContext()->getRequest();
// Priority 1: utm_source GET parameter (new visit)
$utmSource = $request->get('utm_source');
if ($utmSource) {
$utmSource = preg_replace('/[^a-z0-9_-]/i', '', strtolower($utmSource));
setcookie('ct_source', $utmSource, time() + 86400 * 30, '/');
$_COOKIE['ct_source'] = $utmSource;
}
// Priority 2: stored cookie
if (!$utmSource) {
$utmSource = $_COOKIE['ct_source'] ?? 'default';
}
$GLOBALS['CT_CURRENT_SOURCE'] = $utmSource;
}
);
Helper for retrieving the phone number in the template:
namespace Local\Calltracking;
class PhoneResolver
{
public static function getCurrentPhone(): array
{
$source = $GLOBALS['CT_CURRENT_SOURCE'] ?? 'default';
$map = Config::getPhoneMap();
return $map[$source] ?? $map['default'];
}
}
In the site header template:
<?php
$phone = \Local\Calltracking\PhoneResolver::getCurrentPhone();
?>
<a href="<?= htmlspecialchars($phone['href']) ?>"
class="header-phone"
data-source="<?= htmlspecialchars($GLOBALS['CT_CURRENT_SOURCE'] ?? 'default') ?>">
<?= htmlspecialchars($phone['display']) ?>
</a>
The Bitrix Cache Problem
Static call tracking via PHP logic is incompatible with full-page caching. The phone number is personalized data (it depends on the visitor's cookie), so a page containing the phone number must not be cached.
Solutions:
Option 1: JS substitution — output the default number in the HTML and swap it by cookie via a client-side script after page load. The cache is unaffected.
document.addEventListener('DOMContentLoaded', function () {
const phoneMap = {
'yandex': {display: '+7 (495) 111-11-11', href: 'tel:+74951111111'},
'google': {display: '+7 (495) 222-22-22', href: 'tel:+74952222222'},
// ...
'default': {display: '+7 (495) 555-55-55', href: 'tel:+74955555555'},
};
const source = getCookie('ct_source') || 'default';
const phone = phoneMap[source] || phoneMap['default'];
const el = document.querySelector('.header-phone');
if (el) {
el.textContent = phone.display;
el.href = phone.href;
}
});
Option 2: Exclude the phone from the cache via $component->arResult with caching disabled for the specific block. In the header component template:
// In the header component.php
$this->SetResultCacheKeys([]); // Disable cache for the component
Option 3: ESI (Edge Side Includes) — if you use Varnish or nginx with the ESI module. The phone number is moved to a separate non-cached ESI fragment.
For most projects, Option 1 is optimal: page caching is preserved, the phone swap happens in milliseconds, and users barely notice the update.
Integration with the Call Tracking Provider
Even with static call tracking, a provider is still needed for call recording, statistics, and forwarding. Provider-side setup (Callibri, CoMagic, Mango):
- Rent the required number of numbers
- For each number — configure a rule: "if call comes to number X, attribute to source Y"
- Set up forwarding from the substituted number to the main working number
- (Optional) Enable call recording
No API interaction with the provider is required from Bitrix in the static scenario — all channel identification logic resides on the provider's side.
Scope of Work
- Rent substituted numbers from the provider and configure forwarding
- Develop PHP module for channel detection and cookie storage
- Implement JS phone substitution (cache-compatible variant)
- Adapt component templates (header, footer, pop-up forms)
- Configure channel mapping via the administration interface
Timelines: full setup for 4–6 channels — 3–7 business days.







