Download the PHP package sonnenglas/mydhl-php-sdk without Composer
On this page you can find all versions of the php package sonnenglas/mydhl-php-sdk. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download sonnenglas/mydhl-php-sdk
More information about sonnenglas/mydhl-php-sdk
Files in sonnenglas/mydhl-php-sdk
Package mydhl-php-sdk
Short Description Unofficial PHP SDK for MyDHL REST API (DHL Express)
License MIT
Informations about the package mydhl-php-sdk
mydhl-php-sdk
Unofficial PHP SDK for the DHL Express MyDHL REST API (currently aligned with spec 3.3.1, June 2026). That version is sent verbatim in the x-version header, which DHL requires on every call.
Note: Only the modern REST API is supported. The legacy SOAP API is not.
Requirements
- PHP ^8.2
- ext-json
guzzlehttp/guzzle^7.12.1ramsey/uuid^4.7
Installation
Supported services
| Service | Supported |
|---|---|
| RATING | |
| Retrieve Rates for a one-piece Shipment | ✅ |
| Retrieve Rates for Multi-piece Shipments | ✅ |
| Landed Cost | ✅ |
| PRODUCT | |
| Retrieve DHL Express products | ✅ |
| SHIPMENT | |
| Create Shipment | ✅ |
| Customs / international shipments (export declaration) | ✅ |
| Re-download archived shipment documents | ✅ |
| Electronic Proof of Delivery | ✅ |
| Add pieces to an existing shipment | ✅ |
| Upload updated customs docs for shipment | ✅ |
| Upload Commercial Invoice Data for shipment | ✅ |
| TRACKING | |
| Track a single DHL Express Shipment | ✅ |
| Track multiple DHL Express Shipments (batch) | ✅ |
| PICKUP | |
| Create a DHL Express pickup booking request | ✅ |
| Cancel a DHL Express pickup booking request | ✅ |
| Update pickup information | ✅ |
| Check whether a pickup was actually collected (via tracking) | ✅ |
| IDENTIFIER | |
| Allocate identifiers upfront | ✅ |
| ADDRESS | |
| Validate DHL Express pickup/delivery capability | ✅ |
| INVOICE | |
| Upload Commercial Invoice data | ✅ |
| SERVICE POINTS / REFERENCE DATA | |
| Look up servicepoints / reference data | ✅ |
| EARLY SHIPMENT SCREENING | |
| Screen break-bulk baby shipments | ✅ |
Design
The SDK splits responsibilities between value objects (immutable, validated request payloads) and services (thin transport that talk to DHL):
RateRequest,ShipmentRequest,PickupRequest,Pickup,ExportDeclaration, … — immutable inputs, validated in their constructors.RateService::getRates(RateRequest)/getRatesForMultiPieceShipment(MultiPieceRateRequest)— returnRate[].LandedCostService::getLandedCost(LandedCostRequest)— duties, taxes and fees before shipping.ProductService::getProducts(ProductRequest)— available products without full pricing.ShipmentService::createShipment(ShipmentRequest)— returnsShipment; plusaddPieces(...),uploadImage(...),uploadInvoiceData(...)for existing shipments.TrackingService::track(...)/trackBatch(...)PickupService::book(...)/update(...)/cancel(...)ImageService::getImages(...)— re-download archived customs/waybill PDFs.ProofOfDeliveryService::getProofOfDelivery(...)AddressService::validate(...)/isServiceable(...)— pickup/delivery capability checks.IdentifierService::allocate(...)— pre-allocate waybill / invoice-reference identifiers.InvoiceService::uploadInvoiceData(...)— standalone Commercial Invoice data upload.ServicePointService::search(...)/findByAddress(...)/findByCoordinates(...)ReferenceDataService::get(...)— country lists, service codes and other datasets.EarlyShipmentScreeningService::screen(...)— pre-screen break-bulk baby shipments.
Every required field is a constructor parameter, so missing data fails at request-build time, not somewhere inside the API call.
Every getXService() call hands back the same instance for the lifetime of the MyDHL object, so getLastRawResponse() still returns the payload of the call you just made.
Error handling
HTTP and transport failures surface as Sonnenglas\MyDHL\Exceptions\ClientException — Guzzle exceptions never leak out of the SDK:
getMessage() carries the method, the URI and the response body, truncated at 2 000 characters so it stays loggable — reach for getResponseBody() when you need the whole payload. Credentials are sent as an Authorization header and never appear in the message.
Quick start
The base URLs are baked in:
| Environment | URL |
|---|---|
| Sandbox | https://express.api.dhl.com/mydhlapi/test/ |
| Production | https://express.api.dhl.com/mydhlapi/ |
Sandbox is rate-limited to 500 calls/day per credential set.
Usage
Retrieve rates
Multi-piece rates
For shipments with more than one package, use the POST variant:
Landed cost
Estimate duties, taxes and fees for an international shipment before creating it:
LandedCostResult::$products holds one LandedCost per DHL product; $warnings collects anything DHL flagged about the quote.
Available products
Like /rates, but without the full price breakdown — useful for showing which DHL products serve a lane:
Validate an address
Allocate identifiers upfront
IdentifierService::TYPES lists the codes DHL accepts: SID (shipment), PID (piece), ASID3 / ASID6 / ASID12 / ASID24 (alternative shipment identifiers) and HUID (handling unit). Anything else throws InvalidArgumentException before the call is made.
Create a domestic shipment
description (1–70 characters) and incoterm are required on every shipment, domestic ones included — not just customs-declarable ones. Both are validated in the ShipmentRequest constructor, so an omitted value fails locally instead of coming back as a 422.
Customs / international shipments
International shipments need declaredValue, an ExportDeclaration with line items, and (recommended) a tax RegistrationNumber:
The incoterm sits on the ShipmentRequest here. When the same goods are sent to
upload-invoice-data, DHL
expects it inside the declaration instead — that endpoint uses a different schema.
Track a shipment
track() asks DHL for the GMT offset of every scan, so TrackingEvent::getOccurredAt()
returns a correctly zoned timestamp. The batch endpoint has no such option — its events
carry no offset, so use track() when event times matter.
Check whether a pickup actually happened
MyDHL has no read endpoint for pickups — /pickups only accepts POST, PATCH and DELETE.
A dispatch confirmation number therefore proves that the booking was accepted, not that a
courier ever showed up. The scans are the only evidence:
NotCollected is the one worth alerting on: a booked pickup with no scan hours later means
the parcel is still sitting in the warehouse. The same answer is available on the shipment
itself, together with the scan behind it:
Piece-level scans count too — DHL does not always mirror them onto the shipment.
Book / update / cancel a courier pickup separately
Use this when the shipment was created with Pickup::notRequested() and the pickup needs to be booked (or cancelled) independently — typical when an order is cancelled hours before pickup time.
Moving a booking to another slot does not need a cancel + re-book — update() keeps the
confirmation number. DHL replaces the booking with what you send, so pass a complete
PickupRequest plus the account the pickup was originally booked with:
plannedPickupDateAndTime must be in the future and at most 10 days ahead — DHL rejects
anything outside that window.
Re-download archived documents (waybill, customs invoice)
/get-imagedoes not return the transport label. The label is returned inline only atcreateShipmenttime. SaveShipment::getLabelPdf()then.
Modify an existing shipment (add pieces, upload customs docs / invoice data)
All three operations target a shipment that already exists; DHL enables them per customer:
The standalone variant (before the shipment exists) lives on InvoiceService:
The invoice-upload declaration is not the shipment declaration
DHL uses two different schemas for exportDeclaration, and both reject unknown fields. An
ExportDeclaration built for createShipment is therefore not reusable here: uploading
invoice data additionally requires an incoterm on the declaration and a function on the
invoice, while the invoice's signatureName, signatureTitle, totalNetWeight and
totalGrossWeight are only accepted by createShipment.
The SDK keeps both shapes in one pair of value objects and picks the right serialization per endpoint, so you only have to supply the extra fields:
Both extra arguments are optional on the constructor (so shipment declarations stay unchanged) but validated when the upload request is built, which means a missing one fails locally instead of coming back as a 422.
Service points
search(ServicePointRequest) exposes the full query surface (geo radius, capabilities, opening hours, …).
Reference data
getDataset() returns null when DHL has no rows for the dataset; use get() instead when you want the full ReferenceDataResult wrapper. ReferenceDataService::ALLOWED_DATASETS lists every dataset DHL publishes (countries, currencies, service codes, …). Rows are returned as raw associative arrays because each dataset has its own schema.
Early shipment screening
Pre-screen break-bulk baby shipments before tendering them:
Proof of Delivery
Full examples:
- Check Rate
- Landed Cost
- Create Shipment
- Track Shipment
Development
Integration tests against the DHL sandbox
Copy tests/Integration/.env.example and export your sandbox credentials, then:
Without these env vars the integration suite auto-skips, so contributor laptops and CI without secrets stay green. Each integration run consumes one or two of the daily 500 sandbox calls — keep them deliberate.
Upgrading
- From 2.x → 3.0: see
UPGRADE-2.x-to-3.0.md. Covers every remaining endpoint of spec 3.3.1 and realigns the existing payloads with it: failed requests now throw the SDK's ownClientExceptioninstead of a Guzzle exception, service getters return a shared instance,descriptionandincotermare required on every shipment, theRegistrationNumberandCustomerReferencetype codes are validated against DHL's enums (several constants changed value), andtrackBatch()enforces the 200-waybill limit. - From 1.x → 2.0: see
UPGRADE-1.x-to-2.0.md. Most callers only need to update theShipmentresponse getters that became nullable; international shipments need the newExportDeclaration/declaredValuearguments. - From 0.x → 1.0: the fluent setter API on services was replaced by immutable Request VOs. See the 1.0 release notes.
Credits
Built and maintained by Przemek Peron.
License
MIT
All versions of mydhl-php-sdk with dependencies
ext-json Version *
guzzlehttp/guzzle Version ^7.12.1
ramsey/uuid Version ^4.7