Skip to content

Developer notes

How GLS Shipping Labels & Parcel Shops (kv_gls) talks to GLS, with the source of every endpoint, field, status and label format. Items that could not be verified without a GLS test account are flagged Unverified.

GLS runs country-specific APIs. Two were evaluated (2026-11-07):

API Countries Public documentation Test system Decision
MyGLS API (GLS Hungary, Czech Republic, Slovakia, Romania, Slovenia, Croatia) 6 EU countries (+ Serbia, not used in 1.0) Yes: “MyGLS API for system integration” ver. 25.12.11, api.mygls.hu/docs/MyGLS_API.pdf, sample clients mygls_api_20260331.zip Yes: api.test.mygls.<country> (“testing and development”) Used
GLS Group developer portal, ShipIT-Farm API (dev-portal.gls-group.net) DE, AT, BE, LU, DK, NL, PL, IT and others (by GLS country) No: the portal lists no API documents to anonymous visitors (“API documentation is available within the Developer Portal. After logging in …”, portal FAQ) Sandbox exists (third-party integrations name api-sandbox.gls-group.net) Not used in 1.0: nothing could be cited without an account; a second driver is planned once the owner registers

Serbia (api.mygls.rs) is not offered in 1.0: its parcels need SenderIdentityCardNumber (“Only in Serbia! ID card number or PIB”, error 44), Content (error 48) and parcel properties, which this version does not collect, and the public point list has no RS.

The MyGLS API is the only GLS API whose full contract (endpoints, classes, error codes, statuses, label formats) is public and has an official test system, so every field below is cited from it.

Endpoints (MyGLS API, “Country domain API URLs” and the method chapters)

Section titled “Endpoints (MyGLS API, “Country domain API URLs” and the method chapters)”

Base: https://api.mygls.<hr|cz|hu|ro|si|sk> (live) or https://api.test.mygls.<...> (test). REST format: /{service name}.svc/{format}/{methodName}, service ParcelService, format json, HTTP POST (method tables “Request / Method / URL examples”). The sample client sends Content-Type: application/json.

Add-on action Method Document section Notes
Create label, step 1 PrepareLabels “PrepareLabels: Validates parcel data for labels and adds valid parcel data to database” Returns ParcelInfoList[{ClientReference, ParcelId}], PrepareLabelsError[]
Create label, step 2; reprint GetPrintedLabels “Generates parcel numbers and PDF document contains labels in byte array format” ParcelIdList (max. 99), PrintPosition, ShowPrintDialog, TypeOfPrinter; returns Labels (byte[]), PrintDataInfoList, GetPrintedLabelsErrorList
Cancel DeleteLabels “Set DELETED state for labels/parcels with specific database record ID” ParcelIdList (max. 50); returns SuccessfullyDeletedList[{ParcelId, SubParcelIdList}], DeleteLabelsErrorList
Check at GLS (lost answer), Test connection GetParcelList “Get parcel(s) information by date ranges” PickupDateFrom/To, PrintDateFrom/To; returns PrintDataInfoList, GetParcelListErrors
Tracking GetParcelStatuses “Get parcel statuses with/or without POD” ParcelNumber, ReturnPOD (false), LanguageIsoCode left out (“Default: EN”)

Not used: PrintLabels (prepare + print in one call; the two-step flow keeps the ParcelId before the number is generated, see Idempotency), GetPrintData, ModifyCOD, GetParcelListStatuses (its LanguageIsoCode is REQUIRED and EN is not in its list), MasterDataService (needs credentials; the public point list below covers the checkout).

