UPS and FedEx Tracking Webhooks (Track Alert API)
A tracking webhook lets the carrier call your server whenever a package
has a new scan, so you stop polling the tracking API on a timer. UPS and FedEx
both offer one, UPS as the Track Alert API and FedEx through its Advanced
Integrated Visibility webhooks, and RocketShipIt subscribes tracking numbers to
either with the same action: trackalert.
The carrier sends the events straight to your URL. RocketShipIt handles the subscription request, and the event notifications do not pass through it, so the endpoint you register has to be reachable from the public internet.
| carrier | how the webhook is set up | what trackalert does |
|---|---|---|
UPS (UPS-REST) |
Entirely by API | Registers your URL and credential and subscribes one or more tracking numbers to it, in one request |
FedEx (FedEx-REST) |
The subscription (your URL and its security settings) is created on the FedEx side first, and you get back a subscription ID | Attaches a tracking number to that existing subscription |
No other carrier has a push API RocketShipIt supports. For those, poll with
Track. For UPS accounts on Quantum View, you can also pull
recent events in bulk.
UPS Track Alert#
One request carries the destination and the tracking numbers:
{
"carrier": "UPS-REST",
"action": "trackalert",
"params": {
"key": "your-key-from-authenticate-request",
"url": "https://yourdomain.com/webhook/incoming",
"credential_type": "Bearer",
"credential": "something-secure",
"tracking_number": "1ZCIETST0111111114"
}
}
To subscribe several packages at once, send tracking_numbers as a list. When
tracking_numbers is present it replaces tracking_number, so don't send both
expecting them to be combined:
{
"carrier": "UPS-REST",
"action": "trackalert",
"params": {
"key": "your-key-from-authenticate-request",
"url": "https://yourdomain.com/webhook/incoming",
"credential_type": "Bearer",
"credential": "something-secure",
"tracking_numbers": ["1ZCIETST0111111114", "1ZCIETST0422222228"]
}
}
| parameter | UPS field | notes |
|---|---|---|
url |
destination.url |
The callback UPS will POST events to. |
credential_type |
destination.credentialType |
Free text naming the kind of credential, e.g. Bearer. |
credential |
destination.credential |
UPS describes this as "an opaque string meant for client authentication". Treat it as a shared secret, and check it on your endpoint before you trust a notification. |
tracking_number / tracking_numbers |
trackingNumberList |
One number, or a list. |
test |
true sends the request to the UPS test (CIE) environment. |
RocketShipIt calls POST /api/track/v1/subscription/standard/package on the
UPS REST API, with locale en_US and country US.
UPS Track Alert limits#
These come from UPS's Track Alert API specification. They are easy to miss, and all of them cause missing updates rather than a loud failure:
- A subscription lasts 14 days. If a package hasn't been delivered 14 days
after you subscribed, events stop arriving. Re-send
trackalertfor that number to keep receiving them. Slow ground, returns and international packages are the ones that hit this. - Events start from the moment you subscribe. Scans that happened before
it are not replayed. Subscribe right after creating the label, and use
Trackto fill in anything earlier. - 1Z numbers only, at most 100 per request. Each number must match
1Zfollowed by 16 letters or digits. If you send more than 100, UPS rejects the whole request (errorVSS220), so send them in batches of 100. Mail Innovations and freight numbers can't be subscribed. - The test environment creates no subscription. With
test: true, UPS validates the request against its CIE environment and needs CIE test numbers such as1ZCIETST0111111114. Real numbers are rejected there (VSS215), and nothing is ever delivered to your URL. To see a real event arrive, you have to subscribe a real package in production. - Every destination field is required. An empty
url,credentialorcredential_typeis rejected (VSS600,VSS700,VSS800), as is a missing tracking number (VSS200).
The notifications UPS sends carry an activity type along with the status
code: I in transit, X exception, D delivered, U delivery update, and
M/MV manifest. The same code can appear under more than one type, for
example when a package is delivered to a UPS Access Point or returned to the
sender.
UPS's own field documentation also warns about the endpoint and the credential:
- Your endpoint must be up around the clock. UPS says that if the URL is
not continuously available, incoming events are lost. They are not queued
for you to collect later, so if you can't guarantee uptime, keep polling
Trackas a backstop. - Changing the credential breaks the subscription. If the credential
changes, notifications fail until you subscribe the tracking numbers again
with the new one. Rotate it by re-sending
trackalertfor every live tracking number.
FedEx tracking webhooks#
On FedEx the subscription itself is created on the FedEx side, in the FedEx
Developer Portal. That is where you set the destination URL and its security
settings, and you get a subscription ID back. trackalert then adds
tracking numbers to that subscription:
{
"carrier": "FedEx-REST",
"action": "trackalert",
"params": {
"key": "your-key-from-authenticate-request",
"subscription_id": "123",
"tracking_number": "128667043726",
"carrier_code": "FDXE",
"test": true
}
}
| parameter | FedEx field | notes |
|---|---|---|
subscription_id |
subscriptionId |
Required. The webhook subscription from your FedEx project. |
tracking_number |
trackingNumber |
One number per request. |
carrier_code |
carrierCode |
Optional. FDXE Express, FDXG Ground. Helps FedEx when a number is ambiguous. |
unique_id |
trackingNumberUniqueId |
Optional. FedEx's unique ID for a tracking number that has been reused. |
RocketShipIt calls the FedEx Advanced Integrated Visibility tracking-number
association endpoint
(POST /webhook/v1/subscriptions/trackingnumbers/association) with the
add action. Removing a number from a subscription is not exposed through
trackalert.
Response#
Both carriers return the same thing: a list of errors, empty on success.
{
"data": {
"errors": []
}
}
The subscribe call returns no tracking events. Those arrive later at your URL,
in the carrier's own format. UPS can accept part of a list: its reply includes
validTrackingNumbers and invalidTrackingNumbers, so an empty errors list
does not mean every number was subscribed. Like every RocketShipIt response,
the raw carrier reply is in transactions[].response, so check the
invalidTrackingNumbers list there.
Webhooks or polling?#
Use a webhook when you track many packages and care about latency. For
example, you might want to notify a customer when a package goes out for
delivery, without making thousands of Track calls an hour. Polling is simpler
when your server is not publicly reachable, when you ship with carriers other
than UPS and FedEx, or when you need the full scan history in RocketShipIt's
normalised Track response. Many integrations
use both: the webhook as the trigger, and a Track call to fetch the details
in the same shape for every carrier.