Delivery service integration: SDEK, Boxberry, Russian Post, DHL
An online store loses customers not on the product page, but at the delivery selection step. Too few options, incorrect rates, no calculator — and the buyer leaves. According to research, inconvenient delivery selection is in the top 3 reasons for abandoned carts.
Delivery service integration is not just "show a list of pickup points." This means current rates by weight and dimensions, automatic shipment creation, status tracking, API error handling.
Real integration difficulties
Each service has its own API, its own degree of documentation maturity, and its own set of non-obvious limitations.
SDEK API v2 — most mature of Russian carriers. OAuth 2.0 authorization (token lives 24 hours, needs refresh logic), REST JSON. Rate calculation via POST /v2/calculator/tariff, pickup point list via GET /v2/deliverypoints. Typical mistake: forget to pass from_location and packages with real weight and dimensions — response comes back error_code: 3 without explanation. Pickup points should be cached (list changes infrequently), otherwise each checkout request generates separate API call.
Boxberry API — simpler in functionality, XML in some methods (legacy), part of API — REST. Token passed as GET parameter (not Authorization header), which is unusual. Pickup point list returns all at once (~2MB JSON), must definitely be cached in Redis or DB with nightly update.
Russian Post API — most complex of Russian services. SOAP + REST hybrid, requires contract and setup in personal account. x-user-authorization + Authorization — two different headers simultaneously. Standard shipments, EMS, 1st class — different tariff groups. Postal codes (post offices) — separate directory, not always current.
DHL Express API — for international delivery. XML-based API (DHL XML Services), although there's a newer MyDHL+ API. Requires registered account number. Rate Request for calculation, Shipment Request for creating shipment, returns PDF with label.
How we build integration
Abstraction over providers. No store uses one delivery service forever. We build single interface: DeliveryProvider with methods calculateRates(), createShipment(), trackShipment(), getPickupPoints(). Each service — separate implementation. Switch provider or add new — doesn't mean rewriting checkout.
Pickup points caching. Geosearch for pickup points by coordinates or city — frequent query. Can't pull from API every time (limits, delay). Scheme: nightly job updates pickup_points table in PostgreSQL with PostGIS or just with lat/lng. Finding nearest — ORDER BY ST_Distance() or simple Haversine formula if PostGIS is overkill.
Frontend widget. SDEK provides official JS widget (@cdek-it/widget) — quick, but limited in customization. For non-standard designs — custom widget: map (Yandex Maps API or Leaflet with 2GIS tiles), pickup point list with filters, detailed point card with business hours.
Status tracking. Order statuses come either via webhook (SDEK supports) or via periodic polling (Boxberry, Russian Post). For polling — job queue (Laravel Queue, Bull for Node.js), check every 4–6 hours, notify customer when status changes via email or SMS.
Case study: multi-carrier for WooCommerce. Sports nutrition store: SDEK + Boxberry + self-pickup from 3 stores. WooCommerce Shipping plugin didn't provide needed flexibility — wrote custom Shipping Method. calculate_shipping() makes parallel requests to both APIs via GuzzleHttp\Pool, aggregates rates, filters by delivery zone (no SDEK — show only Boxberry). Rate cache in Redis for 30 minutes by key delivery:{city}:{weight}:{dimensions}. Calculation time: was 2.8s (sequential requests), became 380ms (parallel + cache).
Process and timelines
Requirements audit (which services, which scenarios, tracking needed?) → architecture selection → backend API integration → pickup points caching → frontend widget → testing with real shipments in test mode → deployment.
| Scenario | Timeline |
|---|---|
| One service (SDEK or Boxberry), WooCommerce | 1–2 weeks |
| Two-three services + map widget | 3–5 weeks |
| Full multi-carrier + tracking + notifications | 6–10 weeks |
Cost calculated individually.