Every request body carries Username (the MyGLS e-mail) and Password: “Password in string representation must be encrypted with SHA512 algorithm to byte array”. The JSON form follows the official PHP sample: "[" . implode(',', unpack('C*', hash('sha512', $pwd, true))) . "]" (a JSON array of 64 integers, Client\Api::passwordBytes). ClientNumberList: “do not use” (not sent). WebshopEngine (“Webshop engine of the website”, REQUIRED, NOT EMPTY, change 17) is sent as CS-Cart on PrepareLabels. A wrong user is answered with HTTP 401 in the sample client (if http_code == "401" die("Unauthorized")); Appendix A codes 14 (“User not exists”) and 27 (“User is not authorized to access to Client”) are treated the same way. Unverified: which of the two a wrong password produces on the test system.

Parcel object (Parcel, Address, Service classes)

Section titled “Parcel object (Parcel, Address, Service classes)”
Field Value sent Source
ClientNumber GLS client number of the Connection page “Unique client number provided by GLS company”
ClientReference CS<order id>-<record id> (-R for returns), written to the database before the call “Client custom tag identifying parcel”, REQUIRED (error 33)
Count 1 to 99 (order page), 1 with INS “maximum 99”, error 29, error 28 “The Count must be 1 because of the INS service”
Content “Order ” or the setting “Parcel info printed on label” (error 34 when required by the user right)
PickupDate today 23:59:59 in the shop’s time zone + offset, "\/Date(<ms>)\/" sample client "\/Date(".(strtotime("2019-12-14 23:59:59") * 1000).")\/" with date_default_timezone_set("Europe/Budapest"); end of day so the UTC instant stays on the same date
PickupAddress sender of the Connection page (or company data) “The address of place where courier pick up the shipment”
DeliveryAddress customer; for returns the shop “The address of destination place”
FinalDeliveryAddress customer address on PSD parcels change 30: “Backup delivery address (recipient’s own address) when using PSD service” Unverified: for PSD the add-on sends the customer address as DeliveryAddress too, as the official PHP sample does (PSD with a street address); the GLS Czech PSD guide asks own-label printers to put the point address there. To be confirmed on the test system.
CODAmount, CODCurrency, CODReference order total in the COD currency, order id Parcel class; error 8 / 1000
ServiceList PSD, FDS, FSS, SM2, INS, 24H, T09, T10, T12, SAT, TGS, SRS / PRS Appendix B + E
ParcelPropertyList [{Content, Weight, Length, Width, Height}] when enabled change 19, “Appendix E: Parcel property list examples”; PackageType is not sent (Unverified whether some accounts require it)

Address: Name, Street, City, ZipCode, CountryIsoCode REQUIRED; HouseNumber “ONLY NUMBER” (change 6, error 23 “cannot be 0”), so the add-on splits “Váci út 12/b” into Street “Váci út”, HouseNumber “12”, HouseNumberInfo “/b” (Parcel::street). ContactName/Phone/Email are sent for PSD (mandatory there), when a service needs them (FDS e-mail, FSS / SM2 / SAT phone) or when “Send e-mail and phone number” is on. Field lengths: the document publishes none. The add-on cuts by characters (mb_substr, never inside a UTF-8 sequence) to house caps (Name / Street 80, City 60, HouseNumberInfo 40, contact 60, Content 80, reference 40); these are NOT GLS limits. GLS answers an over-long or invalid field with error 13 “Parcel validation issue”, shown on the order page.

Service parameters (Appendix E): {"Code":"PSD","PSDParameter":{"StringValue":"2351-CSOMAGPONT"}}, {"Code":"FDS","FDSParameter":{"Value":"<e-mail>"}}, {"Code":"FSS","FSSParameter":{"Value":"<phone>"}} (error 30: not without FDS), {"Code":"SM2","SM2Parameter":{"Value":"<phone>"}}, {"Code":"INS","INSParameter":{"Value":<value>}}, {"Code":"SAT","Value":"<phone>"} (change 28), value-less 24H, T09, T10, T12, TGS, SRS, PRS. PSD “requires additional attributes … ContactName, ContactPhone, ContactEmail”. The GLS PSD guide says PSD “cannot be combined with other services, such as FDS … or FSS”: FDS / FSS are left out on point deliveries. Unverified: the GLS Czech guide states this for the CSV import; the API may accept the combination. Return service: SRS (ShopReturnService) and PRS (Pick & Return Service, ContactPhone required per change 29). The document gives no further semantics: the merchant enables it only when the contract includes it. Unverified: whether a return parcel without SRS / PRS (plain swapped addresses) is accepted by every GLS country.

