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 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. 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.

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.