Migration from 2023-10 API & Webhook to 2025-01

The following guide will help you migrate the API and webhook from version 2023-10 to version 2025-01.

What's the difference?

ChangeBeforeAfter
Base URLhttps://api.aftership.com/tracking/2023-10https://api.aftership.com/tracking/2025-01
as-api-version response header value2023-102025-01
as-webhook-version request header value2023-102025-01

What do I need to do?

Make the following updates to your application's code:

  1. Replace the base URL from https://api.aftership.com/tracking/2023-10 to https://api.aftership.com/tracking/2025-01.
  2. Select the Webhook version when editing existing URLs or adding new ones. Please review your webhook settings for verification.
  3. Parse the header to distinguish between different versions. Otherwise, no further action is required.

We have updated the rate limit for all endpoints from sharing one rate limit to each endpoint now having its dedicated rate limit to reduce the mutual impact across endpoints. Find the specific rate limit for each endpoint in the table below.

EndpointRate Limit(request / sec)
GET /trackings6
POST /trackings20
GET /trackings/:slug/:tracking_number5
GET /trackings/:id5
POST /couriers/detect3
POST /estimated-delivery-date/predict-batch20
Other endpoints5

What do I need to do?

Please refer to the updated rate limit table above to manage your requests and prevent encountering frequent 429 errors. If your request volume exceeds the maximum rate limit allocated for a specific endpoint, adjust the frequency of your requests accordingly. No additional action is required if your request volume falls within the defined rate limits.

For all POST / PUT / PATCH endpoints (the request with a body), a 400 Bad Request error will be returned if any field in the request body has an incorrect data type.

Before After

The invalid field in the request body will be ignored.

For example, custom_fields is an invalid field as the data type should be an object:

preparing...

The custom_fields will be ignored as the custom_fields should be an object.

A 400 bad request error will be returned if invalid fields in the request body exist.

example:

preparing...

A 400 bad request error will be returned as the custom_fields should be an object.

What do I need to do?

Make the following updates to your application's code:

  1. Implement error handling mechanisms in your application to capture and handle the 400 Bad Request error.
  2. Ensure that the types of all fields are consistent with the types described in the document.

What's the difference?

ChangeBeforeAfter
Deprecating endpoints- POST /notifications/{tracking_id}/add
- POST /notifications/{tracking_id}/remove
- POST /notifications/{slug}/{tracking_number}/add
- POST /notifications/{slug}/{tracking_number}/remove
- GET /notifications/{tracking_id}
- GET /notifications/{slug}/{tracking_number}
- GET /last_checkpoint/{tracking_id}
- GET /last_checkpoint/{slug}/{tracking_number}
- GET /trackings/{slug}/{tracking_number}
- PUT /trackings/{slug}/{tracking_number}
- DELETE /trackings/{slug}/{tracking_number}
- POST /trackings/{slug}/{tracking_number}/retrack
- POST /trackings/{slug}/{tracking_number}/mark-as-completed
Endpoints removed

What do I need to do?

Make the following updates to your application's code:

  1. Replace the "Add notifications" and "Remove notifications" endpoints with the "Update tracking" endpoint to update customers. Refer to the documentation on updating tracking endpoints for more information.
  2. Replace the "Get tracking notifications" endpoint with the "Get tracking" endpoint to retrieve customer's details. Refer to the documentation on get tracking endpoints for more details.
  3. Replace the last_checkpoint endpoints with the GET /tracking/{id} endpoint.
  4. The GET /tracking/{id} endpoint will respond to all checkpoints in the tracking information. Retrieve the last item in the checkpoints array returned by these endpoints to obtain the most recent checkpoint.
  5. Replace GET /trackings/{slug}/{tracking_number} endpoint with GET /trackings/{id} endpoint. Refer to the documentation on get tracking endpoints for more details.
  6. Replace PUT /trackings/{slug}/{tracking_number} endpoint with PUT /trackings/{id} endpoint. Refer to the documentation on update tracking endpoints for more details.
  7. Replace DELETE /trackings/{slug}/{tracking_number} endpoint with DELETE /trackings/{id} endpoint. Refer to the documentation on delete tracking endpoints for more details.
  8. Replace POST /trackings/{slug}/{tracking_number}/retrack endpoint with POST /trackings/{id}/retrack endpoint. Refer to the documentation on retrack tracking endpoints for more details.
  9. Replace POST /trackings/{slug}/{tracking_number}/mark-as-completed endpoint with POST /trackings/{id}/mark-as-completed endpoint. Refer to the documentation on mark tracking as completed endpoints for more details.