“Possible values: A4_2x2, A4_4x1, Connect, Thermo, ThermoZPL, ShipItThermoPdf, ThermoZPL_300DPI, ShipItThermoZpl. Note: ThermoZPL uses 203 DPI (8 dpmm), ThermoZPL_300DPI uses 300 DPI” (GetPrintedLabels; changes 14, 18, 21, 22, 26). PrintPosition “Number of page quarter”, “ACCEPTED ONLY FOR A4-FORMAT”. Labels is “PDF document in byte array” (JSON: array of integers, sample implode(array_map('chr', $labels))). For the ZPL types the add-on stores the bytes as .zpl when they contain ^XA (Shipments::format). Unverified: the exact file type returned for Connect, Thermo and ShipItThermoPdf (treated as PDF when the bytes start with %PDF).

  • ParcelId: “Label/Parcel database record ID” (PrepareLabels) = the id for GetPrintedLabels / DeleteLabels.
  • Reprint: GetPrintedLabels is called again with the same ParcelId. Unverified: the document also lists error 18 “Parcel label is already generated”; if MyGLS refuses a second print, the add-on (first print after a lost answer) takes the parcel number over from GetParcelList and asks the merchant to print the label in MyGLS; a refused reprint shows the GLS message.
  • ParcelNumber (PrintDataInfo: “Parcel number without check digit”) = the tracking number stored and sent to GetParcelStatuses (“ParcelNumber Long … REQUIRED”); ParcelNumberWithCheckdigit is kept as a second number.
  • Public tracking page: https://gls-group.com/<country>/en/parcel-tracking/?match=<number>; the GLS page script reads the match query parameter (queryParams.get('match') on gls-group.com/HU/en/parcel-tracking/, checked 2026-11-07; page saved with the build notes). The core carrier link (customer order page, shipment e-mails) uses https://gls-group.com/GROUP/en/parcel-tracking/?match=%s with a single %s that CS-Cart fills; there is no [tracking_number] placeholder. Unverified: whether the page finds a parcel by the number without check digit as well as with it (GLS shows both on labels).
  • Statuses: Appendix G “GLS Status Codes” mapped in Shipments::STATUS_MAP: created 51, 52, 83, 84; delivered 5, 58, 59 (“Parcelshop pickup”), 92, 93 (“Signature confirmed”); waiting at a point 54, 55, 56, 97; returned 23, 40; problem 11-20, 28-31, 33-36, 38, 39, 42-44, 57, 60, 62, 66, 68-75, 88, 89, 401-404, 420; every other code = in transit. Codes arrive as strings (“05”): compared as numbers. A delivered or returned parcel is never downgraded by a later answer.

