Setting up static call tracking on 1C-Bitrix

Our company is engaged in the development, support and maintenance of Bitrix and Bitrix24 solutions of any complexity. From simple one-page sites to complex online stores, CRM systems with 1C and telephony integration. The experience of developers is confirmed by certificates from the vendor.
Our competencies:
Development stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1175
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Website development for FIXPER company
    811
  • image_bitrix-bitrix-24-1c_development_of_an_online_appointment_booking_widget_for_a_medical_center_594_0.webp
    Development based on Bitrix, Bitrix24, 1C for the company Development of an Online Appointment Booking Widget for a Medical Center
    564
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Development based on 1C Enterprise for MIRSANBEL
    747
  • image_crm_dolbimby_434_0.webp
    Website development on CRM Bitrix24 for DOLBIMBY
    655
  • image_crm_technotorgcomplex_453_0.webp
    Development based on Bitrix24 for the company TECHNOTORGKOMPLEKS
    976

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):

  1. Rent the required number of numbers
  2. For each number — configure a rule: "if call comes to number X, attribute to source Y"
  3. Set up forwarding from the substituted number to the main working number
  4. (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.