Migration from 2024-10 API to 2025-01
This migration guide outlines the essential changes required to implement new functionalities when transitioning from the API and webhook version 2024-10 to 2025-01.
API Version Changes
What's the difference?
| Change | Before | After |
|---|---|---|
| Base URL | https://api.aftership.com/tracking/2024-10 | https://api.aftership.com/tracking/2025-01 |
as-api-version header’s value | 2024-10 | 2025-01 |
as-webhook-version header’s value | 2024-10 | 2025-01 |
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-10tohttps://api.aftership.com/tracking/2025-01. - 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.
API Endpoints Changes
Searching for tracking 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 behavior.
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 a 400 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.
New endpoint for estimated delivery date prediction
A new prediction endpoint has been added to get the estimated delivery date.
What do I need to do?
- If you want to learn more about this endpoint, please refer to the detailed documentation. Otherwise, no further action is required.
Business logic changes
Overview
| Update | Affected Endpoints |
|---|---|
Tracking modal new fields:first_mile.tracking_numberfirst_mile.slugfirst_mile.transit_timefirst_mile.courier_tracking_linkfirst_mile.courier_redirect_linklast_mile.transit_timecustomers[x].rolecustomers[x].languagecheckpoints[x].source | API endpoint: - GET /trackings - GET /trackings/:id - DELETE /trackings/:id - POST /trackings - PUT /trackings/:id - POST /trackings/:id/mark-as-completed - POST /trackings/:id/retrack Tracking Webhook |
- The existing fields tracking.next_couriers will be replaced with tracking.last_mile- The existing field tracking.customer_name will be replaced with tracking.customers[x].name- The existing field tracking.smses will be replaced with tracking.customers[x].phone_number- The existing field tracking.emails will be replaced with tracking.customers[x].email | API endpoint: - GET /trackings - GET /trackings/:id - DELETE /trackings/:id - POST /trackings - PUT /trackings/:id - POST /trackings/:id/mark-as-completed - POST /trackings/:id/retrack Tracking Webhook |
The existing field tracking.last_updated_at will be deprecated. | API endpoint: - GET /trackings - GET /trackings/:id - DELETE /trackings/:id - POST /trackings - PUT /trackings/:id - POST /trackings/:id/mark-as-completed - POST /trackings/:id/retrack Tracking Webhook |
tracking.tracking_ship_date supports new value formats:- YYYY-MM-DD - YYYY-MM-DDTHH:mm:ss - YYYY-MM-DDTHH:mm:ssZ | API endpoint: - GET /trackings - GET /trackings/:id - DELETE /trackings/:id - POST /trackings - PUT /trackings/:id - POST /trackings/:id/mark-as-completed - POST /trackings/:id/retrack Tracking Webhook |
| A new checkpoint will be created when the user marks a tracking as completed. | API endpoint: - GET /trackings - GET /trackings/:id - DELETE /trackings/:id - POST /trackings - PUT /trackings/:id - POST /trackings/:id/mark-as-completed - POST /trackings/:id/retrack Tracking Webhook |
- Rename tracking.courier_destination_country_iso3 to tracking.courier_destination_country_region- Rename tracking.origin_country_iso3 to tracking.origin_country_region- Rename tracking.destination_country_iso3 to tracking.destination_country_region- Rename tracking.checkpoints[x].country_iso3 to tracking.checkpoints[x].country_region- Rename tracking.checkpoints[x].country_name to tracking.checkpoints[x].country_region_name- Rename tracking.tracking_origin_country to tracking.tracking_origin_country_region- Rename tracking.tracking_destination_country to tracking.tracking_destination_country_region- Rename courier.service_from_country_iso3 to courier.service_from_country_regions- Rename estimated_delivery_date.origin_address.country to estimated_delivery_date.origin_address.country_region- Rename estimated_delivery_date.destination_address.country to estimated_delivery_date.destination_address.country_region | API endpoint: - GET /trackings - GET /trackings/:id - DELETE /trackings/:id - POST /trackings - PUT /trackings/:id - POST /trackings/:id/mark-as-completed - POST /trackings/:id/retrack - GET /couriers - GET /couriers/all - POST /couriers/detect - POST /estimated-delivery-date/predict-batch - POST /estimated-delivery-date/predict Tracking Webhook |
Tracking marked as completed endpoint supportsevent_datetime field | API endpoint: - POST /trackings/:id/mark-as-completed |
Key Updates - New fields
first_mile.tracking_number
first_mile.tracking_numberThe tracking number of the first-mile carrier.
What do I need to do?
Parse the new field in the API response and the webhook body if you want to fetch data from it. Otherwise, no further action is required.
first_mile.slug
first_mile.slugThe Unique code of the carrier responsible for the first mile of the shipping of the package.
What do I need to do?
Parse the new field in the API response and the webhook body if you want to fetch data from it. Otherwise, no further action is required.
first_mile.transit_time
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.
What do I need to do?
Parse the new field in the API response and the webhook body if you want to fetch data from it. Otherwise, no further action is required.
first_mile.courier_tracking_link
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".
What do I need to do?
Parse the new field in the API response and the webhook body if you want to fetch data from it. Otherwise, no further action is required.
first_mile.courier_redirect_link
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".
What do I need to do?
Parse the new field in the API response and the webhook body if you want to fetch data from it. Otherwise, no further action is required.
last_mile.transit_time
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.
What do I need to do?
Parse the new field in the API response and the webhook body if you want to fetch data from it. Otherwise, no further action is required.
customers[x].role
customers[x].roleThe role of the customer, indicating whether the customer is an individual or a company.
What do I need to do?
Parse the new field in the API response and the webhook body if you want to fetch data from it. Otherwise, no further action is required.
customers[x].language
customers[x].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.
What do I need to do?
Parse the new field in the API response and the webhook body if you want to fetch data from it. Otherwise, no further action is required.
checkpoints[x].source
checkpoints[x].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?
Parse the new field in the API response and the webhook body if you want to fetch data from it. Otherwise, no further action is required.
Key Updates - Updated fields
tracking.next_couriers rename
tracking.next_couriers renameThe tracking.next_couriers field will be replaced with tracking.last_mile.
What's the difference?
| Change | Before | After |
|---|---|---|
| The field name has been changed and contains the last mile transit time in days. | {"meta":{"code":201},"data":{"tracking":{"id":xxxx,"created_at":xxxx,"updated_at":xxxx,"next_couriers":[{"slug":xxxx,"tracking_number":xxxx,"source":xxxx,"tracking_courier_link":xxxx,"tracking_redirect_link":xxxx,}],…}}} | {"meta":{"code":201},"data":{"tracking":{"id":xxxx,"created_at":xxxx,"updated_at":xxxx,"last_mile":{"slug":xxxx,"tracking_number":xxxx,"source":xxxx,"transit_time":xxxx,"tracking_courier_link":xxxx,"tracking_redirect_link":xxxx},…}}} |
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.
tracking.customer_name replaced
tracking.customer_name replacedThe tracking.customer_name field will be replaced with tracking.customers[x].name customers.
What's the difference?
| Change | Before | After |
|---|---|---|
| The field type has been changed to object. | {"meta": {"code": 201},"data": {"tracking": {"id": xxxx,"created_at": xxxx,"updated_at": xxxx,"customer_name": xxxx,…}}} | {"meta":{"code":201},"data":{"tracking":{"id":xxxx,"created_at":xxxx,"updated_at":xxxx,"customers":[{"role":xxxx,"name":xxxx,"email":xxxx,...},{"role":null,"name":xxxx,"email":xxxx,...}],...}}} |
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[x].namein the API response and the webhook body if you want to retrieve data from the new field. Otherwise, no further action is required.
tracking.emails replaced
tracking.emails replacedThe tracking.emails field will be replaced with tracking.customers[x].email customers.
What's the difference?
| Change | Before | After |
|---|---|---|
| The field type has been changed from an array to an object. | {"meta":{"code":201},"data":{"tracking":{"id":xxxx,"created_at":xxxx,"updated_at":xxxx,"emails":[xxxx,xxxx],…}}} | {"meta":{"code":201},"data":{"tracking":{"id":xxxx,"created_at":xxxx,"updated_at":xxxx,"customers":[{"role":xxxx,"name":xxxx,"email":xxxx,…},{"role":xxxx,"name":xxxx,"email":xxxx,…}],…}}} |
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[x].emailin the API response and the webhook body if you want to retrieve data from the new field. Otherwise, no further action is required.
tracking.smses replaced
tracking.smses replacedThe tracking.smses field will be replaced with tracking.customers[x].phone_number customers.
What's the difference?
| Change | Before | After |
|---|---|---|
| The field type has been changed from an array to an object. | {"meta": {"code": 201},"data": {"tracking": {"id": xxxx,"created_at": xxxx,"updated_at": xxxx,"smses": [xxxx, xxxx],…}}} | {"meta":{"code":201},"data":{"tracking":{"id":xxxx,"created_at":xxxx,"updated_at":xxxx,"customers":[{"role":xxxx,"name":xxxx,"phone_number":xxxx,…},{"role":xxxx,"name":xxxx,"phone_number":xxxx,…}],…}}} |
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[x].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.
tracking.tracking_ship_date format updated
tracking.tracking_ship_date format updatedThe tracking.tracking_ship_date field date format has been updated and will support more date-time formats.
What's the difference?
| Change | Before | After |
|---|---|---|
| Removed the YYYYMMDD format, and introduced support for the new formats: - YYYY-MM-DD - YYYY-MM-DDTHH:mm:ss - YYYY-MM-DDTHH:mm:ssZ | {"meta": {"code": 201},"data": {"tracking": {"id": xxxx,"created_at": xxxx,"updated_at": xxxx,"tracking_ship_date": "20250102",…}}} | {"meta": {"code": 201},"data": {"tracking": {"id": xxxx,"created_at": xxxx,"updated_at": xxxx,"tracking_ship_date": "2025-01-02T10:00:00+01:00",…}}} |
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.
Create new checkpoint when tracking is marked as completed
A new checkpoint will be created when the users marks the tracking as completed.
What's the difference?
| Change | Before | After |
|---|---|---|
| A new checkpoint will be created when the user marks the tracking as completed. | {"meta": {"code": 201},"data": {"tracking": {"id": xxxx,"created_at": xxxx,"updated_at": xxxx,"checkpoints": [],…}}} | {"meta": {"code": 201},"data": {"tracking": {"id": xxxx,"created_at": xxxx,"updated_at": xxxx,"checkpoints": [{"checkpoint_time": "xxxx","source": "user","subtag": "Delivered_001", "subtag_message": "Delivered", "tag": "Delivered", …}],…}}} |
What do I need to do?
- Parse the new checkpoint in the API response and the webhook body if you want to retrieve data. Otherwise, no further action is required.
tracking.courier_destination_country_iso3 renamed
tracking.courier_destination_country_iso3 renamedThe tracking.courier_destination_country_iso3 field will be renamed to tracking.courier_destination_country_region.
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.courier_destination_country_iso3field. - Parse the new field
tracking.courier_destination_country_regionin the API response and the webhook body if you want to retrieve data from the new field. Otherwise, no further action is required.
tracking.origin_country_iso3 renamed
tracking.origin_country_iso3 renamedThe tracking.origin_country_iso3 field will be renamed to tracking.origin_country_region.
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.origin_country_iso3field. - Parse the new field
tracking.origin_country_regionin the API response and the webhook body if you want to retrieve data from the new field. Otherwise, no further action is required.
tracking.destination_country_iso3 renamed
tracking.destination_country_iso3 renamedThe tracking.destination_country_iso3 field will be renamed to tracking.destination_country_region.
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.destination_country_iso3field. - Parse the new field
tracking.destination_country_regionin the API response and the webhook body if you want to retrieve data from the new field. Otherwise, no further action is required.
tracking.tracking_origin_country renamed
tracking.tracking_origin_country renamedThe tracking.tracking_origin_country field will be renamed to tracking.tracking_origin_country_region.
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.tracking_origin_countryfield. - Parse the new field
tracking.tracking_origin_country_regionin the API response and the webhook body if you want to retrieve data from the new field. Otherwise, no further action is required.
tracking.tracking_destination_country renamed
tracking.tracking_destination_country renamedThe tracking.tracking_destination_country field will be renamed to tracking.tracking_destination_country_region.
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.tracking_destination_countryfield. - Parse the new field
tracking.tracking_destination_country_regionin the API response and the webhook body if you want to retrieve data from the new field. Otherwise, no further action is required.
tracking.checkpoints[x].country renamed
tracking.checkpoints[x].country renamedThe tracking.checkpoints[x].country field will be renamed to tracking.checkpoints[x].country_region.
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.checkpoints[x].countryfield. - Parse the new field
tracking.checkpoints[x].country_regionin the API response and the webhook body if you want to retrieve data from the new field. Otherwise, no further action is required.
tracking.checkpoints[x].country_name renamed
tracking.checkpoints[x].country_name renamedThe tracking.checkpoints[x].country_name field will be renamed to tracking.checkpoints[x].country_region_name.
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.checkpoints[x].country_namefield. - Parse the new field
tracking.checkpoints[x].country_region_namein the API response and the webhook body if you want to retrieve data from the new field. Otherwise, no further action is required.
courier.service_from_country_iso3 renamed
courier.service_from_country_iso3 renamedThe courier.service_from_country_iso3 field will be renamed to courier.service_from_country_regions.
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
courier.service_from_country_iso3field. - Parse the new field
courier.service_from_country_regionsin the API response and the webhook body if you want to retrieve data from the new field. Otherwise, no further action is required.
estimated_delivery_date.origin_address.country renamed
estimated_delivery_date.origin_address.country renamedThe estimated_delivery_date.origin_address.country field will be renamed to estimated_delivery_date.origin_address.country_region.
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
estimated_delivery_date.origin_address.countryfield. - Parse the new field
estimated_delivery_date.origin_address.country_regionin the API response and the webhook body if you want to retrieve data from the new field. Otherwise, no further action is required.
estimated_delivery_date.destination_address.country renamed
estimated_delivery_date.destination_address.country renamedThe estimated_delivery_date.destination_address.country field will be renamed to estimated_delivery_date.destination_address.country_region.
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
estimated_delivery_date.destination_address.countryfield. - Parse the new field
estimated_delivery_date.destination_address.country_regionin the API response and the webhook body if you want to retrieve data from the new field. Otherwise, no further action is required.
Additional courier fields enums renamed
The tracking_destination_country enum and the tracking_origin_country enum will be renamed to tracking_destination_country_region and tracking_origin_country_region.
What's the difference?
| Change | Before | After |
|---|---|---|
courier.required_fields enums renamed | The enums of the courier.required_fields: - tracking_account_number - tracking_postal_code - tracking_ship_date - tracking_key - tracking_origin_country - tracking_destionation_country - tracking_state | The enums of the courier.required_fields: - tracking_account_number - tracking_postal_code - tracking_ship_date - tracking_key - tracking_origin_country_region - tracking_destionation_country_region - tracking_state |
What do I need to do?
- Parse the new enums
tracking_destination_country_regionandtracking_origin_country_regionin the API response if you want to retrieve data. Otherwise, no further action is required.
Detect courier endpoint request body updated
What's the difference?
| Change | Before | After |
|---|---|---|
tracking_origin_country in the request body renamed to tracking_origin_country_region | tracking_origin_country | tracking_origin_country_region |
tracking_destination_country in the request body renamed to tracking_destination_country_region | tracking_destination_country | tracking_destination_country_region |
origin_country_iso3 in the request body renamed to origin_country_region | origin_country_iso3 | origin_country_region |
destination_country_iso3 in the request body renamed to destination_country_region | destination_country_iso3 | destination_country_region |
What do I need to do?
- Remove the
tracking_origin_country,tracking_destination_country,origin_country_iso3, anddestination_country_iso3fields in the request body. - Aadd the new fields
tracking_origin_country_region,tracking_destination_country_region,origin_country_region, anddestination_country_regionin the API request body if you want to retrieveinput these data from these fields. Otherwise, no further action is required.
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 event_datetime field | The request body: { "reason": "DELIVERED" } | The request body: { "reason": "DELIVERED", "event_datetime": "2024-12-10T01:22:19+01:00" } |
What do I need to do?
- Add the new field
event_datetimeto the API request body if you want to retrieve corresponding data. Otherwise, no further action is required.
What if I don’t migrate?
If you choose not to migrate, the 2024-10 API will continue to be supported for at least 18 months. However, API traffic will eventually be redirected to the 2025-01 version following the deprecation of the versioned API. For further details on API versioning, please refer to our documentation.