# Webhooks

## Getting Started

You need to provide parcelLab with a webhook URL that is secured with HTTPS.

{% hint style="info" %}
Plain HTTP is not supported.
{% endhint %}

All webhook requests are sent from one of the following IP addresses (for firewall whitelists).

<pre><code>35.156.240.70
35.157.215.87
<strong>35.157.195.174
</strong></code></pre>

The following authentication mechanisms are supported:

* HTTP Basic Authentication
* Access Token Headers
* OAuth2.0 Access Token

Webhooks are sent for individual consignments (that is: records in the parcelLab system). For split orders with multiple trackings, each individual tracking will receive an `onDispatch` event, even if they are sent at the same time.

{% hint style="info" %}
If an error is received after sending a webhook, there will be 15 retries at 30 minute intervals before an error is logged and the event is deleted from the queue.
{% endhint %}

## Events

The events described in the following table can be sent via webhook. There is no deduplication of events, which means multiple scans of the `OutForDelivery` type will trigger multiple webhooks.

All events contain the same data structure as specified in the [Data Format](#data-format) section, with a set of the events having additional fields that are only present for a specific event type. For details on the structure of the `data` key, please refer to the [Structure of `data`](#structure-of-data) section.&#x20;

<table><thead><tr><th width="181">Event</th><th>Description</th></tr></thead><tbody><tr><td>onInternalDelay</td><td>The consignment was not shipped in time. This event describes a breach of the specified <code>send_date</code> on record creation, if not specified differently in the retailer’s logic.</td></tr><tr><td>onCreated</td><td>A new record has been created through data transfer from the retailer.</td></tr><tr><td>onUpdated</td><td>A record has been updated through data transfer from the retailer. This does not include the updated fields.</td></tr><tr><td>onDispatch</td><td><p>The consignment was scanned for the first time (that is: network inbound scan) and is now on its way.<br></p><p>This event is only triggered once.</p></td></tr><tr><td>onScheduled</td><td><p>The carrier has announced the delivery day, optionally including a time window.<br></p><p>This event is triggered for every change in scheduled delivery date (for example: if the delivery is scheduled for an earlier or later day or to a different time window).</p></td></tr><tr><td>onDelay</td><td>Any delay or delivery issue event. This event includes an additional key that specifies the reason code for the delay.</td></tr><tr><td>onOutForDelivery</td><td>The consignment is out for delivery. Depending on the carrier, a delivery time window may be provided.</td></tr><tr><td>onFailedAttempt</td><td><p>The carrier attempted a delivery to the recipient but did not meet someone or could not access the delivery location.<br></p><p>This event can include additional keys that specify details about the expected next action that will be performed by the carrier.</p></td></tr><tr><td>onPickupReady</td><td><p>The consignment is ready for collection by the recipient at a courier location (for example: an access point or parcel locker). A further event for delivery or no collection can be expected.<br></p><p>This event can include additional keys that specify the pickup location.</p></td></tr><tr><td>onDelivered</td><td><strong>Final event:</strong> The consignment has been delivered.<br><br>This event can include an additional key that provides unstructured information about the delivery (for example: signature name).</td></tr><tr><td>onReturn</td><td><p><strong>Final event:</strong> The delivery failed and the consignment will be returned to the sender.<br></p><p>This event can include an additional key that specifies the reason code for the return.</p></td></tr></tbody></table>

## Data Format

Each event is submitted as a `POST` request.

The request can include the following optional URL query parameters, which are keys in the `references` field:

* courier
* tracking\_number
* orderNo
* client
* warehouse
* destination\_countr&#x79;*\_*&#x69;so3
* language

For more information, please refer to the [Structure of `references`](#structure-of-references) section.

The actual data is transmitted in the JSON encoded body (where header `Content-Type: 'application/json'` is set).

```json
{
  "event": "onDelivered",
  "eventid": "abc123-def456",
  "timestamp": "2000-12-16T16:12:00.000Z",
  "references": {
    "courier": "dhl-germany",
    "tracking_number": "00340000000001",
    "orderNo": "ORD0012345",
    "client": "parcelFashion",
    "warehouse": "EU",
    "destination_country_iso3": "DEU",
    "language": "de"
  },
  "data": {
    "tracking_link": "https://www.dhl.de/...?piececode=00340000000001",
    "reroute_link": "",
    "raw_scan": "DLVRD-ACCPT-ZU | parcelLab",
    "raw_scan_location": "Munich",
    "delivery_status": "Delivered",
    "delivery_status_locale_short": "Zugestellt",
    "delivery_status_locale_long": "Die Lieferung wurde der Empfängerin oder dem Empfänger (parcelLab) zugestellt.",
    "expected_delivery_date_raw": "",
    "expected_delivery_date_formatted": "",
    "delivery_location": "Doorstep",
    "delivery_information": "parcelLab"
  },
  "reporting": {
    "lead_time_door_to_door": 2,
    "lead_time_qualified_attempt": 2,
    "lead_time_end_to_end": 3
  }
}
```

## Data Structure of Webhooks

On the top level, webhooks include the fields described in the following table.

<table><thead><tr><th width="185">Field</th><th>Description</th></tr></thead><tbody><tr><td>event</td><td>The event type as specified in the <a href="#events">Events</a> section (for example: <code>onCreated</code>).</td></tr><tr><td>eventid</td><td>A unique ID for this specific event that can be used for deduplication. Event IDs are strings consisting of <code>a-f</code>, <code>0-9</code> and dash <code>-</code>.</td></tr><tr><td>timestamp</td><td>The timestamp of when the event happened (not when the webhook was triggered) in ISO 8601 format: Date and time in UTC <code>YYYY-MM-DDTHH:mm:ssZ</code>.</td></tr><tr><td>references</td><td>The reference numbers for the consignment as described in the <a href="#structure-of-references">Structure of <code>references</code> </a>section. Can optionally be submitted as URL search query parameters as well.</td></tr><tr><td>reporting</td><td>Logistics lead times of the consignment as described in the <a href="#structure-of-reporting">Structure of <code>reporting</code></a> section.</td></tr><tr><td>data</td><td>The event data as described in the <a href="#structure-of-data">Structure of <code>data</code></a> section. The <code>data</code> key contains mandatory fields that are transferred for every event and optional fields that are only transferred for specific events.</td></tr></tbody></table>

### JSON Schema

This section provides details on the JSON schema for webhooks.&#x20;

<details>

<summary>JSON Schema for Webhook Payloads</summary>

```json
{
  "$schema": "http://json-schema.org/draft-06/schema#",
  "$ref": "#/definitions/Root",
  "title": "parcelLab webhook JSON schema",
  "definitions": {
    "Root": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "event": {
          "type": "string",
          "description": "The event type as specified in the section for events below, for example onCreated"
        },
        "eventid": {
          "type": "string",
          "description": "A unique ID for this specific event that can be used for deduplication. Event IDs are string consisting of a-f, 0-9 and dash -"
        },
        "timestamp": {
          "type": "string",
          "format": "date-time",
          "description": "The timestamp of when the event happened (not the webhook was triggered) in ISO 8601 format"
        },
        "references": {
          "$ref": "#/definitions/References",
          "description": "Reference numbers for this consignment"
        },
        "data": {
          "$ref": "#/definitions/Data",
          "description": "Contains mandatory fields that are transferred for every event and optional fields that are only transferred for specific events"
        },
        "reporting": {
          "$ref": "#/definitions/Reporting",
          "description": "Logistics lead times of this consignment"
        }
      },
      "required": [
        "data",
        "event",
        "eventid",
        "references",
        "reporting",
        "timestamp"
      ],
      "title": "Root"
    },
    "Data": {
      "title": "Data",
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "tracking_link": {
          "type": "string",
          "format": "uri",
          "description": "URL to the carrier tracking page"
        },
        "reroute_link": {
          "type": "string",
          "format": "uri",
          "description": "URL to the carrier tracking page with delivery services like rerouting, change of delivery date etc. (is empty if link is not yet or no longer available)"
        },
        "raw_scan": {
          "type": "string",
          "description": "The raw event data as provided by the carrier (there is no common data pattern as this data is provided depending on the carrier interface)"
        },
        "raw_scan_location": {
          "type": "string",
          "description": "The location as provided by the carrier as a string, does not include coordinates or any structured address or location data"
        },
        "delivery_status": {
          "type": "string",
          "enum": [
            "InternalStatus",
            "Pending",
            "WarehousePrepared",
            "WarehouseDelay",
            "Packed",
            "Loaded",
            "OrderProcessed",
            "ReturnRegistered",
            "PickUpScheduled",
            "PickUpPlanned",
            "Upgrade",
            "InboundScan",
            "InterimHaul",
            "InTransit",
            "ExportHub",
            "ImportHub",
            "CustomsIn",
            "CustomsReleased",
            "Scheduled",
            "DestinationDeliveryCenter",
            "DestinationDeliveryDepot",
            "OutForDelivery",
            "Rerouted",
            "FailedAttemptFirst",
            "FailedAttemptSecond",
            "FailedAttemptFinal",
            "PickupReadyToday",
            "PickupReadyNextDay",
            "PartiallyDelivered",
            "Delivered",
            "Stored",
            "Return",
            "ReturnWarning",
            "Exception",
            "ReturnInProcess",
            "ReturnProcessed",
            "ReturnRefunded",
            "None",
            "Ignore",
            "Cancelled"
          ],
          "description": "parcelLab status code as specified at https://how.parcellab.works/docs/reporting/status-model#status-codes"
        },
        "delivery_status_locale_short": {
          "type": "string",
          "description": "Short localized string representing the delivery status limited to a few words (based on language as per references.language)"
        },
        "delivery_status_locale_long": {
          "type": "string",
          "description": "Full localized string representing the delivery status including all details like delivery date, delivery location, and delivery information (based on language as per references.language)"
        },
        "expected_delivery_date_raw": {
          "type": "string"
        },
        "expected_delivery_date_formatted": {
          "type": "string",
          "description": "Localized representation of the delivery date (based on language as per references.language), or empty string if no reliable EDD available"
        },
        "delivery_location": {
          "type": "string",
          "description": "Planned or actual delivery location as per parcelLab's delivery location codes, will be non-empty as soon as carrier scan indicates a planned delivery location and is not only set on successful delivery (see https://how.parcellab.works/docs/reporting/status-model#delivery-location-codes)"
        },
        "exception_code": {
          "type": "string",
          "enum": [
            "Exception",
            "Damaged",
            "TimeIssue",
            "AddressIssue",
            "CustomerRefusal",
            "Return",
            "DeliveryPayment",
            "Customs",
            "IdentFailed",
            "Misrouted",
            "ItemMissing",
            "LackSpace",
            "Notified",
            "MissingUpdates"
          ],
          "description": "Reason for the delay specified as parcelLab Delivery Problem Code, this key also is used for delivery_status_locale_long (see https://how.parcellab.works/docs/reporting/basics-of-logistics-analysis#delivery-problems)"
        },
        "next_action": {
          "type": "string",
          "enum": [
            "NewAttemptToday",
            "NewAttemptNextDay",
            "PickUpReady"
          ],
          "description": "Planned next action for this delivery after a failed attempt"
        },
        "next_action_is_specified": {
          "type": "boolean",
          "description": "Whether or not the next_action is explicitly specified in the scan event for the failed attempt (then true), or is set by the default value for the courier (then false)"
        },
        "pickup_location_address": {
          "type": "string",
          "description": "Plain address of pickup location without any HTML formatting, typically includes location name, street, zip code and city, country (all comma separated)"
        },
        "pickup_location_opening_hours": {
          "type": "object",
          "description": "Opening hours of pickup location in the format as specified below, is not available for all pickup locations"
        },
        "pickup_location_coordinates": {
          "type": "object",
          "description": "Geo-coordinates of pickup location in the format { lat: Float, long: Float }, is not available for all locations"
        },
        "delivery_information": {
          "type": "string",
          "description": "Specifies any details about the delivery, for example name of recipient, name of neighbor, description of deposit location for signature release"
        },
        "return_reason": {
          "type": "string",
          "enum": [
            "Recall",
            "NotCollected",
            "CustomerRefusal",
            "DeliveryPayment",
            "Customs",
            "AddressIssue",
            "Damaged",
            "Exception",
            "FailedDelivery",
            "Unknown"
          ],
          "description": "parcelLab code for failed delivery reasons as specified at https://how.parcellab.works/docs/reporting/watchlist#reasons-for-failed-deliveries"
        }
      },
      "required": [
        "tracking_link",
        "reroute_link",
        "raw_scan",
        "raw_scan_location",
        "delivery_status",
        "delivery_status_locale_short",
        "delivery_status_locale_long",
        "expected_delivery_date_raw",
        "expected_delivery_date_formatted",
        "delivery_location"
      ]
    },
    "References": {
      "title": "References",
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "courier": {
          "type": "string",
          "description": "parcelLab courier code"
        },
        "tracking_number": {
          "type": "string",
          "description": "Tracking number"
        },
        "orderNo": {
          "type": "string",
          "description": "Customer-facing order number"
        },
        "client": {
          "type": "string",
          "description": "Shop identifier"
        },
        "warehouse": {
          "type": "string",
          "description": "Warehouse identifier"
        },
        "destination_country_iso3": {
          "type": "string",
          "pattern": "^[A-Z]{3}$",
          "description": "Destination country as ISO 3166-1 alpha-3"
        },
        "language": {
          "type": "string",
          "pattern": "^[a-z]{2}$",
          "description": "Customer language as ISO 639-1 (two-letter code)"
        }
      },
      "required": [
        "client",
        "courier",
        "destination_country_iso3",
        "language",
        "orderNo",
        "tracking_number",
        "warehouse"
      ]
    },
    "Reporting": {
      "title": "Reporting",
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "lead_time_door_to_door": {
          "type": "integer",
          "minimum": -1,
          "maximum": 90,
          "description": "Courier network lead time starting from the inbound scan (onDispatch) until the first qualified delivery attempt (the first occurrence of onFailedAttempt, onPickupReady, or onDelivered)"
        },
        "lead_time_qualified_attempt": {
          "type": "integer",
          "minimum": -1,
          "maximum": 90,
          "description": "Recipient-percevied lead time starting from the EDI label confirmation by the carrier (typically coincides with onCreated) until the first qualified delivery attempt (the first occurrence of onFailedAttempt, onPickupReady, or onDelivered)"
        },
        "lead_time_end_to_end": {
          "type": "integer",
          "minimum": -1,
          "maximum": 90,
          "description": "Complete delivery time from the EDI label confirmation by the carrier (typically coincides with onCreated) until final delivery to the recipient (onDelivered)"
        }
      },
      "required": [
        "lead_time_door_to_door",
        "lead_time_end_to_end",
        "lead_time_qualified_attempt"
      ]
    }
  }
}
```

</details>

### Structure of `references`

The `references` key includes the most commonly used references for the consignment and forwards the data as received from the retailer. These fields are described in the following table.

<table><thead><tr><th width="242.5">Field</th><th>Description</th></tr></thead><tbody><tr><td>courier</td><td>The parcelLab carrier code.</td></tr><tr><td>tracking_number</td><td>The tracking number.</td></tr><tr><td>orderNo</td><td>The customer-facing order number.</td></tr><tr><td>client</td><td>The shop identifier.</td></tr><tr><td>warehouse</td><td>The warehouse identifier.</td></tr><tr><td>destination_country<em>_</em>iso3</td><td>The country code (for example: <code>USA</code>, <code>GBR</code>, <code>DEU</code>).</td></tr><tr><td>language</td><td>The language code (for example: <code>en</code>, <code>es</code>, <code>de</code>).</td></tr></tbody></table>

For further details on the content and format of these fields, please refer to the data model description for sending data to parcelLab.

{% content-ref url="../data-model" %}
[data-model](https://docs.parcellab.com/docs/developers/v2/data-elements/data-model)
{% endcontent-ref %}

### Structure of `reporting`

Lead times that are not yet available because their ending scan has not happened yet are sent with a `-1`. The fields included in the `reporting` key are described in the following table.

<table><thead><tr><th width="268.5">Field</th><th>Description</th></tr></thead><tbody><tr><td>lead_time_door_to_door</td><td>The carrier network lead time starting from the inbound scan (<code>onDispatch</code>) until the first qualified delivery attempt (the first occurrence of <code>onFailedAttempt</code>, <code>onPickupReady</code>, or <code>onDelivered</code>).</td></tr><tr><td>lead_time_qualified_attempt</td><td>The recipient-perceived lead time starting from the EDI label confirmation by the carrier (typically coincides with <code>onCreated</code>) until the first qualified delivery attempt.</td></tr><tr><td>lead_time_end_to_end</td><td>The complete delivery time from EDI label confirmation until final delivery to the recipient (<code>onDelivered</code>).</td></tr></tbody></table>

### Structure of `data`

The fields included in the `data` key are described in the following table.

<table><thead><tr><th width="316.5">Field</th><th>Description</th></tr></thead><tbody><tr><td>tracking_link</td><td>The URL to the carrier’s tracking page.</td></tr><tr><td>reroute_link</td><td>The URL to the carrier’s tracking page with delivery services such as rerouting, change of delivery date, and so on. This field will be empty if the link is not defined yet or no longer available.</td></tr><tr><td>raw_scan</td><td>The raw event data as provided by the carrier. There is no common data pattern as the data provided depends on the carrier interface.</td></tr><tr><td>raw_scan_location</td><td>The location as provided by the carrier as a string. This does not include coordinates or any structured address or location data.</td></tr><tr><td>delivery_status</td><td>The parcelLab status code as specified in <a href="../../status-model#status-codes">our status code documentation</a>.</td></tr><tr><td>delivery_status_locale_short</td><td>A short localized string representing the delivery status limited to a few words (based on language as per <code>references.language</code>).</td></tr><tr><td>delivery_status_locale_long</td><td>A full localized string representing the delivery status including all details such as delivery date, delivery location, and delivery information (based on language as per references.language).</td></tr><tr><td>expected_delivery<em>_</em>date_raw</td><td>JSON Object or an empty object if no reliable estimated delivery date is available, <a href="#format-of-expected_delivery_date_raw">according to this format</a>.</td></tr><tr><td>expected_delivery<em>_</em>date_formatted</td><td>A localized representation of the delivery date (based on language as per <code>references.language</code>) or an empty string if no reliable estimated delivery date is available.</td></tr><tr><td>delivery_location</td><td>The planned or actual delivery location according to parcelLab’s <a href="../../status-model#delivery-location-codes">delivery location codes</a>. This field will be non-empty as soon as the carrier scan indicates a planned delivery location and is not only set on successful delivery.</td></tr><tr><td>exception_code<br><br>Only for <code>onDelay</code></td><td>The reason for the delay according to parcelLab's <a href="https://app.gitbook.com/s/-LPf1Lv1YUuLYva6LrXQ/platform/overview/logistics-analysis#delivery-problems">delivery problem codes</a>. This key is also used for <code>delivery_status_locale_long</code>.</td></tr><tr><td>next_action<br><br>Only for <code>onFailedAttempt</code></td><td>The planned next action for this delivery after a failed attempt. This can be one of the following: <code>NewAttemptToday</code>, <code>NewAttemptNextDay</code> or <code>PickUpReady</code>.</td></tr><tr><td>next_action<em>_</em>is_specified<br><br>Only for <code>onFailedAttempt</code></td><td>If the <code>next_action</code> is explicitly specified in the scan event for the failed attempt (then <code>true</code>) or is set by the default value for the carrier (then <code>false</code>).</td></tr><tr><td>pickup_location<em>_</em>address<br><br>Only for <code>onPickupReady</code></td><td>The plain address of pickup location without any HTML formatting. This typically includes location name, street, zip code and city, country (all comma separated).</td></tr><tr><td>pickup_location<em>_</em>opening_hours<br><br>Only for <code>onPickupReady</code></td><td>The opening hours of the pickup location.<br><strong>Note:</strong> This is not available for all pickup locations.</td></tr><tr><td>pickup_location<em>_</em>coordinates<br><br>Only for <code>onPickupReady</code></td><td>The geo-coordinates of the pickup location in the format <code>{ lat: Float, long: Float }</code>.<br><strong>Note:</strong> This is not available for all pickup locations.</td></tr><tr><td>delivery_information<br><br>Only for <code>onDelivered</code></td><td>This specifies any details about the delivery (for example: name of recipient, name of neighbor, description of deposit location for signature release).</td></tr><tr><td>return_reason<br><br>Only for <code>onReturn</code></td><td>The return reason according to parcelLab’s <a href="https://app.gitbook.com/s/-LPf1Lv1YUuLYva6LrXQ/engage/customer-service/watchlist#reasons-for-failed-deliveries">code reasons for failed deliveries</a>.</td></tr></tbody></table>

#### Format of `expected_delivery_date_raw`

If no expected delivery date is known, this field will be populated as `"null"`.

If an expected delivery date is set, it can be in one of these formats, with all date/time format definitions in [Unicode Technical Standard #35](https://www.unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table). No timezone is given, as all dates are in local time of the recipient.

```json
{
    "startDate": "yyyy-MM-dd",
    "endDate": "yyyy-MM-dd",
    "startTime": "HH:mm:ss",
    "endTime": "HH:mm:ss"
}
```

There are two cases for known expected delivery dates:

1. Date only: Can be a date range or a single date. In this case both `startDate` and `endDate` are set. The format is `yyyy-MM-dd`.
2. Date and time: If the expected delivery date is a single day, it can optionally include a time window for delivery. In this case again *startDate* is equal to *endDate*, and both `startTime` and `endTime` are filled in format `HH:mm:ss`. In the case of the carrier announcing an approximate point in time for delivery (for example: *"delivery approximately at 12:35h"*), *startTime* and *endTime* will also contain the same value: *12:35:00*.

## Data in CloudEvent Format

parcelLab also supports transmitting data in the CloudEvent format. The `data` key is then dropped from the payload and directly listed as part of CloudEvent’s data key. The `references` and `reporting` keys are kept as a sub-object within the transmitted data.

```json
{
  "specversion": "1.0",
  "id": "abc123-def456",
  "source": "https://parcellab.com/integration/cloudevents",
  "type": "com.parcellab.tracking.event.ondelivered",
  "time": "2000-12-16T16:12:00.000Z",
  "datacontenttype": "application/json",
  "data": {
    "references": {
      "courier": "dhl-germany",
      "tracking_number": "00340000000001",
      "orderNo": "ORD0012345",
      "client": "parcelFashion",
      "warehouse": "EU",
      "destination_country_iso3": "DEU",
      "language": "de"
    },
    "data": {
      "tracking_link": "https://www.dhl.de/...?piececode=00340000000001",
      "reroute_link": "",
      "raw_scan": "DLVRD-ACCPT-ZU | parcelLab",
      "raw_scan_location": "Munich",
      "delivery_status": "Delivered",
      "delivery_status_locale_short": "Zugestellt",
      "delivery_status_locale_long": "Die Lieferung wurde der Empfängerin oder dem Empfänger (parcelLab) zugestellt.",
      "expected_delivery_date_raw": "",
      "expected_delivery_date_formatted": "",
      "delivery_location": "Doorstep",
      "delivery_information": "parcelLab"
    },
    "reporting": {
      "lead_time_door_to_door": 2,
      "lead_time_qualified_attempt": 2,
      "lead_time_end_to_end": 3
    }
  }
}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.parcellab.com/docs/developers/v2/data-elements/get-status-updates/webhooks.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
