Setting Up Shipment Tracking on 1C-Bitrix
Shipment tracking seems simple until you do it right. "Show order status" means: get real-time status from the carrier, store history, notify customers on changes, and display correctly in the personal account. This requires solid architecture, a fault-tolerant agent, and integration with various APIs. As certified 1C-Bitrix specialists, we deliver a proven turnkey solution. Manual status checking is expensive: an operator spends on average 5 minutes per order, and with 500 orders per month, that's 40–60 working hours. Automating tracking is 5 times more efficient and pays off in a few months.
Why manual status checking is inefficient
Each delivery service has its own API format, request frequency limits, and authentication methods. For example, CDEK uses OAuth tokens and returns detailed status with 30+ codes, while Russian Post requires an access key and works over SOAP. We implement an abstraction: a unified TrackerInterface with specific trackers written for each carrier. This simplifies adding new carriers and replacing outdated ones. Automated tracking reduces support load by 5 times compared to manual checking — confirmed by projects where we've implemented it.
Problems that tracking solves
Typical pain points of an online store: customers call "where is my order?", operators spend hours manually checking statuses, lost parcels are detected too late. Automated tracking addresses these issues:
- Support load reduced by 60–70% — customers see statuses themselves.
- Early failure detection — if status hasn't updated for N days, the system sends an alert.
- Increased loyalty — process transparency accelerates repeat purchase decisions (repeat orders increase by 15–20% according to our project data).
Why automated tracking is more profitable than manual
Automation pays off through reduced support costs and fewer order cancellations due to uncertainty. In one project, after implementation, delivery-related support tickets dropped by 70%, and repeat purchases increased by 15%. Additionally, the system automatically detects delivery delays — you can respond promptly. Compare: manual checking of 500 orders takes ~40 hours per month, while an automated agent takes 0.5 hours on the server.
How we do it: stack and implementation example
We use standard Bitrix mechanisms: agents, ORM, infoblocks v2.0. For history storage, we create a custom_tracking_history table (HL-block or custom table via ORM). The agent polls carrier APIs for orders in active statuses (N, P, Q). Example base structure:
// Tracking history table (migration) class TrackingHistoryTable extends \Bitrix\Main\ORM\Data\DataManager { public static function getTableName(): string { return 'custom_tracking_history'; } public static function getMap(): array { return [ new \Bitrix\Main\ORM\Fields\IntegerField('ID', ['primary' => true, 'autocomplete' => true]), new \Bitrix\Main\ORM\Fields\IntegerField('ORDER_ID'), new \Bitrix\Main\ORM\Fields\StringField('CARRIER'), // sdek, boxberry, etc. new \Bitrix\Main\ORM\Fields\StringField('TRACK_NUMBER'), new \Bitrix\Main\ORM\Fields\StringField('STATUS_CODE'), new \Bitrix\Main\ORM\Fields\StringField('STATUS_TEXT'), new \Bitrix\Main\ORM\Fields\StringField('LOCATION'), // current location new \Bitrix\Main\ORM\Fields\DatetimeField('STATUS_DATE'), new \Bitrix\Main\ORM\Fields\DatetimeField('CREATED_AT'), ]; } } The agent cyclically polls orders and on status change saves a record and sends notification. Agent code:
class DeliveryTrackingAgent { public static function run(): string { // Select orders with tracking numbers in active statuses $orders = \Bitrix\Sale\Order::getList([ 'filter' => [ '!PROPERTY_TRACK_NUMBER' => false, 'STATUS_ID' => ['N', 'P', 'Q'], // not closed ], 'select' => ['ID', 'PROPERTY_TRACK_NUMBER', 'PROPERTY_CARRIER_CODE'], ]); $carriers = [ 'sdek' => new SdekTracker(), 'boxberry' => new BoxberryTracker(), 'pochta' => new PochtaTracker(), ]; while ($order = $orders->fetch()) { $carrier = $carriers[$order['PROPERTY_CARRIER_CODE_VALUE']] ?? null; if (!$carrier) continue; try { $status = $carrier->getStatus($order['PROPERTY_TRACK_NUMBER_VALUE']); self::saveStatus($order['ID'], $status); } catch (\Throwable $e) { // log, don't crash \Bitrix\Main\Diag\Debug::writeToFile($e->getMessage(), 'tracking', '/local/logs/tracking.log'); } } return __CLASS__ . '::run();'; } private static function saveStatus(int $orderId, array $status): void { // Check if status changed $last = TrackingHistoryTable::getRow([ 'filter' => ['ORDER_ID' => $orderId], 'order' => ['STATUS_DATE' => 'DESC'], 'select' => ['STATUS_CODE'], ]); if ($last && $last['STATUS_CODE'] === $status['code']) return; // unchanged TrackingHistoryTable::add([ 'ORDER_ID' => $orderId, 'STATUS_CODE' => $status['code'], 'STATUS_TEXT' => $status['text'], 'LOCATION' => $status['location'] ?? '', 'STATUS_DATE' => new \Bitrix\Main\Type\DateTime($status['date']), 'CREATED_AT' => new \Bitrix\Main\Type\DateTime(), ]); // Send notification on status change self::notifyCustomer($orderId, $status); } } Example notification (email template)
// In notifyCustomer method $event = new \Bitrix\Main\Mail\Event(array( 'EVENT_NAME' => 'TRACKING_STATUS_CHANGED', 'LID' => SITE_ID, 'C_FIELDS' => array( 'ORDER_ID' => $orderId, 'STATUS_TEXT' => $status['text'], 'TRACK_NUMBER' => $trackNumber, ), )); $event->send(); Tracking page in the personal account
To display history in the personal account, we use a component that fetches data from TrackingHistoryTable. Example data retrieval:
// Component for displaying tracking history $orderId = (int)$_REQUEST['ORDER_ID']; $order = \Bitrix\Sale\Order::load($orderId); if ($order && $order->getUserId() === $USER->GetID()) { $history = TrackingHistoryTable::getList([ 'filter' => ['ORDER_ID' => $orderId], 'order' => ['STATUS_DATE' => 'ASC'], 'select' => ['STATUS_CODE', 'STATUS_TEXT', 'LOCATION', 'STATUS_DATE'], ])->fetchAll(); // Tracking number for link to carrier site $trackNumber = $order->getPropertyCollection() ->getItemByOrderPropertyCode('TRACK_NUMBER') ?->getValue(); } | Carrier | Tracking link |
|---|---|
| CDEK | CDEK |
| Boxberry | Boxberry |
| Russian Post | Russian Post |
| Yandex Delivery | Via personal account using claim_id |
What is included in the turnkey work
- Analysis of current orders and used carriers.
- Development of tracking history table (HL-block or custom table).
- Writing tracker classes for each carrier.
- Agent configuration with logging and error handling.
- Development of history page in customer personal account.
- Email notifications on status change (email templates).
- Documentation and instructions for adding new carriers.
- Handover of server access and code, training of your developer.
- Technical support for one month after delivery.
Timelines
| Stage | Duration |
|---|---|
| Tracking agent + status history | 2–3 days |
| + Tracking page in personal account | +1 day |
| + Email notifications on status change | +1 day |
Want a consultation for your project? Contact us — we'll assess the scope and propose an optimal solution. We have over 20 tracking projects under our belt, with quality guarantees and on-time delivery. Get a free project estimate — just write to us.

