Migration from 2024-04 API & Webhook to 2025-07
Important Update: Changes to Our API Versioning Schedule
Starting from version 2025-07, we’re adjusting our API version release cadence to enhance stability and quality. New major versions of our API will now be released once every six months (twice yearly), replacing our previous quarterly schedule
The following guide will help you migrate the API and webhook from version 2024-04 to version 2025-07.
1. General API Changes
1.1 Endpoint Change
What's the difference?
| Change | Before | After |
|---|---|---|
| Base URL | https://api.aftership.com/tracking/2024-04 | https://api.aftership.com/tracking/2025-07 |
as-api-version response header value | 2024-04 | 2025-07 |
as-webhook-version request header value | 2024-04 | 2025-07 |
What do I need to do?
Make the following updates to your application's code:
- Replace the base URL from https://api.aftership.com/tracking/2024-04 to https://api.aftership.com/tracking/2025-07.
- Select the Webhook version when editing existing URLs or adding new ones. Please review your webhook settings for verification.
- Parse the header to distinguish between different versions. Otherwise, no further action is required.
1.2 Rate Limit Policy
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.
| Endpoint | Rate Limit(request / sec) |
|---|---|
| GET /trackings | 6 |
| POST /trackings | 20 |
| GET /trackings/:slug/:tracking_number | 5 |
| GET /trackings/:id | 5 |
| POST /couriers/detect | 3 |
| POST /estimated-delivery-date/predict-batch | 20 |
| Other endpoints | 5 |
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.
1.3 Enhanced field type verification
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. example: |
A 400 bad request error will be returned if invalid fields in the request body exist. example: |
What do I need to do?
Make the following updates to your application's code:
- Implement error handling mechanisms in your application to capture and handle the 400 Bad Request error.
- Ensure that the types of all fields are consistent with the types described in the document.
2. API Endpoints Deprecation
What's the difference?
| Change | Before | After |
|---|---|---|
| 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 - GET /couriers/all | Endpoints removed |
What do I need to do?
Make the following updates to your application's code:
- 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.
- 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.
- Replace the last_checkpoint endpoints with the GET /tracking/{id} endpoint.
- 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.
- Replace GET /trackings/{slug}/{tracking_number} endpoint with GET /trackings/{id} endpoint. Refer to the documentation on get tracking endpoints for more details.
- Replace PUT /trackings/{slug}/{tracking_number} endpoint with PUT /trackings/{id} endpoint. Refer to the documentation on update tracking endpoints for more details.
- Replace DELETE /trackings/{slug}/{tracking_number} endpoint with DELETE /trackings/{id} endpoint. Refer to the documentation on delete tracking endpoints for more details.
- 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.
- 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.
- Replace GET /couriers/all endpoints with GET /couriers endpoint. Refer to the documentation on GET couriers endpoints for more information.
3. New SDK Added
4. Tracking Model New Fields Added
| New Fields | Description |
|---|---|
| legacy_id | The length of the tracking ID has been updated from a 24-character string to a string ranging from 0 to 128 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_weight | The 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_url | The 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_url | The 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_number | The tracking number of the first-mile carrier. |
| first_mile.slug | The Unique code of the carrier responsible for the first mile of the shipping of the package |
| first_mile.transit_time | The 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_link | The 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_link | The 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_time | The 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.*.role | The role of the customer indicates whether the customer is an individual or a company. |
| customers.*.language | The 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.*.source | The source of the checkpoint, which can either be from the carrier or when the user marks the tracking as completed. |
| latest_estimated_delivery.revise_reason | Explains the reason for a change to the latest_estimated_delivery. This string will only have a value if: 1. The source for the latest EDD is AfterShip EDD. 2. The reason for the change is known. For a comprehensive list of reasons, please refer to this document. |
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.
5. Tracking Model Fields Updated
5.1 Tracking ID format changed
- The length of the tracking ID has been updated from a 24-character string to a string ranging from 0 to 128 characters, following the format
[0-9a-zA-Z-_]{1,128}. - The tracking ID now supports user-provided values. If no value is provided, the system will automatically generate a 32-character UUID.
What's the difference?
| Change | Before | After |
|---|---|---|
| The length of the tracking ID has been updated from a 24-character string to a string ranging from 0 to 128 characters. | 24-character UUID Sample: qumtnhjfylw9umdmr447r02m | A string following the RegExp pattern: [0-9a-zA-Z-_]{1,128} Sample: 123 |
What do I need to do?
- Update the format of the tracking ID to match the new format [0-9a-zA-Z-_]{1,128} if you are using this field. Otherwise, no action is required.
5.2 Slug format change
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?
| Change | Before | After |
|---|---|---|
slug no longer supports comma-separated string format when detecting couriers. | ["ups", "fedex"] or "ups,fedex" | ["ups", "fedex"] |
What do I need to do?
- If your input for
slugis in comma-separated string format, please convert it to an array of strings. Otherwise, no further action is required.
5.3 order_promised_delivery_date new value format
The order_promised_delivery_date now supports time and timezone information.
What's the difference?
| Change | Before | After |
|---|---|---|
| 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?
- 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.
- Specify the
order_promised_delivery_datewith time and timezone in the API request body if you want to input the field. Otherwise, no further action is required.
5.4 expected_delivery renamed and structure changed
The expected_delivery field will be renamed to courier_estimated_delivery_date and will support time range values.
What's the difference?
| Before | After |
|---|---|
|
Example: |
Example:
|
What do I need to do?
- Remove the parsing for the
expected_delivery" field. - Parse the new field
courier_estimated_delivery_datein the API response and the webhook body if you want to retrieve data from the new field. Otherwise, no further action is required.
5.5 tracking.next_couriers renamed and structure changed
The tracking.next_couriers field will be replaced with tracking.last_mile.
| Before | After |
|---|---|
|
|
What do I need to do?
- Add the new field in the API request body if you want to use the field.
- Remove the parsing for the
tracking.next_couriersfield. - Parse the new field
tracking.last_milein the API response and the webhook body if you want to retrieve data from this field. Otherwise, no further action is required.
5.6 tracking.customer_name replaced
The tracking.customer_name field will be replaced with tracking.customers.*.name.
| Before | After |
|---|---|
|
|
What do I need to do?
- Put the new field in the API request body if you want to use the field.
- Remove the parsing for the
tracking.customer_namefield. - Parse the new field
tracking.customers.*.namein the API response and the webhook body if you want to retrieve data from the new field. Otherwise, no further action is required.
5.7 tracking.emails replaced
The tracking.emails field will be replaced with tracking.customers.*.email.
| Before | After |
|---|---|
|
|
What do I need to do?
- Put the new field in the API request body if you want to use the field.
- Remove the parsing for the
tracking.emailsfield. - Parse the new field
tracking.customers.*.emailin the API response and the webhook body if you want to retrieve data from the new field. Otherwise, no further action is required.
5.8 tracking.smses replaced
The tracking.smses field will be replaced with tracking.customers.*.phone_number.
| Before | After |
|---|---|
|
|
What do I need to do?
- Put the new field in the API request body if you want to use the field.
- Remove the parsing for the
tracking.smsesfield. - Parse the new field
tracking.customers.*.phone_numberin the API response and the webhook body if you want to retrieve data from the new field. Otherwise, no further action is required.
5.9 tracking.shipment_weight structure changed
The tracking.shipment_weight_unit and tracking.shipment_weight fields will be replaced with a nested object tracking.shipment_weight containing unit and value.
| Before | After |
|---|---|
|
|
What do I need to do?
- Put the new field in the API request body if you want to use the field.
- Remove the parsing for the
tracking.shipment_weight_unitandtracking.shipment_weightfields. - Parse the new fields
tracking.shipment_weight.unitandtracking.shipment_weight.valuein the API response and the webhook body if you want to retrieve data from these fields. Otherwise, no further action is required.
5.10 tracking.tracking_ship_date format updated
The tracking.tracking_ship_date field date format has been updated and will support more date-time formats.
| Before | After |
|---|---|
|
|
What do I need to do?
- Ensure that valid values are provided for
tracking_ship_datein your API request. Otherwise, the API will return a 400 error. - Parse the field
tracking.tracking_ship_datein the API response and the webhook body using the new formats if you want to retrieve data. Otherwise, no further action is required.
6. Tracking Model Fields Renamed
The following fields have been renamed:
| Before | After |
|---|---|
| tracking.courier_destination_country_iso3 | tracking.courier_destination_country_region |
| tracking.origin_country_iso3 | tracking.origin_country_region |
| tracking.destination_country_iso3 | tracking.destination_country_region |
| tracking.checkpoints.*.country | tracking.checkpoints.*.country_region |
| tracking.checkpoints.*.country_name | tracking.checkpoints.*.country_region_name |
What do I need to do?
- Remove the parsing for the old field.
- 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.
7. Tracking Model Fields Removed
The following fields have been removed and replaced with new ones.
- tracking_origin_country
- tracking_destination_country
- tracking_postal_code
- tracking_state
What do I need to do?
- Replace the parsing for the
tracking_origin_country_regionwithorigin_country_region. - Replace the parsing for the
tracking_destination_country_regionwithdestination_country_region. - Replace the parsing for the
tracking_postal_codewithdestination_postal_code. - Replace the parsing for the
tracking_statewithdestination_state.
8. Tracking Endpoints Common Changes
8.1 New fields added in tracking
Please refer to the Tracking Model New Fields Added
8.2 Fields updated in tracking
Please refer to the Tracking Model Fields Updated and Tracking Model Fields Renamed
8.3 Fields removed in tracking
Please refer to the Tracking Model Fields Removed
8.4 Removed the tracking object wrapper from the request body
| Before | After |
|---|---|
|
|
What do I need to do?
Remove the tracking object wrapper from the request body when you create/update a tracking or detect couriers.
8.5 Removed the tracking object wrapper from the response body
| Before | After |
|---|---|
|
|
What do I need to do?
Remove the tracking object wrapper from the response body when using the tracking endpoints.
9. Tracking Endpoints Specific Changes
9.1 GET /trackings endpoint
9.1.1 Cursor Pagination
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?
| Change | Before | After |
|---|---|---|
| Query parameters updates | Using page parameters to traverse search results. | Use the cursor to get data for the next page. |
| Response body updates | Returns 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:
- Do not fetch query parameters from the response.
- Iterate search results using the cursor instead of relying on page number.
9.1.2 Parameters validation
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?
| Change | Before | After |
|---|---|---|
Add validation for created_at_max | Set the value to the current time when a user inputs an invalid time value | Return a 400 error when a user inputs an invalid time value |
Add validation for created_at_min | Set the value to 120 days ago when a user inputs an invalid time value | Return a 400 error when a user inputs an invalid time value |
Add validation for updated_at_max | Set the value to the current time when a user inputs an invalid time value | Return a 400 error when a user inputs an invalid time value |
Add validation for updated_at_min | Set the value to 120 days ago when a user inputs an invalid time value | Return 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:
- Implement error-handling mechanisms in your application’s code to capture and handle 400 Bad Request errors.
- Ensure that the data types of all fields match the types specified in the document.
9.2 POST /trackings endpoint
9.2.1 New fields added in the request body
Create a tracking endpoint now supports custom tracking id.
What's the difference?
| Change | Before | After |
|---|---|---|
|
The request body of the tracking create endpoint now supports the |
The request body:
|
You can optionally add an
|
What do I need to do?
Add the new id field to the API request body if you want to retrieve the corresponding data. Otherwise, no action is required.
9.3 GET /trackings/:id endpoint
Please refer to the Tracking Endpoints Common Changes.
9.4 PUT /trackings/:id endpoint
9.4.1 Support updating tracking_ship_date for non-pending trackings
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.
9.5 DELETE /trackings/:id endpoint
Please refer to the Tracking Endpoints Common Changes.
9.6 POST /trackings/:id/retrack endpoint
Please refer to the Tracking Endpoints Common Changes.
9.7 POST /trackings/:id/mark-as-complete endpoint
9.7.1 New fields added in the request body
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 |
The request body:
|
The request body:
|
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.
9.7.2 Create a new checkpoint when tracking is marked as completed
A new checkpoint will be created when the user marks the tracking as completed.
| Before | After |
|---|---|
|
|
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.
10. Courier Model New Fields
| Before | After |
|---|---|
| active | Indicates whether the user has activated the courier. |
| credentials | Refers to the authentication details required for each specific carrier (such as API keys, username, password, etc.) that the user must provide to create a carrier connection. The content varies by carrier. |
| credentials.fields | This field allows users to get the specific authentication details required for establishing a connection with a particular carrier, such as API keys, usernames, and passwords. Each field includes properties like name, data type, and whether it is required. |
| credentials.fields.*.name | The display name of the credential field that users must provide when creating a carrier connection. |
| credentials.fields.*.type | The data type of the credential field, indicating the expected input format. |
| credentials.fields.*.required | It indicates whether the credentials field is mandatory when creating a carrier connection. |
What do I need to do?
To fetch any new fields, simply parse them in the API response and the webhook body. Otherwise, no action is required.
11. Courier Model Fields Renamed
The following fields/enum values have been renamed:
| Before | After |
|---|---|
| courier.service_from_country_iso3 | courier.service_from_country_region |
| courier.required_fields enum value - tracking_origin_country | origin_country_region |
| courier.required_fields enum value - tracking_destination_country | destination_country_region |
| courier.required_fields enum value - tracking_postal_code | destination_postal_code |
| courier.required_fields enum value - tracking_state | destination_state |
What do I need to do?
- Remove the parsing for the old field.
- 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. Courier Endpoints Changes
12.1 GET /couriers
12.1.1 Parameters added
What's the difference?
| New Fields | Description |
|---|---|
| active | Whether the user has activated the courier. |
| slug | Unique courier code. Use a comma for multiple values. (Example: dhl, ups, usps) |
What do I need to do?
Include the new field in the API request parameters if you want to use it. Otherwise, no action is required.
12.2 POST /couriers/detect
12.2.1 Fields updated in the request body
What's the difference?
| Change | Before | After |
|---|---|---|
tracking_origin_country in the request body renamed to origin_country_region | tracking_origin_country | origin_country_region |
tracking_destination_country in the request body renamed to destination_country_region | tracking_destination_country | destination_country_region |
tracking_state in the request body renamed to destination_state | tracking_state | destination_state |
tracking_postal_code in the request body renamed to destination_postal_code | tracking_postal_code | destination_postal_code |
What do I need to do?
- Remove the
tracking_origin_country,tracking_destination_country,tracking_stateandtracking_postal_codefields in the request body. - Add the new fields
origin_country_region,destination_country_region,destination_state, anddestination_postal_codein the API request body if you want to retrieve data from these fields. Otherwise, no further action is required.
13. Estimated Delivery Date Endpoint fields Renamed
The following fields have been renamed:
| Before | After |
|---|---|
| estimated_delivery_date.origin_address.country | estimated_delivery_date.origin_address.country_region |
| estimated_delivery_date.destination_address.country | estimated_delivery_date.destination_address.country_region |
What do I need to do?
- Remove the parsing for the old field.
- Parse the new field in the API response if you want to retrieve data from the new field. Otherwise, no further action is required.
14. Estimated Delivery Date Endpoints Changes
14.1 POST /estimated-delivery-date/predict-batch
14.1.1 New field id for EDD endpoint
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.
15. Webhook Changes
15.1 New fields added in tracking
Please refer to the Tracking model new fields added
15.2 Fields updated in tracking
Please refer to the Tracking model fields updated
15.3 Fields removed in tracking
Please refer to the Tracking Model Fields Removed
16. What if I don’t migrate?
After Oct 9, 2025, any requests made to the 2024-04 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.