LanguageRepository URL
Javahttps://github.com/AfterShip/tracking-sdk-java
Nodejshttps://github.com/AfterShip/tracking-sdk-nodejs
.NEThttps://github.com/AfterShip/tracking-sdk-net
Pythonhttps://github.com/AfterShip/tracking-sdk-python
Rubyhttps://github.com/AfterShip/tracking-sdk-ruby
PHPhttps://github.com/AfterShip/tracking-sdk-php
Golanghttps://github.com/AfterShip/tracking-sdk-go
New FieldsDescription
carbon_emissionsThe model contains the total amount of carbon emissions generated by the shipment. This is a paid service available only for Enterprise plan users. Please contact your customer success manager if you need this.
eventsThe specific event(s) that occurred to a shipment, such as, returned_to_sender. You can find the full list of events and reasons here.
location_idThe location_id refers to the place where you fulfilled the items. If you provide a location_id, the system will automatically use it as the tracking's origin address.
shipping_methodIt refers to the chosen method for delivering the package. Merchants typically offer various shipping methods to consumers during the checkout process, such as, Local Delivery, Free Express Worldwide Shipping, etc. If you have this information, pass the shipping_method while creating or updating the tracking pass the shipping_method while creating or updating the tracking if you have this information.
failed_delivery_attemptsThis field dynamically tracks failed delivery attempts during the delivery of the shipment, allowing you to identify the carriers responsible for the most failures, analyze the root cause, and improve carriers' delivery SOP.
signature_requirementSignature requirement confirmation is a service offered by shipping carriers that provides an extra level of security and proof of delivery for shipments. When using the service, the recipient's signature is required upon delivery.
legacy_idThe length of the tracking ID has been increased from 24 characters to 32 characters. We will use the legacy_id field to store the original 24-character tracking ID to maintain compatibility with existing data. Therefore, all tracking endpoints will continue to work with the legacy_id field as before.
shipment_weightThe shipment weight and unit are obtained from the carrier by default. In scenarios where the carrier does not provide the weight, users can input it manually. AfterShip will prioritize returning the weight value provided by the carrier whenever available.
aftership_tracking_urlThe field contains the tracking page URL of the individual shipment. It will display the default tracking page or a variation based on any applicable segmentation rules.
aftership_tracking_order_urlThe field contains the order URL that displays the tracking page for the entire order, including all shipments. It will show the default tracking page or a variation based on any applicable segmentation rules.
first_mile.tracking_numberThe tracking number of the first-mile carrier.
first_mile.slugThe Unique code of the carrier responsible for the first mile of the shipping of the package
first_mile.transit_timeThe transit time for the first-mile of a shipment in days. This field is calculated based on whether the handed_over_to_last_mile_carrier or received_by_last_mile_carrier event is detected by AfterShip.the handover event date is used to calculate the first-mile transit time.
first_mile.courier_tracking_linkThe field contains the official tracking URL of the first-mile carrier, if available. The language parameter of this link is determined by the destination country/region and the language associated with the shipment. If the destination country/region and language data is unavailable, AfterShip will default the language parameter to "US".
first_mile.courier_redirect_linkThe field provides the link for modifying delivery instructions (such as delivery date and shipping address), if supported by the first-mile carrier. The language parameter of this link is determined by the destination country/region and the language associated with the shipment. If the destination country/region and language data is unavailable, AfterShip will default the language parameter to "US".
last_mile.transit_timeThe transit time for the last-mile of a shipment in days. This field is calculated based on whether the handed_over_to_last_mile_carrier or the received_by_last_mile_carrier event is detected by AfterShip. The handover event date is used to calculate the last-mile transit time.
customers.*.roleThe role of the customer indicates whether the customer is an individual or a company.
customers.*.languageThe customer’s preferred language. If you have set up AfterShip notifications in different languages, this field is used to send the tracking updates to the customer in their preferred language.
checkpoints.*.sourceThe source of the checkpoint, which can either be from the carrier or when the user marks the tracking as completed.

