MDM Express API for Node.js and PHP
MDM Express stores your products and ships them. With dz-delivery, the mdm provider uses its v2 API (parcels, geography, offices) with the same code as every other carrier.
Install
npm install @dz-delivery/core
composer require dz-delivery/sdk
Node.js ≥ 18 (zero dependencies) or PHP ≥ 8.2 (Guzzle 7). Every carrier implements the same interface, so the same code works with the others once the client is configured; some operations are not available on every carrier and throw NOT_SUPPORTED (see the capability matrix).
MDM Express credentials
| Option | Value | Notes |
|---|---|---|
apiKey | API key created in the MDM dashboard | Sent as x-api-key |
storeId | Your MDM store (STR-…) | Required to create a parcel |
MDM Express in TypeScript / Node.js
import { createClient, isShippingError } from '@dz-delivery/core'; const mdm = createClient('mdm', { apiKey: process.env.MDM_API_KEY!, storeId: process.env.MDM_STORE_ID!, // STR-… }); try { const quote = await mdm.getFees({ toWilayaId: 16, toCommuneId: 'Bab Ezzouar' }); const parcel = await mdm.createParcel({ recipientName: 'Mohamed Amine', phone: '0550123456', address: 'Cité 221 lots, n°98', wilayaId: 16, communeName: 'Bab Ezzouar', // products stocked at MDM: their catalog id (PRD-…) productList: [{ name: 'T-shirt', quantity: 1, productId: 'PRD-B5C63A' }], price: 2500, }); const { parcel: current, events } = await mdm.track(parcel.trackingNumber); // Delivered parcels, filtered by MDM const delivered = await mdm.listParcels({ status: 'delivered', pageSize: 50 }); } catch (err) { if (isShippingError(err)) console.error(err.code, err.message); }
MDM Express in PHP / Laravel
use DzDelivery\ClientFactory; use DzDelivery\Errors\ShippingError; $mdm = ClientFactory::create('mdm', [ 'apiKey' => getenv('MDM_API_KEY'), 'storeId' => getenv('MDM_STORE_ID'), ]); try { $parcel = $mdm->createParcel([ 'recipientName' => 'Mohamed Amine', 'phone' => '0550123456', 'address' => 'Cité 221 lots, n°98', 'wilayaId' => 16, 'communeName' => 'Bab Ezzouar', 'productList' => [['name' => 'T-shirt', 'quantity' => 1, 'productId' => 'PRD-B5C63A']], 'price' => 2500, ]); $result = $mdm->track($parcel->trackingNumber); } catch (ShippingError $e) { echo "{$e->code}: {$e->getMessage()}"; }
Full PHP guide (Laravel, errors, testing): PHP guide.
Supported methods
| Method | Support | Notes |
|---|---|---|
createParcel | yes | MDM catalog products (productId or sku = PRD-…) and storeId. |
track | yes | Unified status + history. |
getFees | yes | Exact rate per commune (home or office) and return fee. |
getWilayas / getCommunes | yes | MDM commune ids (DZA161601). |
getStopDesks | yes | Offices of MDM’s partner carriers. |
listParcels | yes | status and updatedSince filters applied by MDM. |
cancelParcel | NOT_SUPPORTED | No cancellation through the API: use the MDM dashboard. |
healthCheck | yes | Checks the key (real call). |
MDM Express statuses → unified statuses
The SDK maps MDM status slugs (read live from GET /api/v2/shipping/parcels/metadata, October 2026) to unified statuses. The status filter of listParcels uses the same table: every MDM slug of the requested status is filtered by MDM.
| MDM status (slug) | Unified status |
|---|---|
out-of-stock | pending |
confirmed, preparing, packaged, ready-for-dispatch | created |
received-by-mdm, dispatched, received, in-transit, ready-for-delivery, out-for-delivery, postponed, delivery-attempt-failed, exchange-pending, refund-pending | in_transit |
delivered, delivered-partially, exchange-collected | delivered |
delivery-failed, returning, return-ready, return-received, return-grouped, returned, exchange-failed, refund-collected, refunded, refunded-partially | returned |
lost | cancelled (terminal, neither delivered nor returned) |
Full list of unified statuses and what they mean: SDK statuses.
Good to know about the MDM Express API
- Tested live (October 2026, read-only): wilayas, communes, offices, fees, list, tracking, errors.
- Fulfillment: MDM ships products stocked in its own warehouse. Every
productListline must carry its MDM catalog id; an unknown product →VALIDATION_ERRORonproductList. priceis what the recipient pays: MDM adds no delivery fee on top.- MDM has no “order reference” field:
orderIdis written to the notes (Ref: …). The MDM search API has no filter for it, so the SDK filtersorderIdon each page it reads: uselistParcels({ orderId, allPages: true })('allPages' => truein PHP) to search every page, not just the first one. - The
baseUrlmust use HTTPS.
Detailed native mapping (endpoints, fields, statuses): PROVIDERS.md.
MDM Express FAQ
Where do I find my MDM API key and storeId?
In the MDM dashboard: the API key in the API settings, the storeId (STR-…) in your store.
Can I ship a product that is not stocked at MDM?
No: MDM rejects products outside its catalog. Add the product to MDM first, then use its PRD-… id.
How do I cancel an MDM parcel?
The API has no cancellation: cancelParcel returns NOT_SUPPORTED. Cancel from the MDM dashboard.
Other carriers
Same interface, same normalized statuses (pending, in_transit, delivered…).