Errors come in the response lists (PrepareLabelsError, GetPrintedLabelsErrorList, DeleteLabelsErrorList, GetParcelListErrors, GetParcelStatusErrors) as ErrorInfo {ErrorCode, ErrorDescription, ClientReferenceList, ParcelIdList}. Client\Api::raise turns the first one into an exception (status 422, 401 for 14 / 27, 500 for 1001). Code 31 (“Same request sent 5 times within last 5 minutes”) is never retried.

  1. Per-order MySQL lock (Idempotency::lock('label-<order>')), an existing active shipment is returned instead.
  2. A pending row and the deterministic ClientReference are written before PrepareLabels.
  3. The ParcelId is saved before GetPrintedLabels; a print failure leaves a “prepared” row that prints again safely.
  4. No answer (timeout, 5xx, error 1001) = “result unknown”: never retried automatically. “Check at GLS” runs GetParcelList by print date and by pickup date around the request and adopts the parcel with the same ClientReference; if nothing is found, the merchant confirms in MyGLS and closes the row. Return labels: lock, pending marker and -R reference before the call, returnParcelId saved before printing; a dead or unanswered request becomes “unknown” and is never repeated (there is no automatic lookup for returns: the merchant checks MyGLS by the -R reference and confirms). Print, cancel, Check at GLS and confirm share the per-order lock; every state change is an atomic claim (UPDATE ... WHERE status = <read state>) whose result is checked, and tracking answers never overwrite a state changed meanwhile. Cancelling a shipment also deletes its return parcel; a never printed (prepared) record that GLS refuses to delete (Appendix A 6) is closed in the store only. Multi-Vendor parent orders never get a label. Unverified: whether GetParcelList by pickup date lists parcels that were prepared but not printed (the mock does; if MyGLS does not, Check at GLS reports “nothing found” and the merchant checks MyGLS by hand).

Source: GLS Czech Republic, “How to implement ShopDeliveryService” (last update 28.07.2026): “The information on all collection points is available in an XML file … updated once a day in the night. https://ps-maps.gls-czech.com/getDropoffPoints.php?ctrcode=CZ”, for AT, BE, BG, CZ, DE, DK, ES, FI, FR, GR, HU, HR, IT, LU, NL, PL, PT, RO, SK, SI; “To order the service, you only need to know the ParcelShop ID” (attribute ID, e.g. 2351-CSOMAGPONT, the same form as the PSD sample of the MyGLS document). Attributes used: ID, Name, Address, CtrCode, ZipCode, CityName, IsCODHandler, IsParcelLocker, GeoLat, GeoLng, Openings/Openings@Day|OpenHours|MidBreak. The add-on downloads the list once a day per configured country (points_sync job), keeps it in ?:kv_gls_dropoff (a download with less than half of the stored points is not applied), searches it near a postal code or town (centre of the matching points, nearest first) and validates the customer’s choice against it on the server. The XML is parsed with LIBXML_NONET (no external entities).

?:kv_gls_accounts (encrypted password, AES-256-GCM), ?:kv_gls_shipments (one row per label: parcel_id, client_ref, parcel_count, status W pending, A prepared, U unknown, C created, T transit, K at point, D delivered, P problem, R returned, X cancelled), ?:kv_gls_points (point chosen per order), ?:kv_gls_dropoff (GLS point list), ?:kv_gls_jobs, ?:kv_gls_log.

PHP hooks: change_order_status_post (label at a status), get_order_info, place_order, place_suborders (Multi-Vendor), delete_order, template_document_order_context_init (point on invoices). Template hooks: checkout:shipping_method (picker), orders:totals_shipping (customer order page), orders:tabs_extra (admin tab), index:scripts / index:styles. Shipping service class Tygh\Shippings\Services\KvGls (rates are the merchant’s prices; MyGLS has no rate operation). Full page cache: the picker is only on the checkout (not cached); the order page block reads the order, no session data is printed in cacheable dispatches.

  • php81 tests/unit.php / php83 tests/unit.php: every request body checked against tests/fixtures/official/contract.json (classes of the document and of the official C# contract), the official Java sample request, a slice of the real HU point list, error mapping, status mapping, payloads (home, PSD, COD, services, returns), defects checks.
  • node tests/mock-server/server.js 21853: MyGLS mock that follows the documented contract (unknown fields refused, Appendix A validation, 401 on a wrong password) plus test controls.
  • node tests/e2e/e2e.mjs (admin, storefront, labels, tracking, queue) and node tests/e2e/vendor-smoke.mjs (Multi-Vendor vendor panel via vendor.php).

GLS, MyGLS and ParcelShop are trademarks of their owner. This add-on is independent software and is not affiliated with, endorsed by or certified by GLS.