What do I need to do?

For all new fields, simply parse the new field in the API response and the webhook body if you want to fetch the field; otherwise, no action is required.

The existing array field checkpoints.*.coordinates will be deprecated and replaced with object checkpoints.*.coordinate.

What's the difference?

Change Before After

The existing array field checkpoints.*.coordinates will be deprecated and replaced with checkpoints.*.coordinate.

coordinates
Array[number]

Example:
preparing...
coordinate
Object

Example:
preparing...

What do I need to do?

  1. Remove the parsing for the coordinates field.
  2. Parse the new field coordinate in the API response and the webhook body if you are interested in this information; otherwise, no action is required.

The length of the tracking ID has been increased from 24 characters to 32 characters. The original 24-character ID will continue to be maintained and stored in the new legacy_id field. Both IDs can uniquely identify a tracking. We recommend using the 32-character ID, as the legacy ID will eventually be deprecated in upcoming releases.

What's the difference?

ChangeBeforeAfter
The length of the tracking ID has been changed from 24 characters to 32 characters.my2uktymyz72xld1f7l3h01zc17ed4faec0a4831b675f445aad72e4e

What do I need to do?

  1. Extend the length of the tracking ID to 32 characters if you are using this field. Otherwise, no further action is required.

Currently, the slug in detect couriers supports both an array of strings and comma-separated string formats. Moving forward, we will change it to support only the array of strings format.

What's the difference?

ChangeBeforeAfter
slug no longer supports comma-separated string format when detecting couriers.["ups", "fedex"] or "ups,fedex"["ups", "fedex"]

What do I need to do?

  1. If your input for slug is in comma-separated string format, please convert it to an array of strings. Otherwise, no further action is required.

The order_promised_delivery_date now supports time and timezone information.

What's the difference?

ChangeBeforeAfter
Supports new value format- YYYY-MM-DD- YYYY-MM-DD
- YYYY-MM-DDTHH:mm:ss
- YYYY-MM-DDTHH:mm:ssZ

What do I need to do?

  1. Parse the field with time and timezone in the API response and the webhook body if you want to fetch the field. Otherwise, no further action is required.
  2. Specify the order_promised_delivery_date with time and timezone in the API request body if you want to input the field. Otherwise, no further action is required.

The expected_delivery field will be renamed to courier_estimated_delivery_date and will support time range values.

What's the difference?

Before After

expected_delivery
string or null

Example: 2024-10-03T10:00:00

courier_estimated_delivery_date
Object or null

Example:

preparing...

What do I need to do?

  1. Remove the parsing for the expected_delivery" field.
  2. Parse the new field courier_estimated_delivery_date in the API response and the webhook body if you want to retrieve data from the new field. Otherwise, no further action is required.

The tracking.next_couriers field will be replaced with tracking.last_mile.

Before After
preparing...
preparing...

What do I need to do?

  1. Add the new field in the API request body if you want to use the field.
  2. Remove the parsing for the tracking.next_couriers field.
  3. Parse the new field tracking.last_mile in the API response and the webhook body if you want to retrieve data from this field. Otherwise, no further action is required.

The tracking.customer_name field will be replaced with tracking.customers.*.name.

Before After
preparing...
preparing...

What do I need to do?

  1. Put the new field in the API request body if you want to use the field.
  2. Remove the parsing for the tracking.customer_name field.
  3. Parse the new field tracking.customers.*.name in the API response and the webhook body if you want to retrieve data from the new field. Otherwise, no further action is required.

The tracking.emails field will be replaced with tracking.customers.*.email.

Before After
preparing...
preparing...

What do I need to do?

  1. Put the new field in the API request body if you want to use the field.
  2. Remove the parsing for the tracking.emails field.
  3. Parse the new field tracking.customers.*.email in the API response and the webhook body if you want to retrieve data from the new field. Otherwise, no further action is required.

