Setting up trigger push notifications in 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

Triggered Push Notification Setup in 1C-Bitrix

Triggered push notifications differ from segmented broadcasts in one thing: they are sent automatically in response to a specific user action, not on a schedule. Abandoned cart after 2 hours, price drop on a viewed item, restocked wishlist item — these are trigger scenarios. Properly configured, they deliver ROI of 500–1500%.

Event Architecture in Bitrix

Triggered push is built on Bitrix's event model. Every system event is a potential trigger. How it works:

  1. An event occurs (item added to cart, product viewed, order status changed)
  2. Event handler checks scenario conditions
  3. If conditions met — push send task is queued with a delay
  4. Bitrix agent processes queue and sends notifications

Task queue is stored in table:

CREATE TABLE custom_push_queue (
    id INT AUTO_INCREMENT PRIMARY KEY,
    user_id INT NOT NULL,
    scenario VARCHAR(50) NOT NULL,
    payload JSON,
    send_at DATETIME NOT NULL,
    status ENUM('pending', 'sent', 'cancelled') DEFAULT 'pending',
    created_at DATETIME,
    INDEX idx_send_at (send_at, status),
    INDEX idx_user_scenario (user_id, scenario)
);

Key Scenarios

Abandoned cart — most valuable scenario. Logic:

// OnSaleBasketItemSaved handler
AddEventHandler('sale', 'OnSaleBasketItemSaved', function($basketItem) {
    $userId = (int)$basketItem->getField('USER_ID');
    if (!$userId) return; // Only authorized

    // Cancel previous task for this user
    PushQueueTable::cancelByUserAndScenario($userId, 'abandoned_cart');

    // Queue new one — in 2 hours
    PushQueueTable::add([
        'USER_ID' => $userId,
        'SCENARIO' => 'abandoned_cart',
        'PAYLOAD' => json_encode(['cart_url' => '/cart/']),
        'SEND_AT' => new \Bitrix\Main\Type\DateTime(date('Y-m-d H:i:s', time() + 7200)),
        'STATUS' => 'pending',
        'CREATED_AT' => new \Bitrix\Main\Type\DateTime(),
    ]);
});

When user completes order — task is cancelled. When not — push arrives in 2 hours.

Price drop on viewed item — requires view history and price monitoring:

// OnAfterIBlockElementUpdate handler
AddEventHandler('iblock', 'OnAfterIBlockElementUpdate', function(&$fields) {
    $elementId = (int)$fields['ID'];
    $newPrice = getElementPrice($elementId);

    // Find users who viewed this item
    $viewers = ProductViewHistoryTable::getUsersByProduct($elementId, 30); // last 30 days

    foreach ($viewers as $viewer) {
        $oldPrice = ProductPriceHistoryTable::getLastPrice($elementId, $viewer['USER_ID']);
        if ($newPrice < $oldPrice * 0.9) { // 10%+ discount
            PushQueueTable::add([
                'USER_ID' => $viewer['USER_ID'],
                'SCENARIO' => 'price_drop',
                'PAYLOAD' => json_encode(['product_id' => $elementId, 'new_price' => $newPrice]),
                'SEND_AT' => new \Bitrix\Main\Type\DateTime(), // Immediately
                'STATUS' => 'pending',
            ]);
        }
    }
});

Back in stock — via quantity change handler in b_catalog_store_product or 1C sync.

Order statusOnSaleStatusOrderChange event handler. Push sent immediately on each status change.

Queue Processing Agent

Bitrix agent runs every minute:

function ProcessPushQueue(): string {
    $now = new \Bitrix\Main\Type\DateTime();

    $records = PushQueueTable::getList([
        'filter' => ['=STATUS' => 'pending', '<=SEND_AT' => $now],
        'limit' => 100,
    ]);

    while ($record = $records->fetch()) {
        $tokens = PushTokenTable::getByUserId($record['USER_ID']);
        if (!empty($tokens)) {
            $message = PushScenario::buildMessage($record['SCENARIO'], json_decode($record['PAYLOAD'], true));
            PushSender::send($tokens, $message);
        }
        PushQueueTable::update($record['ID'], ['STATUS' => 'sent']);
    }

    return __FUNCTION__ . '();';
}

Deduplication and Smart Scheduling

Without deduplication, user gets three push in an hour. Rules:

  • One scenario — no more than once per 24 hours per user
  • Don't send push from 23:00 to 09:00 (user's timezone)
  • If user already bought after event — cancel task

User timezone from profile or geolocation on registration.

Execution Timeline

Scope Timeline
Abandoned cart + status change 2–3 days
Price drop + back in stock +2 days
Deduplication + timezones + analytics +1–2 days

Each working triggered scenario is an automated salesman who doesn't take salary.