Automate Delivery: Integrating 1C-Bitrix with Nova Poshta
In Ukrainian e-commerce, Nova Poshta is the standard: about 80% of orders go through this service. According to Nova Poshta, up to 5% of waybills contain errors — for 150 orders per day, that's 7-8 incorrect shipments. Each mistake leads to returns and loss of loyalty. Integration via API reduces this figure to 0.5%. Automating waybill creation, tracking, and warehouse selection eliminates manual entry and cuts processing time by 60%.
We (TrueTech) have integrated Bitrix with Nova Poshta on dozens of projects over 5 years and developed an algorithm that eliminates failures. In this article, we'll walk through how to set up integration — from obtaining an API key to full tracking. You'll learn how to avoid common pitfalls and what opportunities a ready module opens up.
How Nova Poshta API Works
Nova Poshta provides a unified JSON API: https://api.novaposhta.ua/v2.0/json/. Authorization via apiKey in the request body. The request format is the same for all operations:
private function apiCall(string $model, string $method, array $props): array
{
$payload = [
'apiKey' => $this->apiKey,
'modelName' => $model,
'calledMethod' => $method,
'methodProperties' => $props,
];
$ch = curl_init('https://api.novaposhta.ua/v2.0/json/');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($payload, JSON_UNESCAPED_UNICODE),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
if (!$response['success']) {
throw new \RuntimeException('НП API: ' . implode(', ', $response['errors']));
}
return $response;
}
Detailed specification is available in the Nova Poshta API Documentation.
City and Warehouse Search
Nova Poshta uses Ref identifiers for all objects. Mapping a Bitrix location to a Nova Poshta city Ref:
public function getCityRef(string $cityName): ?string
{
$cache = \Bitrix\Main\Data\Cache::createInstance();
$key = 'np_city_' . md5($cityName);
if ($cache->initCache(86400, $key, '/np/')) {
return $cache->getVars();
}
$response = $this->apiCall('Address', 'getCities', [
'FindByString' => $cityName,
'Limit' => 5,
]);
$ref = $response['data'][0]['Ref'] ?? null;
if ($ref) {
$cache->startDataCache();
$cache->endDataCache($ref);
}
return $ref;
}
The customer enters a Nova Poshta warehouse number (e.g., "5"), we look up its Ref:
public function getWarehouseRef(string $cityRef, string $warehouseNumber): ?string
{
$response = $this->apiCall('Address', 'getWarehouses', [
'CityRef' => $cityRef,
'WarehouseId' => $warehouseNumber,
]);
return $response['data'][0]['Ref'] ?? null;
}
Creating a Waybill
public function createDocument(
\Bitrix\Sale\Shipment $shipment,
string $recipientCityRef,
string $recipientWarehouseRef
): string {
$order = $shipment->getOrder();
$props = $order->getPropertyCollection();
$response = $this->apiCall('InternetDocument', 'save', [
'NewAddress' => '1',
'PayerType' => 'Recipient', // получатель платит за доставку
'PaymentMethod' => 'Cash',
'CargoType' => 'Cargo',
'Weight' => max($shipment->getWeight() / 1000, 0.1),
'ServiceType' => 'WarehouseWarehouse',
'SeatsAmount' => '1',
'Description' => 'Товар магазина',
'Cost' => (string)round($order->getPrice()),
'CitySender' => $this->getOption('SENDER_CITY_REF'),
'Sender' => $this->getOption('SENDER_COUNTERPARTY_REF'),
'SenderAddress' => $this->getOption('SENDER_WAREHOUSE_REF'),
'ContactSender' => $this->getOption('SENDER_CONTACT_REF'),
'SendersPhone' => $this->getOption('SENDER_PHONE'),
'CityRecipient' => $recipientCityRef,
'RecipientAddress' => $recipientWarehouseRef,
'RecipientsPhone' => $props->getItemByOrderPropertyCode('PHONE')?->getValue(),
'RecipientName' => $props->getItemByOrderPropertyCode('FIO')?->getValue(),
]);
return $response['data'][0]['IntDocNumber'] ?? '';
}
PayerType: Recipient is standard: the recipient pays for delivery upon receipt. PayerType: Sender means the store bears the cost.
Tracking and Error Handling
public function trackDocument(string $docNumber): array
{
$response = $this->apiCall('TrackingDocument', 'getStatusDocuments', [
'Documents' => [['DocumentNumber' => $docNumber]],
]);
return $response['data'][0] ?? [];
}
Returns StatusCode, Status, ScheduledDeliveryDate, warehouse information. A Bitrix agent queries every 2 hours for active shipments.
Typical errors and their handling:
- Invalid warehouse number: the customer enters a non-existent number — the module checks via API and highlights the error before saving.
- City name discrepancies: the customer types "Kiev" instead of "Kyiv". Solution: input normalization and fuzzy search.
- Outdated Ref identifiers: the API may return an error if the directory cache is stale. We use caching with a 24-hour TTL and automatic refresh.
All errors are logged, and the administrator receives a notification. Retries are performed with exponential backoff (up to 3 times).
Preventive measures include warehouse number validation via API before order saving, input normalization, and automatic city Ref substitution. Fuzzy search corrects typos on the fly.
Benefits and Case Study
Manual waybill creation takes 2-3 minutes per order. For 150 orders, that's 7-9 hours per week — the workload of a dedicated employee. Automation via API eliminates this burden and eliminates typos. Compared to manual entry, automation is 3 times faster and reduces delivery errors by 90%. No third-party plugin offers this level of integration without customization.
A clothing store client (~150 orders/day, 99% via Nova Poshta) had a major problem: customers entered warehouse numbers arbitrarily. We implemented input normalization and fuzzy search during checkout. After implementation, incorrectly created waybills dropped from ~15 per week to virtually zero.
Setup and Timelines
- Obtain API key — register in the Nova Poshta cabinet, create an API key.
- Install the module — connect a ready-made solution with sender settings.
- Configure order properties — bind fields for warehouse number, phone, full name.
- Test — create a test waybill, verify tracking.
- Deploy to production — enable the agent for status updates.
Click for more details on API failures
If the Nova Poshta API is temporarily unavailable, the module does not block order placement. The order is saved with a "Pending shipment" flag, and the agent retries on the next run. Maximum delay is 2 hours. On validation errors, the administrator receives an email with a problem description.| Stage | Time (working days) |
|---|---|
| Diagnostics and requirements | 1–2 |
| Core development (waybill creation) | 4–5 |
| Warehouse selection with hints | +2 |
| Tracking and notifications | +2 |
| Cash on delivery | +1 |
| Testing and deployment | 1–2 |
The average full implementation time is up to 8 working days. We work strictly under a contract with fixed deadlines. We guarantee stable module operation after delivery. Certified 1C-Bitrix specialists with 5+ years of experience and 50+ integration projects with Nova Poshta.
What's Included
- Integration module with source code for 1C-Bitrix;
- Documentation for installation, configuration, and operation;
- Administrator training (up to 2 hours);
- Technical support for 30 days after delivery;
- Transfer of all access rights (repository, admin panel, API key).
Want the same? Contact us for a project evaluation. Order a turnkey integration — we'll prepare a commercial proposal and show a demo on your data. Get a consultation today.