The tracking.smses field will be replaced with tracking.customers.*.phone_number.

Before After
preparing...
preparing...

What do I need to do?

  1. Put the new field in the API request body if you want to use the field.
  2. Remove the parsing for the tracking.smses field.
  3. Parse the new field tracking.customers.*.phone_number in the API response and the webhook body if you want to retrieve data from the new field. Otherwise, no further action is required.

The tracking.tracking_ship_date field date format has been updated and will support more date-time formats.

Before After
preparing...
preparing...

What do I need to do?

  1. Ensure that valid values are provided for tracking_ship_date in your API request. Otherwise, the API will return a 400 error.
  2. Parse the field tracking.tracking_ship_date in the API response and the webhook body using the new formats if you want to retrieve data. Otherwise, no further action is required.

The following fields have been renamed:

BeforeAfter
tracking.courier_destination_country_iso3tracking.courier_destination_country_region
tracking.origin_country_iso3tracking.origin_country_region
tracking.destination_country_iso3tracking.destination_country_region
tracking.tracking_origin_countrytracking.tracking_origin_country_region
tracking.tracking_destination_countrytracking.tracking_destination_country_region
tracking.checkpoints.*.countrytracking.checkpoints.*.country_region
tracking.checkpoints.*.country_nametracking.checkpoints.*.country_region_name
tracking.aftership_estimated_delivery_date.confidence_scoretracking.aftership_estimated_delivery_date.confidence_code

What do I need to do?

  1. Remove the parsing for the old field.
  2. Parse the new field in the API response and the webhook body if you want to retrieve data from the new field. Otherwise, no further action is required.

Please refer to the Tracking Model New Fields Added

Please refer to the Tracking Model Fields Updated and Tracking Model Fields Renamed

Before After
preparing...
preparing...

What do I need to do?
Remove the tracking object wrapper from the request body when you create/update a tracking or detect couriers.

Before After
preparing...
preparing...

What do I need to do?
Remove the tracking object wrapper from the response body when using the tracking endpoints.

Use cursor parameters instead of page parameters to improve search performance. Additionally, we have standardized the response body envelope for consistency.

What's the difference?

ChangeBeforeAfter
Query parameters updatesUsing page parameters to traverse search results.Use the cursor to get data for the next page.
Response body updatesReturns all query parameters.Removed query parameters Added pagination object with cursor.

What do I need to do?

Make the following updates to your application's code:

  1. Do not fetch query parameters from the response.
  2. Iterate search results using the cursor instead of relying on page number.

If a user inputs an invalid query time parameter, we will no longer set it to the default time value. We will return a 400 error code to indicate the invalid query time parameter, preventing any hidden behaviour.

What's the difference?

ChangeBeforeAfter
Add validation for created_at_maxSet the value to the current time when a user inputs an invalid time valueReturn a 400 error when a user inputs an invalid time value
Add validation for created_at_minSet the value to 120 days ago when a user inputs an invalid time valueReturn a 400 error when a user inputs an invalid time value
Add validation for updated_at_maxSet the value to the current time when a user inputs an invalid time valueReturn a 400 error when a user inputs an invalid time value
Add validation for updated_at_minSet the value to 120 days ago when a user inputs an invalid time valueReturn a400 error when a user inputs an invalid time value

Common mistake

A common mistake when providing datetime values for the above keys is improper character escaping of the "+" sign. For example, when sending a timestamp via a GET request, such as “2024-12-01T16:42:00+00:00”, the plus sign (“+”) must be correctly escaped as “%2B”. Thus, the correct value should be “2024-12-01T16:42:00%2B00:00”. If this character is not properly escaped, a 400 error will occur in the API response.

What do I need to do?

Make the following updates to your application's code:

  1. Implement error-handling mechanisms in your application’s code to capture and handle 400 Bad Request errors.
  2. Ensure that the data types of all fields match the types specified in the document.
New FieldsDescription
order_idA globally-unique identifier for the order. Use commas for multiple values.

What do I need to do?

  1. Put the new field in the API request parameters if you want to use the field. Otherwise, no further action is required.

