🇫🇷 Version française : lire ici
If you have built an online store in Algeria, you know the drill: every delivery carrier has its own API, and none of them look alike.
- Yalidine:
X-API-ID/X-API-TOKENheaders, a mandatory origin wilaya,page_sizepagination. - ZR Express:
X-Api-Key+X-Tenant, a create endpoint that only returns a UUID, territory lookups throughPOSTsearch endpoints. - DHD, Anderson, World Express (all on the Ecotrack platform): Bearer token, query-string parameters, orders that must be validated after creation.
- Maystro: no tracking API at all — everything comes through webhooks.
And every one of them speaks its own status language: "En préparation", OrderReceived, 41…
So I built dz-delivery, an open-source (MIT) SDK that exposes one interface for all of these carriers, in TypeScript and PHP.
Install
npm install @dz-delivery/core # Node.js ≥ 18, zero runtime dependencies composer require dz-delivery/sdk # PHP ≥ 8.2 Same code, any carrier
import { createClient } from '@dz-delivery/core'; const shipping = createClient('yalidine', { apiId: process.env.YALIDINE_API_ID!, apiToken: process.env.YALIDINE_API_TOKEN!, fromWilayaId: 16, // origin wilaya (Algiers) }); // or: createClient('zrexpress', { apiKey, tenant }) // or: createClient('ecotrack', { token, courier: 'dhd' }) const quote = await shipping.getFees({ toWilayaId: 31 }); // { deliveryFee, stopDeskFee, currency: 'DZD' } const parcel = await shipping.createParcel({ recipientName: 'Mohamed Amine', phone: '0550123456', address: 'Cité 221 lots, n°98', wilayaName: 'Oran', communeName: 'Bir El Djir', productName: 'T-shirt', price: 2500, // cash on delivery, in DZD }); const { parcel: current, events } = await shipping.track(parcel.trackingNumber); console.log(current.status); // 'pending' | 'in_transit' | 'delivered' | 'returned' | ... In PHP / Laravel it is the same thing with ClientFactory::create('yalidine', [...]).
What the SDK handles for you
- Wilayas and communes: pass the official code (1–58), the carrier's own id, or a name — the SDK resolves it before calling the API.
- Phone numbers: normalized to the format each carrier expects.
- Unified statuses:
pending,created,in_transit,delivered,returned,cancelled,unknown. The native response is still available inraw. - Typed errors:
AUTH_FAILED,VALIDATION_ERROR,NOT_FOUND,RATE_LIMITED,NOT_SUPPORTED… - Stop desks:
getStopDesks({ wilayaId })for carrier offices and pickup points. - Account sync:
listParcels()to pull parcel statuses (ZR Express). - ZR Express extras: PDF labels, bulk create/delete, signed webhook verification.
Carrier guides
- Yalidine / GuepEx API
- ZR Express API
- Maystro Delivery API
- Ecotrack API (DHD, Anderson, World Express…)
Contributing
The code is on GitHub: https://github.com/developing-off/dz-delivery. Contributions are very welcome — especially new carriers (NoEST, MDM…) or anonymized real API responses to strengthen the test fixtures.
If you use it in production, let me know in the comments 🙏


