DSX Webhooks
DASHER_COMPLETED_SHOPPING
The DASHER_COMPLETED_SHOPPING webhook is sent when a Dasher completes in-store shopping for an order, before checking-out. No webhooks are sent while the Dasher is actively shopping. This webhook delivers detailed information about the shopping event, including Dasher details, pickup and dropoff information, and a comprehensive list of items that were shopped.
The shopped_items section will indicate each item that was shopped, and the associated quantity. This section will also indicate if the item was a substitution.
| Field Name (Shopped Items Section) | Description | Always Expected or May be Null |
|---|---|---|
| name | The name of the item | Always Expected |
| description | Description of the item | Always Expected |
| quantity | The number of units of the item shopped | Always Expected |
| weight | The weight of the item, if applicable (may be null). | May be Null |
| weight_components | An array detailing the components of the weight, if applicable. | May be Null |
| weight_unit | The unit of weight measurement, if applicable (may be null). | May be Null |
| external_id | Merchant's unique item identifier. | Always Expected |
| requested_item_external_id | Merchant's unique item identifier of the original item ordered. | May be Null (if originally requested item picked) |
| external_instance_id | An additional identifier for the item used to distinguish between multiple items with the same external_id, such as for 2 steaks in the same order with separate weights or the "line number" of an itemized SKU in the order | May be Null (if additional identifier not used) |
| price | The price of the item in cents. | Always Expected |
| substitution_source | Source of substitution, if applicable (may be null). "Dasher" when substitution was picked by the dasher (as an override or upon customer request when contacted by dasher); "Customer" when substitution was directly selected by the customer; "Merchant" when substitution was recommended by merchant (customer still gets notified by dasher in case they want something else). Reference the substitutions explainer for more details. | May be Null (if originally requested item picked) |
| scanned_code | The barcode or PLU code of the item. | Always Expected |
| scanned_data_list | A list of ScannedData objects, each representing a possible deconstruction of the scanned barcode. Barcodes can conform to different formats (e.g., UPC-A, EAN-13), and this list captures the parsed components for each possible format. scanned_data_list[0] contains the components for the most likely barcode format (e.g., UPC-A). scanned_data_list[1], scanned_data_list[2], etc., represent the same barcode interpreted under other formats (e.g., EAN-13). The list is ordered by likelihood, with the most probable format appearing first | Always Expected |
| format | Format of barcode. Enum ["upc-a","upc-e","ean-13","ean-8","plu","gtin-14","unknown"] | Always Expected |
| is_variable_measure | Indicates whether the barcode represents a variable measure product with price or weight | May be null |
| indicator_digit | Used in Gtin14 barcodes to classify the type of item | May be null |
| product_code | Product code identifies an item within a given system, used in most barcode formats. | Always Expected |
| check_digit | Check digit if applicable. Typically the last digit of scanned UPC and EAN | May be null |
| country_code | Identify the country where the manufacturer is registered, used in EAN-13 and EAN-8 | May be null |
| manufacturer_code | Identifies the company that produced the product, used in UPC-A, EAN-13, UPC-E | May be null |
| number_system | The type of barcode and its categorization, used in UPC-A, UPC-E | May be null |
| plu_check_digit | Price check digit used in variable measure barcodes | May be null |
| company_prefix | Part of Gtin14 barcode, identifying the brand owner or manufacturer | May be null |
| item_reference | Identifies a product within a company’s, used in Gtin14 barcodes | May be null |
| scanned_code_sans_check_digit | Barcode without check digit | May be null |
| addition_source | Source of a new item added to the order after shopping has started, if applicable (may be null). Enum ["dasher","customer","merchant"] | May be Null (if no new item was added) |
| requested_item_external_instance_id | The external instance ID of the requested item, if applicable (may be null). This is used to identify the additional uniqueness of an item in an order, such as the "line number" of an itemized SKU in the order. | May be Null (if additional identifier not used) |
| fulfilled_substitution_type | Type of substitution fulfilled, if applicable (may be null). Enum ["no_substitution","pre_selected","customer_contacted","dasher_override"] | May be null |
| is_unknown | Boolean indicating if the item is unknown (may be null). Will be TRUE if the item is not found in the catalog | May be null |
Scenario 1: Item shopped as ordered
| Field Name (Shopped Items Section) | Value | Description |
|---|---|---|
| name | Cereal | Name of ordered item |
| description | 25oz cereal box | Description of the ordered item |
| quantity | 2 | Quantity ordered |
| weight | 2.5 | Total weight of the item |
| weight_components | ||
| weight_unit | oz | Weight unit of measure |
| external_id | 258147369 | Merchant's item identifier |
| requested_item_external_id | ||
| external_instance_id | 861555aa-6875-4b02-b5f2-58ee14f323a9 | Additional identifier as passed in the original payload |
| price | 499 | Price provided by merchant in inventory feed |
| substitution_source | ||
| scanned_code | 41303002001 | Barcode scanned by Dasher |
| addition_source | ||
| requested_item_external_instance_id | ||
| scanned_data_list | see json below | see explanation below |
| fulfilled_substitution_type | no_substitution | Original item picked |
| is_unknown | false | This item exists in catalog |
scanned_data_list:
[
{
"format": "upc-a",
"is_variable_measure": false,
"product_code": "00200",
"check_digit": "1",
"manufacturer_code": "41303",
"number_system": "0",
"scanned_code_sans_check_digit": "04130300200"
},
{
"format": "ean-13",
"is_variable_measure": false,
"product_code": "00200",
"check_digit": "1",
"country_code": "004",
"manufacturer_code": "1303",
"scanned_code_sans_check_digit": "004130300200"
}
]
The barcode scanned by the mobile device is a UPC-A barcode. However, for 13-digit barcodes that start with a leading zero, mobile devices may automatically omit the first zero during scanning. As a result, the scanned_data_list contains two items: scanned_data_list
- [0] represents the components of the barcode interpreted as a UPC-A code, which is the most likely format; scanned_data_list
- [1] represents the components of the same barcode interpreted as an EAN-13 code, which is also a valid possibility
Scenario 2: Weighted Item shopped as ordered
| Field Name (Shopped Items Section) | Value | Description |
|---|---|---|
| name | Banana | Name of ordered item |
| description | Chiquita Bananas | Description of the ordered item |
| quantity | 8 | Quantity ordered |
| weight | 3.25 | Total weight of the item |
| weight_components | 3.25 | Array of individually weighted items (if applicable) |
| weight_unit | oz | Weight unit of measure |
| external_id | 4011 | Merchant's item identifier |
| requested_item_external_id | ||
| external_instance_id | 861555aa-6875-4b02-b5f2-58ee14f323a9 | Additional identifier as passed in the original payload |
| price | 191 | Price provided by merchant in inventory feed. If the item is a weighted item, this value will be total weight multiplied by the weighted price |
| substitution_source | ||
| scanned_code | 4011 | Barcode scanned by Dasher |
| addition_source | ||
| requested_item_external_instance_id | ||
| scanned_data_list | see json below | Components of the barcode interpreted as a PLU code |
| fulfilled_substitution_type | no_substitution | Original item picked |
| is_unknown | false | This item exists in catalog |
scanned_data_list:
[
{
"format": "plu",
"is_variable_measure": false,
"product_code": "4011"
}
]
Scenario 3: Item substituted when the preference is set to contact customer
| Field Name (Shopped Items Section) | Value | Description |
|---|---|---|
| name | Cereal | Name of ordered item |
| description | 25oz cereal box | Description of the ordered item |
| quantity | 2 | Quantity ordered |
| weight | 2.5 | Total weight of the item |
| weight_components | ||
| weight_unit | oz | Weight unit of measure |
| external_id | 987456321 | Merchant's item identifier of the substitute item |
| requested_item_external_id | 258147369 | Merchant's item identifier of the original ordered item |
| external_instance_id | 861555aa-6875-4b02-b5f2-58ee14f323a9 | Additional identifier of the substitute item |
| price | 499 | Price provided by merchant in inventory feed |
| substitution_source | DASHER | Item substituted by dasher |
| scanned_code | 04904403 | Barcode scanned by Dasher |
| addition_source | ||
| requested_item_external_instance_id | d4621a56-208b-4d03-b6dd-6dc5c1eea47f | Additional identifier as passed in the original payload |
| scanned_data_list | see json below | see explanation below |
| fulfilled_substitution_type | contact | The item is picked as substitution after contacting consumer |
| is_unknown | false | This item exists in catalog |
scanned_data_list:
[
{
"format": "ean-8",
"is_variable_measure": false,
"product_code": "90440",
"check_digit": "3",
"country_code": "04",
"scanned_code_sans_check_digit": "0490440"
}
]
Scenario 4: Item substituted using a customer pre-selected substitute
| Field Name (Shopped Items Section) | Value | Description |
|---|---|---|
| name | Cereal | Name of ordered item |
| description | 25oz cereal box | Description of the ordered item |
| quantity | 2 | Quantity ordered |
| weight | 2.5 | Total weight of the item |
| weight_components | ||
| weight_unit | oz | Weight unit of measure |
| external_id | 987456321 | Merchant's item identifier of the substitute item |
| requested_item_external_id | 258147369 | Merchant's item identifier of the original ordered item |
| external_instance_id | 861555aa-6875-4b02-b5f2-58ee14f323a9 | Additional identifier of the substitute item |
| price | 499 | Price provided by merchant in inventory feed |
| substitution_source | CUSTOMER | Item substitution pre-selected by customer |
| scanned_code | 06541238 | Barcode scanned by Dasher |
| addition_source | ||
| requested_item_external_instance_id | d4621a56-208b-4d03-b6dd-6dc5c1eea47f | Additional identifier as passed in the original payload |
| scanned_data_list | see json below | Components of the barcode interpreted as a UPC-E code |
| fulfilled_substitution_type | pre-selected | The item is pre-selected by customer as substitution |
| is_unknown | false | This item exists in catalog |
scanned_data_list:
[
{
"format": "upc-e",
"is_variable_measure": false,
"product_code": "12",
"check_digit": "8",
"manufacturer_code": "654",
"number_system": "0",
"scanned_code_sans_check_digit": "0654123"
}
]
Scenario 5: Item substituted using a merchant recommended substitute
| Field Name (Shopped Items Section) | Value | Description |
|---|---|---|
| name | Cereal | Name of ordered item |
| description | 25oz cereal box | Description of the ordered item |
| quantity | 2 | Quantity ordered |
| weight | 2.5 | Total weight of the item |
| weight_components | ||
| weight_unit | oz | Weight unit of measure |
| external_id | 987456321 | Merchant's item identifier of the substitute item |
| requested_item_external_id | 258147369 | Merchant's item identifier of the original ordered item |
| external_instance_id | 861555aa-6875-4b02-b5f2-58ee14f323a9 | Additional identifier of the substitute item |
| price | 499 | Price provided by merchant in inventory feed |
| substitution_source | MERCHANT | item substitution recommended by merchant |
| scanned_code | 219408516284 | Barcode scanned by Dasher |
| addition_source | ||
| requested_item_external_instance_id | d4621a56-208b-4d03-b6dd-6dc5c1eea47f | Additional identifier as passed in the original payload |
| scanned_data_list | see json below | see explanation below |
| fulfilled_substitution_type | pre-selected | The item is pre-selected by merchant as substitution |
| is_unknown | false | This item exists in catalog |
scanned_data_list:
[
{
"format": "upc-a",
"is_variable_measure": true,
"product_code": "19408",
"check_digit": "4",
"number_system": "2",
"plu_check_digit": "5",
"scanned_code_sans_check_digit": "21940851628"
},
{
"format": "ean-13",
"is_variable_measure": true,
"product_code": "9408",
"check_digit": "4",
"country_code": "021",
"plu_check_digit": "5",
"scanned_code_sans_check_digit": "021940851628"
}
]
The barcode scanned by the mobile device is a UPC-A barcode starting with 2, which indicate it’s a weighted item:
- scanned_data_list[0] represents the components of the barcode interpreted as a UPC-A code, which is the most likely format.
- scanned_data_list[1] represents the components of the same barcode interpreted as an EAN-13 code, which is also a valid possibility"
Scenario 6: Dasher Item Override (When Overrides As Subs is not enabled)
| Field Name (Shopped Items Section) | Value | Description |
|---|---|---|
| name | Cereal | Name of ordered item |
| description | 25oz cereal box | Description of the ordered item |
| quantity | 2 | Quantity ordered |
| weight | 2.5 | Total weight of the item |
| weight_components | ||
| weight_unit | oz | Weight unit of measure |
| external_id | 258147369 | Merchant's item identifier of the orginal ordered item |
| requested_item_external_id | ||
| external_instance_id | 861555aa-6875-4b02-b5f2-58ee14f323a9 | Additional identifier as passed in the original payload |
| price | 499 | Price provided by merchant in inventory feed |
| substitution_source | DASHER | Item substituted by dasher |
| scanned_code | 10019320008266 | Barcode or PLU of the override item inputted by Dasher |
| addition_source | ||
| requested_item_external_instance_id | ||
| scanned_data_list | see json below | Components of the barcode interpreted as a GTIN-14 code |
| fulfilled_substitution_type | override | Dasher assert they picked the correct item |
| is_unknown | true | This item missing in catalog |
scanned_data_list:
[
{
"format": "gtin-14",
"indicator_digit": "1",
"product_code": "001932001351",
"check_digit": "2",
"scanned_code_sans_check_digit": "1001932001351"
}
]
Scenario 7: Dasher Item Override when item is found in the catalog (When Overrides As Subs is enabled)
| Field Name (Shopped Items Section) | Value | Description |
|---|---|---|
| name | Cereal | Name of ordered item |
| description | 25oz cereal box | Description of the ordered item |
| quantity | 2 | Quantity ordered |
| weight | 2.5 | Total weight of the item |
| weight_components | ||
| weight_unit | oz | Weight unit of measure |
| external_id | 987456321 | Merchant's item identifier of the override item |
| requested_item_external_id | 258147369 | Merchant's item identifier of the original ordered item |
| external_instance_id | 861555aa-6875-4b02-b5f2-58ee14f323a9 | Additional identifier as passed in the original payload |
| price | 499 | Price provided by merchant in inventory feed |
| substitution_source | DASHER | Substituted by dasher |
| scanned_code | 8901042957517 | Barcode or PLU of the override item inputted by Dasher |
| addition_source | ||
| requested_item_external_instance_id | ||
| scanned_data_list | see json below | Components of the barcode interpreted as a EAN-13 code |
| fulfilled_substitution_type | override | Dasher assert they picked the correct item |
| is_unknown | true | This item exists in catalog |
scanned_data_list:
[
{
"format": "ean-13",
"is_variable_measure": false,
"product_code": "95751",
"check_digit": "7",
"country_code": "890",
"manufacturer_code": "1042",
"scanned_code_sans_check_digit": "890104295751"
}
]
Scenario 8: Dasher Item Override when item is not found in the catalog (When Overrides As Subs is enabled)
| Field Name (Shopped Items Section) | Value | Description |
|---|---|---|
| name | Cereal | Name of ordered item |
| description | 25oz cereal box | Description of the ordered item |
| quantity | 2 | Quantity ordered |
| weight | 2.5 | Total weight of the item |
| weight_components | ||
| weight_unit | oz | Weight unit of measure |
| external_id | 987456321 | Merchant's item identifier of the original ordered item even though there was an override item. This is because item info is unknown as it is not found in the catalog, this scenario is treated similar to overrides being disabled. Note: is_unknown = true |
| requested_item_external_id | ||
| external_instance_id | 861555aa-6875-4b02-b5f2-58ee14f323a9 | Additional identifier as passed in the original payload |
| price | 499 | Price provided by merchant in inventory feed |
| substitution_source | DASHER | Substituted by dasher |
| scanned_code | 10019320013512 | Barcode or PLU of the override item inputted by Dasher |
| addition_source | ||
| requested_item_external_instance_id | ||
| scanned_data_list | see json below | Components of the barcode interpreted as a GTIN-14 code |
| fulfilled_substitution_type | override | Dasher assert they picked the correct item |
| is_unknown | true | This item exists in catalog |
scanned_data_list:
[
{
"format": "gtin-14",
"indicator_digit": "1",
"product_code": "001932001351",
"check_digit": "2",
"scanned_code_sans_check_digit": "1001932001351"
}
]
Scenario 9: Multi-Scan Variable Weight Item (when item_entries_info is enabled)*
| Field Name (Shopped Items Section) | Value | Description |
|---|---|---|
| name | Beef Round Steak | Name of ordered item |
| quantity | 2 | Number of packs ordered |
| weight | 2.083 | Total weight across all packs (kg) |
| weight_components | [1.072, 1.011] | Per-pack weights as scanned and entered by the Dasher |
| weight_unit | kg | Weight unit |
| external_id | 10301198 | Merchant item identifier |
| external_instance_id | WCXAAWW6 | Additional identifier for this order line |
| price | 4846 | Price in cents (from inventory feed or POS) |
| scanned_code | 01093396873934041525081331030010723922001608102508026014 | Barcode of the last scanned pack |
| fulfilled_substitution_type | no_substitution | Overall item-level fulfillment type |
| item_entries_info | See below | Per-pack scan data (feature flag required) |
item_entries_info:
"item_entries_info": [
{
"entry_fulfilled_substitution_type": "no_substitution",
"scanned_data_list": [
{
"format": "gs1-data-matrix",
"scanned_code": "01093396873934041525081331030010723922001608102508026014",
"barcode_weight": 1.072,
"barcode_weight_unit": "kg",
"barcode_price": 16.08,
"barcode_expiration_date": "2025-08-13"
}
]
},
{
"entry_fulfilled_substitution_type": "no_substitution",
"scanned_data_list": [
{
"format": "gs1-data-matrix",
"scanned_code": "01093396873934041525081231030010113922001538102508026014",
"barcode_weight": 1.011,
"barcode_weight_unit": "kg",
"barcode_price": 15.38,
"barcode_expiration_date": "2025-08-12"
}
]
}
]
In this example: Packs 1 and 2 are GS1 Data Matrix barcodes with price, weight, and expiry explicitly decoded.
ITEM_SHOPPING_UPDATE
DoorDash sends the ITEM_SHOPPING_UPDATE webhook throughout the shopping journey whenever an item's status changes — reflecting the Dasher's progress and any Dasher/customer communication for items that were picked, substituted, or refunded. It shares the same payload structure as DASHER_COMPLETED_SHOPPING (above), so you can reuse the same processing logic for both real-time updates and the final completion event.
Before You Begin
Developers can self-serve webhook setup in the Developer Portal. After setting up a webhook endpoint, contact DoorDash to enable ITEM_SHOPPING_UPDATE.
Note: Once DASHER_COMPLETED_SHOPPING has triggered, changes to the order are no longer accepted through DoorDash.
New Fields
DoorDash added the following fields to support item-level fulfillment visibility. These fields appear in both DASHER_COMPLETED_SHOPPING and ITEM_SHOPPING_UPDATE payloads.
| Field Name | Location | Type | Description |
|---|---|---|---|
| requested_quantity | shopped_items, unfulfilled_items | Nullable String | The number of units the customer originally requested. 0 for items shopped by weight. |
| requested_weight | shopped_items, unfulfilled_items | Nullable String | The weight requested for variable-weight items. 0 for items shopped by quantity. |
| unfulfilled_items (see below) | Root object | Optional Array | A new list containing items that could not be fulfilled at all, i.e. refunded. |
| pick_status_timestamp | shopped_items, unfulfilled_items | Nullable String | Timestamp reflecting when the item status last changed. Format: yyyy-MM-dd'T'HH:mm:ss.SSSSSS'Z'. Example: 2025-08-05T23:41:22.103131Z |
unfulfilled_items
A list of items that were not picked or substituted, but instead marked for refund. Each item carries a subset of fields from the shopped_items object — only the ones relevant to unfulfilled items.
"unfulfilled_items": [
{
"name": String,
"description": String,
"external_id": String,
"additionSource": String,
"pick_status_timestamp": String,
"requested_quantity": Double,
"requested_weight": Double
}
]
Tip:
unfulfilled_itemsonly includes items the Dasher attempted to shop for. It does not include items dropped during the initialCREATE_DELIVERYorACCEPT_QUOTEAPI request — those are listed in thedropped_itemsobject returned in the initial API response. To surface a comprehensive list of unfulfilled items to the customer, reference bothunfulfilled_itemsanddropped_items.
ITEM_SHOPPING_UPDATE vs. DASHER_COMPLETED_SHOPPING
DASHER_COMPLETED_SHOPPING and ITEM_SHOPPING_UPDATE share the same payload structure — only the event name differs. This ensures your systems can reuse the same processing logic for both real-time updates and the final completion event.
| ITEM_SHOPPING_UPDATE | DASHER_COMPLETED_SHOPPING | |
|---|---|---|
| Format | Same payload structure | Same payload structure |
| Timing | Sent when an item's status is updated during shopping. | Sent when the Dasher completes the entire shopping trip. |
| Items | Contains the list of items shopped so far, including substitutes and refunds. The final ITEM_SHOPPING_UPDATE contains the same items as the following DASHER_COMPLETED_SHOPPING. | Contains the full list of shopped items, including substitutes and refunds. |
| Flexibility | Items can still change after this webhook is sent. | Items cannot change after this webhook is sent. |
| Frequency | Sent at least once per item, and again whenever items are updated or added during shopping. | Sent once. |
Sample Webhook
The following payload applies to both ITEM_SHOPPING_UPDATE and DASHER_COMPLETED_SHOPPING.
{
"created_at": "2025-10-09T23:10:03.976384Z",
"event_name": "ITEM_SHOPPING_UPDATE",
"shopped_items": [
{
"name": "Coke Zero",
"description": "",
"quantity": 1,
"weight": null,
"weight_components": [],
"weight_unit": null,
"external_id": "4900004256",
"requested_item_external_id": null,
"external_instance_id": "1",
"price": 395,
"substitution_source": null,
"scanned_code": null,
"addition_source": null,
"requested_item_external_instance_id": null,
"scanned_data_list": null,
"fulfilled_substitution_type": "no_substitution",
"is_unknown": true,
"item_entries_info": null,
"pick_status_timestamp": "2025-10-09T22:45:05.836800Z",
"requested_quantity": 2,
"requested_weight": 0
},
{
"name": "vegetables",
"description": "",
"quantity": 1,
"weight": 0.8,
"weight_components": [0.8],
"weight_unit": "lbs",
"external_id": "970461140",
"requested_item_external_id": null,
"external_instance_id": "1",
"price": 559,
"substitution_source": null,
"scanned_code": null,
"addition_source": null,
"requested_item_external_instance_id": null,
"scanned_data_list": null,
"fulfilled_substitution_type": "no_substitution",
"is_unknown": true,
"item_entries_info": null,
"pick_status_timestamp": "2025-10-09T22:46:32.797320Z",
"requested_quantity": 0,
"requested_weight": 1
}
],
"unfulfilled_items": [
{
"name": "Top Flight Standards 3 Subject College Rule Notebook 1 Pack",
"description": "",
"external_id": "0007575533601_W",
"addition_source": null,
"pick_status_timestamp": "2025-10-09T23:10:01.285058Z",
"requested_quantity": 1,
"requested_weight": 0
}
]
}
FAQ
Q: Does unfulfilled_items include all items that were dropped from the original order?
A: No. unfulfilled_items only includes items the Dasher attempted to shop for — it does not include items dropped during the initial CREATE_DELIVERY or ACCEPT_QUOTE API request. The dropped_items object present in the initial API response contains merchant-supplied IDs of all items dropped at order creation. To surface a comprehensive list of unfulfilled items to the customer, reference both unfulfilled_items and dropped_items.
Q: What is the expected timeframe between the final ITEM_SHOPPING_UPDATE and DASHER_COMPLETED_SHOPPING events? A: The webhooks are triggered by different actions — the final ITEM_SHOPPING_UPDATE happens when all items in the list have been shopped for, whereas DASHER_COMPLETED_SHOPPING is triggered when the Dasher proceeds to the checkout screen. In most cases these fire in close succession, but there may be a delay while the Dasher is prompted to review pending substitutions or communicate with the customer.
DASHER_COMPLETED_STAGING
In Dasher Shop and Stage Orders, following the DASHER_COMPLETED_SHOPPING, a DASHER_COMPLETED_STAGING webhook will be sent to confirm the location where the Dasher has staged the order.
"staged_containers" : [
{
"external_id": "id1",
"external_location_id": "A1",
"external_zone_id": "Ambient"
},
{
"external_id": "id2",
"external_location_id": "A1",
"external_zone_id": "Ambient"
},
{
"external_id": "id3",
"external_location_id": "A1",
"external_zone_id": "Ambient"
}
]