Conformance with the DPD documentation
What the add-on does, against the official DPD Web Connect web services of DPD Germany. The WSDL and XSD files are public and were downloaded unchanged on 7 November 2026 from the DPD stage system (https://public-ws-stage.dpd.com/services/<Service>/<Version>/?wsdl); they are saved in tests/fixtures/wsdl:
| Service | Version | Operations used |
|---|---|---|
| LoginService | 2.0 | getAuth |
| ShipmentService | 4.5 (4.4 is also online; 4.5 is the newer one) | storeOrders |
| ParcelShopFinderService | 5.0 | findParcelShops, getAvailableServices (connection test); findParcelShopsByGeoData and findCities exist in the client but are not used by the checkout |
| ParcelLifeCycleService | 2.0 | getTrackingData |
Why this API. DPD publishes SOAP web services with public WSDL files and a free stage (test) system. There is no public REST specification with a sandbox; the REST variants that the WSDL documentation mentions are described only in the operation texts, so the add-on uses the SOAP interface that has a contract (WSDL + XSD). Every request the add-on builds is validated against the XSD files in the unit tests (tests/unit.php, libxml schema validation) and again by the mock server in the HTTP tests; the answers of the mock are built from the same XSD files. The add-on has not been run against a real DPD account: credentials and DPD’s approval are needed (see the open items).
| Topic | Documented behaviour | What the add-on does |
|---|---|---|
| Environments | Stage https://public-ws-stage.dpd.com/services/..., production https://public-ws.dpd.com/services/... (the soap:address of the WSDL files). Test and production have separate users. Every development using DPD Web Connect has to be approved by DPD before production use (DPD’s statement for the German portal, quoted by third-party integrators; not in the WSDL) |
Test mode = stage host, Live mode = production host; separate DelisID, password and customer number per mode. Ask your DPD contact for approval before you switch to Live |
| Authentication | LoginService.getAuth(delisId, password, messageLanguage) returns Login(delisId, customerUid, authToken, depot). Every other call carries the SOAP header authentication(delisId 8-10 characters, authToken max 64, messageLanguage 5 characters "de_DE"/"en_US") |
Login with the password, token cached (encrypted, per account and mode) for six hours, a fresh login after an authenticationFault (error codes -1 NoAccess, -2 NoRights) and one retry. The WSDL does not state a token lifetime, the six hours are a conservative choice of the add-on. Language en_US |
| Test connection | no dedicated operation | getAuth, then getAvailableServices (read-only) with the token. ShipmentService has no validation mode: the test never creates a shipment |
| Create shipment | storeOrders(printOptions, order[1..30]); each order = generalShipmentData (+parcels, productAndServiceData). Required: sendingDepot (4 digits), product (CL, E830, E12, E18, IE2, PL, MAIL, MAX), sender/recipient addresses, orderType consignment, parcel.weight |
One order per request. sendingDepot = Login.depot (override on the Connection page); product from the service (below); mpsCustomerReferenceNumber1 “Order 000123”; identificationNumber unique per label; mpsExpectedSendingDate = today in Europe/Berlin plus the setting (YYYYMMDD); softwareVersion “Kavio Labs kv_dpd 1.0”; sender.customerNumber is mandatory in the schema and is taken from the Connection page |
| Products | CL DPD CLASSIC, E830 8:30, E12 12:00, E18 18:00, IE2 Express International, MAX; PL, MAIL exist in the enum. 4.5 phased out DPD 10:00, DPD Priority and the thermal formats DPL / PDL |
Services: DPD Classic (CL), DPD Pickup (CL + parcelShopDelivery), 8:30, 12:00, 18:00, MAX, Express International. PL (PARCELLetter) and MAIL are not offered. Which product your contract allows is decided by DPD (it answers with a fault) |
| Weight | parcel.weight: “in grams rounded in 10 gram units … (e.g. 300 equals 3kg)”, minimum 1 |
The weight in units of 10 g (3 kg = 300). The text of the specification is ambiguous (“grams” and “10 gram units”); the add-on follows its example. Verify with a real label that a 1 kg parcel prints 1 kg |
| Parcel size | parcel.volume = LLLWWWHHH in cm without separators, minimum 1001001 |
Sent when length, width and height are set in the settings |
| Addresses | address: name1 (“The maximum length is 35”), street (“(The maximum length is 35)”), city (“always 35” except dangerous goods), street, houseNo (max 8), country ISO alpha-2, zipCode, city; email, phone, mobile optional; addressType PRV or COM on the recipient |
Street and house number split; company becomes name1, the person name2; addressType COM with a company, else PRV; name1, street and city are cut to 35 characters at a word boundary (multi-byte safe); the overflow of a name goes to name2, the overflow of a street to additionalInfo2 / additionalInfo3 (address lines 2 and 3); element order follows the XSD (Parcel::sortAddress) |
| Customer data to DPD | email/phone optional; predict (interactive notification) optional |
Off by default (data protection): no e-mail or phone in the address and no predict. With the setting “Send e-mail and phone number to DPD” the address gets both and predict (channel 1 = e-mail, language = destination country) is added. You need the customer’s consent |
| DPD Pickup (parcel shop) | parcelShopDelivery(parcelShopId, parcelShopPudoId, parcelShopNotification[1..1](channel, value, language)); the recipient is the real recipient. parcelShopId “can be obtained from parcel shop finder” |
The checkout picker uses findParcelShops; the chosen parcelShopId and pudoId are stored with the order and sent. The notification is mandatory in the schema, so for this service the customer’s e-mail address is always sent (channel 1); the picker tells the customer. An order without e-mail address is refused |
| Parcel shop search | findParcelShops(country, zipCode, city, street, houseNo, limit 1-100, availabilityDate yyyy-MM-dd, hideClosed, searchCountry, services); ParcelShopType has parcelShopId, pudoId, company, street, houseNo, zipCode, city, latitude, longitude, distance, openingHours(weekday, openMorning, closeMorning, openAfternoon, closeAfternoon), holiday, services |
Search around the shipping address (limit 12, availability date = tomorrow); the shop list shows name, address and opening hours as DPD sends them. distance is not shown: the specification does not say in which unit it is. ParcelShopFinderService has no “get by id” operation, so the server validates the customer’s choice by searching again and requiring the shop in the result |
| Label | printOptions.printOption(outputFormat PDF/ZPL/ZPL300/PKPASS/BARCODE_IMAGE/MULTIPAGE_IMAGE, paperFormat A4/A6/A7, ...); the answer carries orderResult.output (all labels) and parcelInformation.output per parcel, base64 |
PDF (A6 or A4), ZPL or ZPL300 (A6 only, as documented). The label is stored with the shipment and downloaded from the stored copy |
| Parcel label number | parcel.parcelLabelNumber optional (“Number range and validity are checked”); the answer returns parcelInformation.parcelLabelNumber and dpdReference; mpsId is the shipment number |
The add-on does not send a label number; DPD assigns it. Assumed, to be verified at the stage system (the XSD allows omitting it). The returned number is the tracking number |
| Errors | shipmentResponse.faults[0..3](faultCode, message) with codes SHIPPING_1, SHIPPING_2, COMMON_1 … (list in the XSD); SOAP faults for authentication (authenticationFault), login (LoginException) and data / system faults |
A fault becomes the message of the failed job (COMMON_2: Mandatory input field is empty ...). Connection errors, HTTP 5xx and 429 are retried with back-off; other errors are permanent. The exact SOAP fault encoding is taken from the XSD, no real fault was captured |
| Cash on delivery | parcel.cod was removed in ShipmentService 4.4 (“COD is no longer allowed”) |
Not supported. A payment method can be marked as “cash on delivery” in the settings; labels of such orders are refused with that reason (create them in the DPD portal). No COD amount is ever sent |
| Returns | parcel.returns (“Specifies if parcel is a return parcel”), swap, returnAddress |
“Create return label” on the order: a second storeOrders with sender and recipient swapped (the customer is the sender, your shop the recipient) and returns = true; the PDF is stored as an extra document. The business meaning of returns is not described in the documentation: check one return label with your DPD contact before you offer it. The sender.customerNumber is your number |
| Cancel | no operation in ShipmentService 4.4 / 4.5 (only storeOrders) |
“Cancel” removes the label in the store and marks the shipment cancelled; DPD is not told. Say so to your depot if the parcel was already handed over |
| Close-out / manifest | not part of the web services | Not offered (DPD’s depot or the portal handles the pickup list) |
| Customs | international block for shipments crossing a customs border: customsAmount (two decimals without separator, 14.00 = 1400), customsCurrency, customsInvoice, numberOfArticle (= number of additionalInvoiceLines), additionalInvoiceLines(customsInvoicePosition, quantityItems, customsContent, customsAmountLine, customsOriginAlphanumeric, customsGrossWeight, recipientCountryTarif ...) |
Added for destinations outside the EU customs union (Northern Ireland, postal code BT, counts as inside): amounts in Euro, one line per ordered product, origin from the setting, reasonForExport 01 (sale). Tariff numbers (HS codes) are not sent: CS-Cart has no field for them; DPD may ask for them. Incoterms and payment terms are not set |
| Tracking | ParcelLifeCycleService.getTrackingData(parcelLabelNumber 14 characters) -> trackingresult(shipmentInfo, statusInfo[], contactInfo); StatusInfo.status is SHIPMENT, ACCEPTED, AT_SENDING_DEPOT, ON_THE_ROAD, AT_DELIVERY_DEPOT or DELIVERED, with isCurrentStatus, statusHasBeenReached, location, date, normalItems, importantItems, errorItems |
Mapped to created (SHIPMENT), in transit (ACCEPTED, AT_SENDING_DEPOT, ON_THE_ROAD, AT_DELIVERY_DEPOT), delivered (DELIVERED); an errorItems entry on the current status marks the shipment as a problem. Status text = label of the current status, location and date. A data fault for a parcel DPD has not scanned yet keeps “created”. Polled by cron every two hours |
| Public tracking page | not in the WSDL | https://tracking.dpd.de/status/en_DE/parcel/<number> is the address DPD uses on its website; verify that it opens your parcel |
| Webhooks | none | None; tracking is polled |
| Rate limits | not stated in the WSDL files (DPD Netherlands publishes 30 calls per minute for its own interface) | One call per label, back-off retries, at most 200 shipments checked per cron run |
Not covered in 1.0.0
Section titled “Not covered in 1.0.0”Multi-piece shipments, collection request orders and pickup information (orderType other than consignment), hazardous goods, higher insurance, personal delivery, saturday delivery, guarantee, food, tyres, DPD Predict phone / SMS channels, proactiveNotification, PKPASS and barcode image output, ParcelLifeCycleService getParcelLabelNumberForWebNumber, findParcelShopsByGeoData in the picker, DPD’s REST interface, other DPD countries’ web services (DPD Netherlands, Belgium, … publish their own, different services).
Open items that need a real DPD account
Section titled “Open items that need a real DPD account”- Approval of the integration by DPD and production credentials (DelisID, password, customer number).
- One label from the stage system: check the weight unit, that
parcelLabelNumbermay be omitted, the sending depot of the login, and the fault texts. - Public tracking page link, the meaning of
returns, and the unit ofdistance. - DPD’s logo is not used: no confirmed permission.