WooCommerce Ship Orders (through Shipment Tracking plugin)
Version | Date | Created / Updated | Notes |
---|---|---|---|
v1.0 | 14/05/2025 | Hristiyan Georgiev | Initial version |
The below documentation is for shipment tracking through a specific WooCommerce plugin which is called Shipment Tracking. Standard shipment is not possible through API without a plugin in WooCommerce. Link for the plugin can be found here - https://woocommerce.com/products/shipment-tracking/
In order to ship orders in WooCommerce we first need to get & store all the available carriers, as when pushing the shipment we need to provide the shipping provider. We also have the possibility to ship with an unlisted courier in WooCommerce and this is sent as custom tracking provider (more explanation below).
Other than that, the shipment process is straightforward - we provide the shipping carrier info, tracking info and dispatch time. With the standard shipment call we are only able to do full shipments so we do not provide product information at all.
This plugin does not currently provide native support for partial shipments via its REST API or built-in features. Partial shipment functionality is typically handled by third-party plugins, which add custom order statuses, shipment tracking, and API endpoints for managing partial shipment. There are many plugins and we will probably have to handle this as we go meaning that we might have to integrate to a couple of “plugins” which handle partial shipment, depending which plugin is used by our customer. This is not part of the scope here.
Get Shipping Providers
Using this call we will be able to store the shipping providers in WooCommerce. We want to display
We also want to introduce Courier mapping, and we will use a dependant table of Courier
called WooCommerce Couriers Mapping
for this use. In this table we need to have a dropdown field with all the carriers. The field will be called WooCommerce Courier
where we will show the names of all the available couriers in WooCommerce.
If WooCommerce adds or removes couriers, we want to respectively add/remove them from the table. We don’t want to truncate the table with each run.
API Call : GET /wp-json/wc-shipment-tracking/v3/shipment-trackings/providers
API Docs : https://woocommerce.com/document/shipment-tracking/
Example response :
{
"Global": {
"Aramex": "https://www.aramex.com/track/track-results-new?ShipmentNumber=%1$s"
},
"Australia": {
"Australia Post": "https://auspost.com.au/mypost/track/#/details/%1$s",
"Fastway Couriers": "https://www.fastway.com.au/tools/track/?l=%1$s",
"Aramex Australia": "https://www.aramex.com.au/tools/track?l=%1$s"
},
"Austria": {
"post.at": "https://www.post.at/sv/sendungsdetails?snr=%1$s",
"dhl.at": "https://www.dhl.at/content/at/de/express/sendungsverfolgung.html?brand=DHL&AWB=%1$s",
"DPD.at": "https://tracking.dpd.de/parcelstatus?locale=de_AT&query=%1$s"
},
"Brazil": {
"Correios": "http://websro.correios.com.br/sro_bin/txect01$.QueryList?P_LINGUA=001&P_TIPO=001&P_COD_UNI=%1$s"
},
"Belgium": {
"bpost": "https://track.bpost.be/btr/web/#/search?itemCode=%1$s&postalCode=%2$s"
},
"Canada": {
"Canada Post": "https://www.canadapost-postescanada.ca/track-reperage/en#/resultList?searchFor=%1$s",
"Purolator": "https://www.purolator.com/purolator/ship-track/tracking-summary.page?pin=%1$s"
},
"Czech Republic": {
"PPL.cz": "https://www.ppl.cz/main2.aspx?cls=Package&idSearch=%1$s",
"Česká pošta": "https://www.postaonline.cz/trackandtrace/-/zasilka/cislo?parcelNumbers=%1$s",
"DHL.cz": "https://www.dhl.cz/cs/express/sledovani_zasilek.html?AWB=%1$s",
"DPD.cz": "https://tracking.dpd.de/parcelstatus?locale=cs_CZ&query=%1$s"
},
"Finland": {
"Itella": "https://www.posti.fi/itemtracking/posti/search_by_shipment_id?lang=en&ShipmentId=%1$s"
},
"France": {
"Colissimo": "https://www.laposte.fr/outils/suivre-vos-envois?code=%1$s"
},
"Germany": {
"DHL Intraship (DE)": "https://www.dhl.de/de/privatkunden/pakete-empfangen/verfolgen.html?lang=de&idc=%1$s&rfn=&extendedSearch=true",
"Hermes": "https://www.myhermes.de/empfangen/sendungsverfolgung/sendungsinformation/#%1$s",
"Deutsche Post DHL": "https://www.dhl.de/de/privatkunden/pakete-empfangen/verfolgen.html?lang=de&idc=%1$s",
"UPS Germany": "https://wwwapps.ups.com/WebTracking?sort_by=status&tracknums_displayed=1&TypeOfInquiryNumber=T&loc=de_DE&InquiryNumber1=%1$s",
"DPD.de": "https://tracking.dpd.de/parcelstatus?query=%1$s&locale=en_DE"
},
"Ireland": {
"DPD.ie": "https://dpd.ie/tracking?deviceType=5&consignmentNumber=%1$s",
"An Post": "https://track.anpost.ie/TrackingResults.aspx?rtt=1&items=%1$s"
},
"Italy": {
"BRT (Bartolini)": "https://vas.brt.it/vas/sped_det_show.hsm?Nspediz=%1$s",
"DHL Express": "https://www.dhl.it/it/express/ricerca.html?AWB=%1$s&brand=DHL"
},
"India": {
"DTDC": "https://www.dtdc.in/tracking/tracking_results.asp?Ttype=awb_no&strCnno=%1$s&TrkType2=awb_no"
},
"Netherlands": {
"PostNL": "https://postnl.nl/tracktrace/?B=%1$s&P=%2$s&D=%3$s&T=C",
"DPD.NL": "https://tracking.dpd.de/status/en_US/parcel/%1$s",
"UPS Netherlands": "https://wwwapps.ups.com/WebTracking?sort_by=status&tracknums_displayed=1&TypeOfInquiryNumber=T&loc=nl_NL&InquiryNumber1=%1$s"
},
"New Zealand": {
"Courier Post": "https://trackandtrace.courierpost.co.nz/Search/%1$s",
"NZ Post": "https://www.nzpost.co.nz/tools/tracking?trackid=%1$s",
"Aramex New Zealand": "https://www.aramex.co.nz/tools/track?l=%1$s",
"PBT Couriers": "http://www.pbt.com/nick/results.cfm?ticketNo=%1$s"
},
"Poland": {
"InPost": "https://inpost.pl/sledzenie-przesylek?number=%1$s",
"DPD.PL": "https://tracktrace.dpd.com.pl/parcelDetails?p1=%1$s",
"Poczta Polska": "https://emonitoring.poczta-polska.pl/?numer=%1$s"
},
"Romania": {
"Fan Courier": "https://www.fancourier.ro/awb-tracking/?tracking=%1$s",
"DPD Romania": "https://tracking.dpd.de/parcelstatus?query=%1$s&locale=ro_RO",
"Urgent Cargus": "https://app.urgentcargus.ro/Private/Tracking.aspx?CodBara=%1$s"
},
"South African": {
"SAPO": "http://sms.postoffice.co.za/TrackingParcels/Parcel.aspx?id=%1$s",
"Fastway": "https://fastway.co.za/our-services/track-your-parcel?l=%1$s",
"EPX": "https://epx.pperfect.com/?w=%1$s"
},
"Sweden": {
"PostNord Sverige AB": "https://portal.postnord.com/tracking/details/%1$s",
"DHL.se": "https://www.dhl.com/se-sv/home/tracking.html?submit=1&tracking-id=%1$s",
"Bring.se": "https://tracking.bring.se/tracking/%1$s",
"UPS.se": "https://www.ups.com/track?loc=sv_SE&tracknum=%1$s&requester=WT/",
"DB Schenker": "http://privpakportal.schenker.nu/TrackAndTrace/packagesearch.aspx?packageId=%1$s"
},
"United Kingdom": {
"DHL": "https://www.dhl.com/content/g0/en/express/tracking.shtml?brand=DHL&AWB=%1$s",
"DPD.co.uk": "https://www.dpd.co.uk/apps/tracking/?reference=%1$s#results",
"DPD Local": "https://apis.track.dpdlocal.co.uk/v1/track?postcode=%2$s&parcel=%1$s",
"EVRi": "https://www.evri.com/track/parcel/%1$s",
"EVRi (international)": "https://international.evri.com/tracking/%1$s",
"ParcelForce": "https://www.parcelforce.com/track-trace?trackNumber=%1$s",
"Royal Mail": "https://www3.royalmail.com/track-your-item#/tracking-results/%1$s",
"TNT Express (consignment)": "https://www.tnt.com/express/en_gb/site/shipping-tools/tracking.html?searchType=con&cons=%1$s",
"TNT Express (reference)": "https://www.tnt.com/express/en_gb/site/shipping-tools/tracking.html?searchType=ref&cons=%1$s",
"DHL Parcel UK": "https://track.dhlparcel.co.uk/?con=%1$s"
},
"United States": {
"DHL US": "https://www.logistics.dhl/us-en/home/tracking/tracking-ecommerce.html?tracking-id=%1$s",
"DHL eCommerce": "https://webtrack.dhlecs.com/orders?trackingNumber=%1$s",
"Fedex": "https://www.fedex.com/apps/fedextrack/?action=track&action=track&tracknumbers=%1$s",
"FedEx Sameday": "https://www.fedexsameday.com/fdx_dotracking_ua.aspx?tracknum=%1$s",
"GlobalPost": "https://www.goglobalpost.com/track-detail/?t=%1$s",
"OnTrac": "https://www.ontrac.com/tracking/?number=%1$s",
"UPS": "https://www.ups.com/track?loc=en_US&tracknum=%1$s",
"USPS": "https://tools.usps.com/go/TrackConfirmAction_input?qtc_tLabels1=%1$s"
}
}
The way we want to store the couriers in MCPro is to concatinate the country and courier name. So an example would be - United States
- DHL US
, United States
- DHL eCommerce
etc. We don’t want to store the links.
Add tracking info to order
We want to use the new shipment model through the Order Shipment
table. All triggers & validations are per the abstraction -
Order management general requirements
We want to introduce a default courier field which will be in the Channels WooCommerce
. The field will be an enum dropdown menu where the user can select the desired default courier. The field should not be required. Even if there is a selected default courier, we should first check if we have mapping from the courier on order with a record in table Courier
and pick this with priority. We should only pick the default courier if there is no mapping for the selected courier.
**The mapping will work based on:
- If
Order Shipment
>Carrier
matchesCourier
>Name
we send the selected courierWooCommerceCourier Mappings
>WooCommerce Courier
- If there is no match or no value in the mapping table we need to go into a third case which is to use our default courier set in the
Channel WooCommerce
table. - If we don’t have a default courier and courier mapping and the user tries to send a courier which is not part of the WooCommerce list, we have the possibility to send the courier as a custom tracking provider (details will be in the mapping below)
API Call : POST /wp-json/wc-shipment-tracking/v3/orders/{orderId}/shipment-trackings API Docs : https://woocommerce.com/document/shipment-tracking/
The orderId we pick from Orders
> Marketplace Order ID
It is important to note that with this call we are not shipping the order, but we are adding the tracking info. The order will still be in processing status and we need to mark it as Completed after we have added the tracking info. We mark it as completed with a subsequent call that we want to make after this one (explanation below). As a suggestion, both calls should be incorporated into one cron.
Call Body :
{
"tracking_provider": "Aramex",
"custom_tracking_provider": "Bai Ivan",
"custom_tracking_link": "https://www.speedy.bg/public/bg/track-shipment",
"tracking_number": "12345678",
"date_shipped": "2025-05-14"
}
Mapping :
WooCommerce FIeld | MCPro Field | Notes |
---|---|---|
tracking_provider |
Order Shipment > Carrier |
OR
WooCommerce Courier Mappings
> WooCommerce Courier
OR
Channel WooCommerce
> Default Courier
| Depends on the conditions mentioned above. We need to trim the country and only send the courier name.
If we are sending custom_tracking_provider
, we need to exclude this field from the payload |
| custom_tracking_provider
| Order Shipment
> Carrier
| This field is only used when we are sending a courier that is not listed on WooCommerce. We need to exclude this from the payload if we are sending a listed courier. |
| custom_tracking_link
| Order Shipment
> Tracking URL
OR
Courier
> Courier URL
| We need to include this field only when sending custom_tracking_provider
in all other cases we exclude it. Order Shipment
is with priority. |
| tracking_number
| Order Shipment
> Tracking Number
| |
| date_shipped
| Order Shipment
> Shipped Time
| Needs to be send in a format of “YYYY-MM-DD”. This field is optional so if we don’t have it filled, we can exclude it from the payload. |
Example Success Response :
{
"tracking_id": "f0e366d87f8c6b0e6b34932b9478729a",
"tracking_provider": "Bai Ivan",
"tracking_link": "https://www.speedy.bg/public/bg/track-shipment",
"tracking_number": "12345678",
"date_shipped": "2025-05-14",
"_links": {
"self": [
{
"href": "https://ecom-delicately-vibrant-shark.wpcomstaging.com/wp-json/wc-shipment-tracking/v3/orders/56/shipment-trackings/f0e366d87f8c6b0e6b34932b9478729a",
"targetHints": {
"allow": [
"GET",
"DELETE"
]
}
}
],
"collection": [
{
"href": "https://ecom-delicately-vibrant-shark.wpcomstaging.com/wp-json/wc-shipment-tracking/v3/orders/56/shipment-trackings"
}
],
"up": [
{
"href": "https://ecom-delicately-vibrant-shark.wpcomstaging.com/wp-json/wc-shipment-tracking/v3/orders/56"
}
]
}
}
Example Error Response :
{
"code": "rest_missing_callback_param",
"message": "Missing parameter(s): tracking_number",
"data": {
"status": 400,
"params": [
"tracking_number"
]
}
}
From the success response we only want to map the tracking_id
into Order Shipment
> External Id
.
From the error response we want to create a record in Order Error
> Type
= Shipping and store the message
.
If we had a success response, at this point we still don’t want to mark our shipment record from pending to completed. As mentioned above, we need to make an additional call to mark the order as completed on WooCommerce.
Mark order as completed
Ideally we would like to perform this call straight after the previous one. However, if for some reason the previous call is success but this one fails, we want to use the Order Shipment
> External Id
as trigger so the next time our order ship cron starts, it will see that we have an external id for a specific record and it will skip the first step of adding the tracking and just try to mark the order as completed.
API Call : PUT /wp-json/wc/v3/orders/{orderId}
API Docs : https://woocommerce.github.io/woocommerce-rest-api-docs/?shell#update-an-order
The orderId we pick from Orders
> Marketplace Order ID
Call body :
{
"status": "completed"
}
We just send the status
hardcoded as “completed”
Example Success Response :
{
"id": 52,
"parent_id": 0,
"status": "completed",
"currency": "USD",
"version": "9.8.5",
"prices_include_tax": false,
"date_created": "2025-05-12T11:18:27",
"date_modified": "2025-05-14T11:26:20",
"discount_total": "0.00",
"discount_tax": "0.00",
"shipping_total": "10.00",
"shipping_tax": "0.00",
"cart_tax": "0.00",
"total": "370.00",
"total_tax": "0.00",
"customer_id": 0,
"order_key": "wc_order_ocLQCVE4FlgYB",
"billing": {
"first_name": "Johnkata",
"last_name": "Doe",
"company": "",
"address_1": "969 Market",
"address_2": "",
"city": "Sofia",
"state": "Sofia",
"postcode": "1700",
"country": "Bulgaria",
"email": "john.doe@example.com",
"phone": "(555) 555-5555"
},
"shipping": {
"first_name": "Johnkata",
"last_name": "Doe",
"company": "",
"address_1": "969 Market",
"address_2": "",
"city": "Sofia",
"state": "Sofia",
"postcode": "1700",
"country": "BG",
"phone": ""
},
"payment_method": "bacs",
"payment_method_title": "Direct Bank Transfer",
"transaction_id": "",
"customer_ip_address": "",
"customer_user_agent": "",
"created_via": "rest-api",
"customer_note": "",
"date_completed": "2025-05-14T11:26:20",
"date_paid": "2025-05-12T11:18:27",
"cart_hash": "",
"number": "52",
"meta_data": [
{
"id": 14,
"key": "_wc_shipment_tracking_items",
"value": [
{
"tracking_provider": "aramex",
"custom_tracking_provider": null,
"custom_tracking_link": null,
"tracking_number": "12345678",
"date_shipped": 1747210980,
"tracking_id": "6738eed4d77cf648608183a8709bb8be"
}
]
}
],
"line_items": [
{
"id": 7,
"name": "Popover Heavyweight Hooded Sweatshirt in Red",
"product_id": 17,
"variation_id": 0,
"quantity": 2,
"tax_class": "",
"subtotal": "240.00",
"subtotal_tax": "0.00",
"total": "240.00",
"total_tax": "0.00",
"taxes": [],
"meta_data": [],
"sku": "",
"price": 120,
"image": {
"id": "23",
"src": "https://ecom-delicately-vibrant-shark.wpcomstaging.com/wp-content/uploads/2025/05/6dc67-product-4.png"
},
"parent_name": null
},
{
"id": 8,
"name": "Vintage Slub Cotton Crewneck T-shirt",
"product_id": 18,
"variation_id": 0,
"quantity": 3,
"tax_class": "",
"subtotal": "120.00",
"subtotal_tax": "0.00",
"total": "120.00",
"total_tax": "0.00",
"taxes": [],
"meta_data": [],
"sku": "",
"price": 40,
"image": {
"id": "25",
"src": "https://ecom-delicately-vibrant-shark.wpcomstaging.com/wp-content/uploads/2025/05/507df-product-2.png"
},
"parent_name": null
}
],
"tax_lines": [],
"shipping_lines": [
{
"id": 9,
"method_title": "Flat Rate",
"method_id": "flat_rate",
"instance_id": "",
"total": "10.00",
"total_tax": "0.00",
"taxes": [],
"tax_status": "taxable",
"meta_data": []
}
],
"fee_lines": [],
"coupon_lines": [],
"refunds": [],
"payment_url": "https://ecom-delicately-vibrant-shark.wpcomstaging.com/checkout/order-pay/52/?pay_for_order=true&key=wc_order_ocLQCVE4FlgYB",
"is_editable": false,
"needs_payment": false,
"needs_processing": true,
"date_created_gmt": "2025-05-12T08:18:27",
"date_modified_gmt": "2025-05-14T08:26:20",
"date_completed_gmt": "2025-05-14T08:26:20",
"date_paid_gmt": "2025-05-12T08:18:27",
"gift_cards": [],
"currency_symbol": "$",
"_links": {
"self": [
{
"href": "https://ecom-delicately-vibrant-shark.wpcomstaging.com/wp-json/wc/v2/orders/52",
"targetHints": {
"allow": [
"GET",
"POST",
"PUT",
"PATCH",
"DELETE"
]
}
}
],
"collection": [
{
"href": "https://ecom-delicately-vibrant-shark.wpcomstaging.com/wp-json/wc/v2/orders"
}
],
"email_templates": [
{
"embeddable": true,
"href": "https://ecom-delicately-vibrant-shark.wpcomstaging.com/wp-json/wc/v2/orders/52/actions/email_templates"
}
]
}
}
Example error response :
{
"code": "rest_invalid_param",
"message": "Invalid parameter(s): status",
"data": {
"status": 400,
"params": {
"status": "status is not one of auto-draft, pending, processing, on-hold, completed, cancelled, refunded, failed, and checkout-draft."
},
"details": {
"status": {
"code": "rest_not_in_enum",
"message": "status is not one of auto-draft, pending, processing, on-hold, completed, cancelled, refunded, failed, and checkout-draft.",
"data": null
}
}
}
}
From the success response we want to store the date_completed_gmt
into Order Shipment
> Shipment Processed Time
and mark the order as shipped.
From the error response we want to create a record in Order Error
> Type
= Shipping and store the details
> status
>message