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) v1 (default): entirely by API. v2: the URL and credential are set once in the UPS developer portal v1: registers your URL and credential and subscribes one or more tracking numbers to it, in one request. v2: subscribes the tracking numbers to the webhook set in the portal
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#

UPS has two versions of the Track Alert API. RocketShipIt uses v1 unless you send track_alert_version, so existing requests keep working unchanged.

  • v1 (default): each request carries the destination URL and credential.
  • v2: UPS removed the destination from the request. You set the webhook URL and credential once, under Manage Webhook Credentials in the UPS developer portal, and requests carry only tracking numbers. UPS recommends v2 for new integrations and still supports v1.

v1#

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.
track_alert_version URL path v1 (default) or v2. See v2.
track_alert_type URL path standard (default) or enhanced. See Enhanced subscriptions.
event_preferences eventPreferences Enhanced only. Up to four of D, I, M, X.
test true sends the request to the UPS test (CIE) environment.

RocketShipIt calls POST /api/track/{version}/subscription/{type}/package on the UPS REST API, with locale en_US and country US. With no track_alert_version or track_alert_type, that is /api/track/v1/subscription/standard/package.

v2#

Set up the webhook URL and credential in the UPS developer portal first, under Manage Webhook Credentials. Then send track_alert_version: "v2" with the tracking numbers:

{
  "carrier": "UPS-REST",
  "action": "trackalert",
  "params": {
    "key": "your-key-from-authenticate-request",
    "track_alert_version": "v2",
    "tracking_numbers": ["1ZCIETST0111111114", "1ZCIETST0422222228"]
  }
}

With v2, RocketShipIt does not send url, credential or credential_type, because UPS no longer accepts a destination in the request. If you send them anyway they are ignored. To move from v1 to v2, set up the same URL and credential in the portal, then add track_alert_version: "v2".

Enhanced subscriptions#

An enhanced subscription adds more detail to each notification, such as the delivery photo and the signature. UPS makes it available only to accounts that are set up for it. It works with v1 and v2.

Send event_preferences to choose which activity types UPS sends. Sending it selects enhanced on its own:

{
  "carrier": "UPS-REST",
  "action": "trackalert",
  "params": {
    "key": "your-key-from-authenticate-request",
    "track_alert_version": "v2",
    "event_preferences": ["D", "X"],
    "tracking_number": "1ZCIETST0111111114"
  }
}
code activity
D Delivery, with the delivery photo
I In progress
M Manifest
X Exception

To get every activity type on an enhanced subscription, send track_alert_type: "enhanced" with no event_preferences. Sending event_preferences with track_alert_type: "standard" is an error.

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 trackalert for 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 Track to fill in anything earlier.
  • 1Z numbers only, at most 100 per request. Each number must match 1Z followed by 16 letters or digits. If you send more than 100, UPS rejects the whole request (error VSS220), 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 as 1ZCIETST0111111114. 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, credential or credential_type is 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 Track as a backstop.
  • Changing the credential breaks the subscription (v1). If the credential changes, notifications fail until you subscribe the tracking numbers again with the new one. Rotate it by re-sending trackalert for every live tracking number. With v2 the credential lives in the UPS developer portal.

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 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, so an empty errors list does not mean every number was subscribed. For UPS, RocketShipIt copies UPS's lists into valid_tracking_numbers and invalid_tracking_numbers. Check invalid_tracking_numbers for numbers that were not subscribed:

{
  "data": {
    "errors": [],
    "valid_tracking_numbers": ["1ZCIETST0111111114"],
    "invalid_tracking_numbers": ["1Z1234567$8"]
  }
}

Each list is left out when UPS does not return it. FedEx does not return these lists.

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.