8.2 POST /trackings endpoint

Please refer to the Tracking Endpoints Common Changes.

Please refer to the Tracking Endpoints Common Changes.

Regardless of the tracking status, you can update the value of the tracking_ship_date field.

What do I need to do?
Include the field in the API request body if you want to update it. Otherwise, no further action is required.

Please refer to the Tracking Endpoints Common Changes.

8.6 POST /trackings/:id/retrack endpoint

Please refer to the Tracking Endpoints Common Changes.

8.7 POST /trackings/:id/mark-as-complete endpoint

Tracking mark as completed endpoint supports event_datetime.

What's the difference?

Change Before After

The request body of tracking mark as completed endpoint now supports the event_datetime field

The request body:

preparing...

The request body:

preparing...

What do I need to do?

Add the new field event_datetime to the API request body if you want to retrieve corresponding data. Otherwise, no further action is required.

A new checkpoint will be created when the user marks the tracking as completed.

Before After
preparing...
preparing...

What do I need to do?

Parse the new field tracking.checkpoints in the API response and the webhook body if you want to retrieve data. Otherwise, no further action is required.

The following fields/enum values have been renamed:

BeforeAfter
courier.service_from_country_iso3courier.service_from_country_region
courier.required_fields enum value - tracking_origin_countrytracking_origin_country_region
courier.required_fields enum value - tracking_destination_countrytracking_destination_country_region

What do I need to do?

  1. Remove the parsing for the old field.
  2. Parse the new field in the API response if you want to retrieve data from the new field. Otherwise, no further action is required.

Please refer to the Courier Model Fields Renamed.

Please refer to the Courier Model Fields Renamed.

10.3 POST /couriers/detect

What's the difference?

ChangeBeforeAfter
tracking_origin_country in the request body renamed to tracking_origin_country_regiontracking_origin_countrytracking_origin_country_region
tracking_destination_country in the request body renamed to tracking_destination_country_regiontracking_destination_countrytracking_destination_country_region
origin_country_iso3 in the request body renamed to origin_country_regionorigin_country_iso3origin_country_region
destination_country_iso3 in the request body renamed to destination_country_regiondestination_country_iso3destination_country_region

What do I need to do?

  1. Remove the tracking_origin_country,tracking_destination_country, origin_country_iso3 and destination_country_iso3 fields in the request body.
  2. Add the new fields tracking_origin_country_region, tracking_destination_country_region, origin_country_region, and destination_country_region in the API request body if you want to retrieve data from these fields. Otherwise, no further action is required.

The following fields have been renamed:

BeforeAfter
estimated_delivery_date.origin_address.countryestimated_delivery_date.origin_address.country_region
estimated_delivery_date.destination_address.countryestimated_delivery_date.destination_address.country_region

What do I need to do?

  1. Remove the parsing for the old field.
  2. Parse the new field in the API response if you want to retrieve data from the new field. Otherwise, no further action is required.

12.1 POST /estimated-delivery-date/predict-batch

The reference ID acts as a unique identifier to associate the estimated delivery date value generated by AfterShip EDD to its corresponding shipment.

What do I need to do?

Parse the new field in the API response if you want to fetch the field. Otherwise, no further action is required.

Indicates the confidence level and associated reason for an AI EDD prediction request. For a comprehensive list of confidence codes, refer to this document.

What do I need to do?

Parse the new field in the API response if you want to fetch the field. Otherwise, no further action is required.

We have added validation checks for the origin_address.postal_code and destination_address.postal_code fields in the Tracking AI EDD. In case of invalid input, the API will now return META.CODE 4008 to indicate the errors.

What do I need to do?

Provide valid values for origin_address.postal_code and destination_address.postal_code in your API request otherwise, the API will return an error.

Please refer to the Tracking model new fields added

Please refer to the Tracking model fields updated

After Oct 9, 2025, any requests made to the 2023-10 version will automatically be redirected to version 2024-07. While this redirection is designed to minimize disruptions, it may not fully support all functionalities available in the newer versions, and therefore, we encourage you to complete the migration at your earliest convenience.