# About webhooks for tracking with Shipium

Webhooks are a convenient way for your organization to be notified automatically of tracking event updates, eliminating the need to repeatedly call the [Shipment Tracking API](https://docs.shipium.com/docs/shipment-tracking-api) to check a tracking event status. Once you register a webhook for your organization, the next tracking update for your event will send notifications to the API endpoint for trackings matching your selected data fields (e.g., `eventType`). Shipium returns the full event history for each tracking number in the webhook call.

This document guides you through the steps to register your organization's webhook. To start, you'll need to access the [Shipium Console](https://partner.shipium.com/login).

## One webhook registered per application to be notified with tracking event updates

Your organization can use a Shipium webhook to receive tracking event updates to one API endpoint (or _application_), and then manage those updates via your internal processing system or pass them to your internal customers.

For example, if you have customers A, B, and C that need to receive tracking event updates automatically, you would register one Shipium webhook for Customer A, one for Customer B, and one for Customer C, with three unique API endpoints.

Shipium suggests creating one webhook per desired application for which your organization needs to receive tracking event updates.

Once you've registered your organization's webhook, you can disable, delete, and enable it via the [Shipium Console](https://partner.shipium.com/login). You also can view a summary of all your registered webhooks within the console.

# Steps to register your webhook

Once in the [Shipium Console](https://partner.shipium.com/login), you'll follow these instructions to register your organization's webhook:

1. In the navigation pane at left, navigate to **Tracking**, and then **Webhooks**.

2. Select the **Add Webhook** button to the far right of your screen, located near your organization's user account profile information. Any existing webhooks your organization has previously registered will appear on the **Webhooks** screen. If your organization has no existing registered webhooks, this screen will contain no webhooks until you've registered your first one.

3. Complete the **Settings** fields shown in the **Add Webhook** screen.

1. **Name.** Provide a descriptive name for your webhook. We suggest using a simple naming convention that uses the underscore symbol to separate each element of your webhook's intended use, such as `{organization}_{action}_{app}_{version}`.
      1. Use a descriptive name so it's easy to determine the nature of your webhook (e.g., `OrganizationName_eastCoastTrackingBroker_app1_v1` vs. `service_123`).
      2. If this webhook is being used as a test or is meant to be short-lived, indicate such in the name (e.g., `OrganizationName_eastCoastTrackingBroker_app1_v1_**Test**`).
      3. Avoid using abbreviations in your name if possible, for greater clarity (e.g., `OrganizationName_eastCoastTrackingBroker_app1_v1` vs. `OrganizationName_ectBroker_app1_v1`).
   2. **Payload URL.** Create a web app with a Uniform Resource Locator (URL) to use as your webhook to receive tracking event notifications. This is the endpoint deployed on your server to receive incoming webhook tracking events your organization has selected. Enter your Hypertext Transfer Protocol Secure (HTTPS) URL endpoint into the **Payload URL** field.
   3. **Status.** Shipium automatically sets new webhooks to inactive by default. This allows your organization to test your webhook before activating it, or create a webhook that you may not be ready to activate yet.

4. **Tenants.** This field will only appear if your organization uses Shipium's **Tenant** feature.
      1. If your organization doesn't use **Tenants**, you can disregard this step.
      2. If you use **Tenants**, you may want to receive tracking updates for all tenants, only one tenant, or a subset of tenants. This field allows you to determine how to set up automatic tracking updates by tenant.
         - In the first screenshot that follows, **All** has been selected from the dropdown menu, meaning no further action is required to receive tracking updates for all your organization's tenants.

- In the second screenshot that follows, individual tenants have been selected from the dropdown menu. The list continues to populate horizontally across the data field as you add desired tenants, whether just one or multiple.

4. Optionally, add custom HTTP headers to your webhook. In the **Headers** section, select **Add Additional Header** and provide a key and value for each header you want to include. Headers are sent with every webhook request Shipium delivers to your endpoint.

Custom headers are commonly used to add an `Authorization` header for HTTP Basic Authentication. For step-by-step instructions on generating and configuring custom headers, see [Configure Custom HTTP Headers for Webhooks](https://docs.shipium.com/docs/configure-custom-http-headers-for-webhooks).
5. Select the tracking events you'd like to receive automatic tracking event update notifications for. If you choose to customize your selections rather than opting to **Send me all event types**, use the [Tracking Event Types for Your Webhooks](https://docs.shipium.com/docs/webhooks-for-tracking-updates#tracking-event-types-for-your-webhook) section below to help determine your selections.

6. Finally, send a test payload to your endpoint. Refer to the [Final Steps and Sample Webhook Call Content](https://docs.shipium.com/docs/webhooks-for-tracking-updates#final-steps-and-sample-webhook-call-content) section below for confirmation information.

Net new tracking events will push to your designated endpoint as **received** once you've completed registration.

# Tracking event types for your webhook

The following table maps example carrier delivery steps and messages to the designated tracking event type rollup categories for webhooks.

| Tracking event type for webhook | Example carrier delivery step                      | Message                                                                     |
| ------------------------------- | -------------------------------------------------- | --------------------------------------------------------------------------- |
| Label Printed                   | Info Received                                     | Information about this package has been received by the carrier.          |
| Registered                       | Not Applicable                                    | The package has been registered for tracking.                              |
| In Transit                      | Picked Up                                         | The package has been picked up by the carrier.                             |
| In Transit                      | Arrived Carrier Facility                          | The package has arrived at a carrier facility.                             |
| In Transit                      | Departed Carrier Facility                          | The package has left the carrier facility.                                 |
| In Transit                      | In Transit                                        | The package is in transit to the customer.                                 |
| Out For Delivery                | Out For Delivery                                  | The package is out for delivery.                                          |
| Delivered                       | Delivered                                         | The package has been delivered.                                            |
| Exception                       | Hold                                             | The package is on hold and in possession of the carrier.                   |
| Exception                       | Exception                                         | There has been a carrier exception for this package.                       |
| Return to Sender: In Transit     | Picked Up                                         | The package is being returned and has been picked up by the carrier.       |
| Return to Sender: In Transit     | Arrived Carrier Facility                          | The package is being returned and has arrived at a carrier facility.     |
| Return to Sender: In Transit     | Departed Carrier Facility                          | The package is being returned and has left a carrier facility.             |
| Return to Sender: In Transit     | In Transit                                        | The package is being returned and is in transit to the returns facility.   |
| Return to Sender: Exception       | Hold                                             | The package is being returned, and the package is on hold and in possession of the carrier. |
| Return to Sender: Exception       | Exception                                         | The package is being returned, and there has been a carrier exception for this package. |
| Return to Sender: Out For Delivery | Out For Delivery                                  | The package is being returned and is out for delivery.                    |
| Return to Sender: Delivered      | Delivered                                         | The package is being returned and has been delivered.                      |

# Final steps and sample webhook call content

This section provides instructions for responding successfully to your webhook's initial call as well as sample webhook messages as they would appear as incoming tracking event updates.

## How to respond to your first webhook call

Once you've successfully registered your organization's webhook, you will start receiving webhook calls. Upon receipt of your webhook call, you'll need to respond with an appropriate Hypertext Transfer Protocol (HTTP) status code to acknowledge successful receipt of your webhook call. Your response must:

- be an HTTP status code within the 200-299 range; and
- be delivered within 3 seconds of receiving the webhook call.

## Shipium's automated response to your newly registered webhook

Once you've registered your organization's webhook, Shipium:

- will retry the webhook three times if a 200-level HTTP code response is not received within 3 seconds, as required;
- will log this as an error; and
- may decide to mark your API endpoint as broken if multiple errors are received.

## Webhook retry logic

If a webhook push fails to deliver to the target location, the Shipium platform employs automated retry logic. This is not the same as guaranteed delivery of webhooks. If your organization’s system or webhook endpoint is down for an extended period, retries may not deliver the payload. Our webhooks have the below retry/backoff policy for new webhook setups. We will configure separate policies if there are recurring performance issues with a managed endpoint we are publishing to.

**Backoff Policy for Webhooks**

- Maximum Delivery Attempt Count = 4
- Backoffs per Attempt:
  - Attempt #1 = No backoff
  - Attempt #2 Backoff = 1 hour
  - Attempt #3 Backoff = 4 hours
  - Attempt #4 Backoff = 16 hours
- Maximum Backoff Time/Expires After = 24 hours

## Best practices for processing webhooks

When building an application to consume Shipium's tracking webhooks, following these best practices ensures your integration is robust and reliable.

### Handle events asynchronously

To avoid timeouts, we recommend that your webhook endpoint immediately acknowledges receipt of an event by returning a 2xx HTTP status code. Then, process the event payload in a background job. This ensures that you have time to execute your business logic without holding up the connection, which can lead to unnecessary retries from our system.

### Event ordering is not guaranteed

Due to the nature of distributed systems, network conditions, and our retry logic for failed deliveries, Shipium does not guarantee that webhook events will arrive in the order they were generated.

For example, webhook timeouts or errors when calling your endpoint can cause our system to retry earlier events. This means it is possible to receive a `tracking_updated` event with a `shipmentStatus` of "Delivered" before you receive an event for the same shipment with a status of "Out For Delivery".

To handle this, you should always rely on the `eventTimestamp` field within the metadata of each event object as the single source of truth for the chronological order of events.

Before processing an event, compare its `eventTimestamp` to the timestamp of the last event you processed for that `shipiumTrackingId`. This will ensure that you are not overwriting newer data with older data that has been delivered out of order.

**Example pseudocode:**

```text
function process_webhook(event) {
  // Retrieve the last processed event timestamp for this shipment
  last_timestamp = get_last_timestamp_for_shipment(event.payload.trackings[0].shipiumTrackingId);

// Compare timestamps to ensure the new event is not older
  if (event.metadata.eventTimestamp > last_timestamp) {
    // Process the event
    update_shipment_status(event.payload.trackings[0]);

// Store the timestamp of the processed event
    store_last_timestamp_for_shipment(event.payload.trackings[0].shipiumTrackingId, event.metadata.eventTimestamp);
  } else {
    // Ignore the event as it is older than the data we already have
    log("Ignoring out-of-order event for shipment: " + event.payload.trackings[0].shipiumTrackingId);
  }
}
```  
### Handle duplicate events

Our retry mechanism can sometimes result in the same event being sent to your endpoint more than once. To prevent duplicate processing, your webhook consumer should be idempotent.

A simple way to achieve this is to log the `eventId` from the event's metadata and check this log before processing a new event. If the `eventId` has already been processed, you can safely discard the duplicate.

## Sample webhook messages

This section provides sample expected webhook messages as they would appear when delivering tracking event updates, for both the standard model and the Tracking Updated event type.

### Sample webhook message for standard model

This sample JavaScript Object Notation (JSON) message demonstrates the standard model of the push event sent to your organization's endpoint for your registered webhook.

```json
{
  "events": [ // List of events to potentially support notifying in batch\
      {\
          "metadata": { // The metadata associated with this event\
              "eventId": "string", // UUID identifying this event for debugging/idempotency purposes\
              "eventTimestamp": "date-time", // UTC timestamp of when this event was generated in our system\
              "eventType": "string", // Indicates what event occurred in our system\
              "payloadSchemaVersion": "string", // The schema version of this event's payload (major)\
              "testEvent": boolean // Indicates if this is a test notification instead of a real event\
          },\
          "payload": { // The actual event data associated with this event\
            // eventType specific payload\
          }\
      }\
  ]
}
```

### Sample webhook message for eventType tracking_updated

This sample JSON message demonstrates the example model of the push event for eventType `tracking_updated` sent to your organization's endpoint for your registered webhook.

```json
{
  "events": [\
    {\
      "metadata": {\
        "eventId": "01408a5d-5e2d-4b2b-927f-c65d310ac356",\
        "eventTimestamp": "2024-09-09T16:21:13.110732395Z",\
        "eventType": "tracking_updated",\
        "payloadSchemaVersion": "v1",\
        "testEvent": false\
      },\
      "payload": {\
        "trackings": [\
          {\
            "shipiumTrackingId": "8f803e62-5f48-4f15-b4de-ae741dded55f",\
            "shipiumShipmentId": "1b727aa5-c2b9-4795-b2f1-7d2427312e8e",\
            "partnerShipmentId": null,\
            "carrierId": "usps",\
            "carrierTrackingId": "9400111206211849664726",\
            "carrierServiceMethodId": "usps-ground-advantage-service-method",\
            "shipmentStatus": "Delivered",\
            "shippedDateTime": "2024-09-05T23:50:00Z",\
            "deliveredAtDateTime": "2024-09-09T12:03:00-04:00",\
            "carrierEstimatedDeliveryDate": "2024-09-09T21:00:00-04:00",\
            "originalCarrierEstimatedDeliveryDate": "2024-09-09T21:00:00-04:00",\
            "partnerReferenceId": "myPartnerReferenceId",\
            "partnerReferenceId2": "myPartnerReferenceId2",\
            "carrierTrackingLink": "https://www.usps.com/uspstrack/?trknbr=8675309123",\
            "shipiumTenantId": "ab815bcc-950a-4902-ad8c-ac5ff6d9a438",\
            "partnerTenantId": "1",\
            "trackingEvents": [\
              {\
                "carrierDescription": "Delivered, In/At Mailbox",\
                "shipmentStatus": "Delivered",\
                "eventDate": "2024-09-09T16:03:00Z",\
                "postalCode": "10314",\
                "country": "US",\
                "city": "STATEN ISLAND",\
                "region": "NY"\
              },\
              {\
                "carrierDescription": "Out for Delivery",\
                "shipmentStatus": "Out For Delivery",\
                "eventDate": "2024-09-09T10:10:00Z",\
                "postalCode": "10314",\
                "country": "US",\
                "city": "STATEN ISLAND",\
                "region": "NY"\
              },\
              {\
                "carrierDescription": "Arrived at Post Office",\
                "shipmentStatus": "In Transit",\
                "eventDate": "2024-09-09T09:28:00Z",\
                "postalCode": "10314",\
                "country": "US",\
                "city": "STATEN ISLAND",\
                "region": "NY"\
              },\
              {\
                "carrierDescription": "Arrived at USPS Facility",\
                "shipmentStatus": "In Transit",\
                "eventDate": "2024-09-07T23:10:00Z",\
                "postalCode": "10314",\
                "country": "US",\
                "city": "STATEN ISLAND",\
                "region": "NY"\
              },\
              {\
                "carrierDescription": "Departed USPS Regional Facility",\
                "shipmentStatus": "In Transit",\
                "eventDate": "2024-09-07T21:46:00Z",\
                "postalCode": null,\
                "country": "US",\
                "city": "BROOKLYN",\
                "region": "NY"\
              },\
              {\
                "carrierDescription": "Arrived at USPS Regional Facility",\
                "shipmentStatus": "In Transit",\
                "eventDate": "2024-09-07T10:21:00Z",\
                "postalCode": null,\
                "country": "US",\
                "city": "BROOKLYN",\
                "region": "NY"\
              },\
              {\
                "carrierDescription": "Departed USPS Regional Facility",\
                "shipmentStatus": "In Transit",\
                "eventDate": "2024-09-07T08:49:00Z",\
                "postalCode": null,\
                "country": "US",\
                "city": "MID",\
                "region": "NY"\
              },\
              {\
                "carrierDescription": "Arrived at USPS Regional Facility",\
                "shipmentStatus": "In Transit",\
                "eventDate": "2024-09-07T05:55:00Z",\
                "postalCode": null,\
                "country": "US",\
                "city": "MID",\
                "region": "NY"\
              },\
              {\
                "carrierDescription": "Departed USPS Facility",\
                "shipmentStatus": "In Transit",\
                "eventDate": "2024-09-06T10:16:00Z",\
                "postalCode": "30268",\
                "country": "US",\
                "city": "PALMETTO",\
                "region": "GA"\
              },\
              {\
                "carrierDescription": "Arrived at USPS Regional Origin Facility",\
                "shipmentStatus": "In Transit",\
                "eventDate": "2024-09-06T01:05:00Z",\
                "postalCode": null,\
                "country": "US",\
                "city": "ATLANTA",\
                "region": "GA"\
              },\
              {\
                "carrierDescription": "Accepted at USPS Origin Facility",\
                "shipmentStatus": "In Transit",\
                "eventDate": "2024-09-05T23:50:00Z",\
                "postalCode": "30349",\
                "country": "US",\
                "city": "ATLANTA",\
                "region": "GA"\
              },\
              {\
                "carrierDescription": "Shipping Label Created, USPS Awaiting Item",\
                "shipmentStatus": "Label Printed",\
                "eventDate": "2024-09-05T20:04:00Z",\
                "postalCode": "30349",\
                "country": "US",\
                "city": "ATLANTA",\
                "region": "GA"\
              }\
            ]\
          }\
        ]\
      }\
    }\
  ]
}
```

## Tracking updated webhook payload elements defined

Data elements for the `tracking_updated` event type are defined in the following tables.

### Metadata elements

| Element               | Details                                                                                                            |
| --------------------- | --------------------------------------------------------------------------------------------------------------------- |
| `eventId`            | **Type:** String<br>**Description:** UUID identifying this event, used for debugging and idempotency purposes        |
| `eventTimestamp`      | **Type:** String (date-time)<br>**Description:** UTC timestamp of when this event was generated in Shipium's system, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format |
| `eventType`          | **Type:** String<br>**Description:** Indicates what event occurred; always `tracking_updated` for tracking webhooks  |
| `payloadSchemaVersion` | **Type:** String<br>**Description:** The schema version of this event's payload; current value is `v1`.              |
| `testEvent`          | **Type:** Boolean<br>**Values:**`true` or `false`<br>**Description:** If `true`, this is a test notification rather than a real event. |

### Tracking payload elements

| Element                    | Details                                                                                                            |
| -------------------------- | --------------------------------------------------------------------------------------------------------------------- |
| `shipiumTrackingId`       | **Type:** String<br>**Description:** Shipium's unique tracking identifier (UUID) for this shipment                 |
| `shipiumShipmentId`       | **Type:** String<br>**Description:** Shipium's unique shipment identifier (UUID); present for Shipium-created shipments|
| `partnerShipmentId`       | **Type:** String<br>**Description:** Your organization's shipment identifier, if provided during creation or registration|
| `carrierId`               | **Type:** String<br>**Description:** The carrier handling the shipment (e.g., `ups`, `fedex`, `usps`)             |
| `carrierTrackingId`      | **Type:** String<br>**Description:** The carrier's tracking number for this shipment                                 |
| `carrierServiceMethodId`  | **Type:** String<br>**Description:** The carrier's service method identifier (e.g., `usps-ground-advantage-service-method`)|
| `shipmentStatus`          | **Type:** String<br>**Description:** The current normalized shipment status; see [How Tracking Works](https://docs.shipium.com/docs/how-tracking-works) for all possible values.  |
| `shippedDateTime`        | **Type:** String (date-time)<br>**Description:** When the shipment was shipped, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format |
| `deliveredAtDateTime`    | **Type:** String (date-time)<br>**Description:** When the shipment was delivered, if applicable, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format |
| `carrierEstimatedDeliveryDate` | **Type:** String (date-time)<br>**Description:** The carrier's current estimated delivery date. This value may change over time as the carrier updates its estimate. |
| `originalCarrierEstimatedDeliveryDate` | **Type:** String (date-time)<br>**Description:** The carrier's initial estimated delivery date, captured at the time of the first tracking update. This value does not change. |
| `partnerReferenceId`      | **Type:** String<br>**Description:** An optional reference identifier provided by your organization                   |
| `partnerReferenceId2`     | **Type:** String<br>**Description:** An optional second reference identifier provided by your organization              |
| `carrierTrackingLink`     | **Type:** String<br>**Description:** A direct URL to the carrier's public tracking page for this shipment          |
| `shipiumTenantId`        | **Type:** String<br>**Description:** Shipium's tenant identifier, if applicable                                    |
| `partnerTenantId`         | **Type:** String<br>**Description:** Your organization's tenant identifier, if provided                             |
| `trackingEvents`          | **Type:** Array<br>**Description:** The complete event history for this shipment. Each entry contains the elements defined in the table below. |

### Tracking event elements

| Element               | Details                                                                                                            |
| --------------------- | --------------------------------------------------------------------------------------------------------------------- |
| `carrierDescription`   | **Type:** String<br>**Description:** The carrier's original description of the tracking event                       |
| `shipmentStatus`      | **Type:** String<br>**Description:** The normalized Shipium shipment status at the time of this event               |
| `eventDate`           | **Type:** String (date-time)<br>**Description:** When the event occurred, in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format |
| `postalCode`          | **Type:** String<br>**Description:** Postal code where the event occurred; may be `null` for some carrier events     |
| `country`            | **Type:** String<br>**Description:** Country code where the event occurred (ISO 3166-1 alpha-2)                   |
| `city`                | **Type:** String<br>**Description:** City where the event occurred                                              |
| `region`              | **Type:** String<br>**Description:** State, province, or region where the event occurred                          |

# Multiple trackings in webhook payloads

While most webhook notifications contain updates for a single tracking number, it's possible to receive multiple trackings in a single webhook payload. This occurs when Shipium processes tracking updates for multiple shipments simultaneously, typically when updates are received from carriers in batches.

**Key points about multiple trackings:**

- The `events` array will always contain a single event object.
- The `trackings` array within the payload can contain up to 10 tracking entries per webhook.
- Each tracking entry will have a distinct tracking number; the same tracking number will not appear multiple times within the same webhook.
- Each tracking contains its complete event history in the `trackingEvents` array.

# Related documentation

- [Shipium Webhooks](https://docs.shipium.com/docs/shipium-webhooks)
- [Configure Custom HTTP Headers for Webhooks](https://docs.shipium.com/docs/configure-custom-http-headers-for-webhooks)
- [Webhooks for Carrier Selection & Label Updates](https://docs.shipium.com/docs/webhooks-for-carrier-selection-label-updates)
- [Webhooks for Billing Management](https://docs.shipium.com/docs/webhooks-for-billing-management)
- [Webhooks for Pack App Order Lifecycle](https://docs.shipium.com/docs/webhooks-for-pack-app-order-lifecycle)

# Resources

Your Shipium team member is available to help along the way. However, you might find these resources helpful:

- [Terms and Definitions](https://docs.shipium.com/docs/common-terms)
- [Troubleshooting Guide](https://docs.shipium.com/docs/troubleshooting-guide)
- [Contact Shipium Support](https://docs.shipium.com/docs/contacting-shipium-support)
