> For the complete documentation index, see [llms.txt](https://developer.esw.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developer.esw.com/fulfillment-api/resources/tutorials/confirming-pick-and-pack.md).

# Confirming Pick & Pack

`POST /v1/PickPack/Confirm` is how a 3PL provider tells ESW that an order has been picked from warehouse locations, packed into a box, and is ready for carrier collection. It is the operational handover point between the 3PL's warehouse management system and ESW's shipping and fulfilment platform.

The request carries a detailed `pickPackResponse` payload that reports whether the pack operation succeeded.

```
POST /v1/PickPack/Confirm
Authorization: Bearer <token>
Content-Type: application/json
```

***

### When to Call This Endpoint

Call `POST /PickPack/Confirm` when a 3PL's picking and packing operation for an order is **complete** :

* All order items have been located and picked from their warehouse locations (or a pick failure has been recorded)
* The goods have been packed into shipping cartons
* The parcel(s) are staged for carrier collection

This endpoint is called **by the 3PL.** It represents the 3PL's report back to ESW on what happened during the warehouse operation.

***

### Request Structure

`PickPackConfirm` has four required fields.

```json
POST /v1/PickPack/Confirm

{
  "confirmationId": "PPK-2024-78542-001",
  "confirmationDateTime": "2024-11-03T09:15:00.000Z",
  "eswOrderReference": "ESW-20241101-00012345",
  "tenantCode": "MYBRAND",
  "pickPackResponse": { ... }
}
```

| Field                  | Type                  | Required | Description                                                                  |
| ---------------------- | --------------------- | -------- | ---------------------------------------------------------------------------- |
| `confirmationId`       | string                | ✅        | Your unique identifier for this pick/pack confirmation — the idempotency key |
| `eswOrderReference`    | string                | ✅        | The ESW order reference this confirmation relates to                         |
| `tenantCode`           | string                | ✅        | The brand/tenant code                                                        |
| `pickPackResponse`     | object                | ✅        | The outcome of the pick/pack operation — see Steps 4–9                       |
| `confirmationDateTime` | ISO 8601 UTC datetime | ❌        | When the pick/pack operation completed in the warehouse                      |

***

### `confirmationId` — The Idempotency Key

`confirmationId` is the primary deduplication key for this endpoint. If a request times out or fails in transit, you can safely resubmit with the same `confirmationId` — ESW will recognise it as a duplicate and not apply the operation twice.

```json
"confirmationId": "PPK-2024-78542-001"
```

#### Generation recommendations

* Generate a `confirmationId` before sending the request — store it in your WMS alongside the order reference
* Make it meaningful for debugging: include the order reference and a pack station identifier
* Use a format that is unique per pick/pack event — not per order (an order may be packed in multiple waves if it has back-orders)

```ruby
PPK-{orderRef}-{packStation}-{timestamp}
PPK-78542-WH01-20241103091500
```

#### `confirmationDateTime`

Capture and submit the timestamp of when the physical packing completed in the warehouse, not when the API call was made. This is used for SLA tracking and audit.

```json
"confirmationDateTime": "2024-11-03T09:15:00.000Z"
```

***

### &#x20;`pickPackResponse` — Overview

`pickPackResponse` is the structured report of what happened during the warehouse operation. It has one required field (`status`) and three optional fields.

```json
"pickPackResponse": {
  "status": "Confirmed",
  "failureReason": "None",
  "problemMessage": null,
  "itemsResult": [ ... ]
}
```

| Field            | Required | Description                                                          |
| ---------------- | -------- | -------------------------------------------------------------------- |
| `status`         | ✅        | The overall outcome of the pick/pack — `PickPackResponseStatus` enum |
| `failureReason`  | ❌        | Provider-level reason when `status` is a failure variant             |
| `problemMessage` | ❌        | Free-text additional detail about a failure                          |
| `itemsResult`    | ❌        | Per-item breakdown — required when `status` is `PartialResult`       |

***

### `PickPackResponseStatus`

#### `"AcceptedForProcessing"` — queued, not yet processed

```json
"status": "AcceptedForProcessing"
```

The 3PL's system has accepted the pick/pack instruction and queued it for processing, but picking has not yet been attempted. ESW will wait for a subsequent `Confirmed` or failure status.

**When to use:** When your WMS operates asynchronously — you confirm receipt of the instruction immediately and will send the final result separately.

**ESW action:** Records the status; awaits a follow-up confirmation.

***

#### `"Confirmed"` — fully successful

```json
"status": "Confirmed"
```

All items in the order were picked and packed successfully. The package is ready for carrier collection.

**When to use:** Every item was found, picked, and packed at the correct quantity. This is the normal success outcome.

**ESW action:** Marks the order as picked and packed; triggers the next step in the fulfilment workflow (carrier booking, label generation).

***

#### `"PartialResult"` — some items succeeded, some failed

{% code expandable="true" %}

```json
"status": "PartialResult",
"itemsResult": [
  {
    "productCode": "SKU-00123",
    "quantityConfirmed": 1,
    "quantityFailed": 0,
    "status": "Success"
  },
  {
    "productCode": "SKU-00456",
    "quantityConfirmed": 0,
    "quantityFailed": 2,
    "status": "Failed",
    "failureReason": "Damaged"
  }
]
```

{% endcode %}

Some items were successfully picked and packed; others could not be fulfilled. The `itemsResult` array **must** be populated when this status is used — it tells ESW which items succeeded and which failed, and why.

**When to use:** A multi-item order where at least one item was packed but at least one could not be. Common causes: one item is out of stock, one item is damaged, one item has been cancelled.

**ESW action:** Proceeds to ship the successfully packed items; takes action on the failed items (refund, reallocation, customer service) based on the `failureReason`.

***

#### `"Failed"` — the entire operation failed

```json
"status": "Failed",
"failureReason": "TransientError",
"problemMessage": "WMS connection timeout during pick instruction retrieval"
```

The pick/pack operation failed entirely — no items were picked or packed. The `failureReason` explains why.

**When to use:** A systemic failure prevented any picking — WMS outage, integration error, configuration problem.

**ESW action:** Depending on `failureReason`, ESW may retry, escalate, or cancel the operation.

***

#### `"BadRequest"` — the request was invalid

```json
"status": "BadRequest",
"problemMessage": "Order reference ESW-20241101-00012345 not found in WMS"
```

The 3PL's system could not process the request because the data was invalid — typically because the order reference is unknown to the WMS.

**When to use:** The order reference received from ESW does not exist in the 3PL's system.

**ESW action:** Investigates the reference mismatch; may require manual intervention.

***

#### `"BadAddress"` — delivery address is invalid

```json
"status": "BadAddress",
"failureReason": "BadAddress",
"problemMessage": "Destination postcode ZZ9 9ZZ not recognised by carrier routing system"
```

The carrier has rejected the shipment because the delivery address is invalid or undeliverable.

**When to use:** Address validation at the carrier or routing level has failed. The goods have been packed but the carrier cannot accept the shipment.

**ESW action:** ESW initiates an address correction workflow — the order will not ship until a valid address is confirmed.

***

#### `"TimeOut"` — the operation timed out

```json
"status": "TimeOut",
"failureReason": "TimeOut"
```

The pick/pack operation did not complete within the expected time window.

**When to use:** The WMS did not receive a response from a subsystem within the required timeout. The state of the physical goods is unknown.

**ESW action:** ESW treats this as a transient failure and may retry or escalate.

***

### &#x20;`PickPackProviderFailureReason`&#x20;

`failureReason` at the `pickPackResponse` level describes why the **overall** operation failed. It is used when `status` is `Failed`, `BadAddress`, or `TimeOut`.

| Value                 | When to use                                                                        | Retry?                           |
| --------------------- | ---------------------------------------------------------------------------------- | -------------------------------- |
| `"None"`              | No failure — operation succeeded. Use with `Confirmed` or `AcceptedForProcessing`. | N/A                              |
| `"TransientError"`    | A temporary system error — network timeout, WMS outage, temporary unavailability   | ✅ Retry recommended              |
| `"BadAddress"`        | The carrier cannot deliver to the address — postcode invalid, address unrecognised | ❌ Requires address correction    |
| `"TimeOut"`           | The operation did not complete within the expected time window                     | ✅ Retry after interval           |
| `"_3PLNotConfigured"` | The 3PL has not been correctly set up in ESW's system for this order/tenant        | ❌ Requires ESW configuration fix |

***

### &#x20;`itemsResult` — the Per-Item Breakdown

`itemsResult` is an array of `ItemProviderResponse` objects. It is **required** when `status` is `PartialResult` and **recommended** for `Confirmed` status to provide ESW with a full audit trail.

```json
"itemsResult": [
  {
    "productCode": "SKU-00123",
    "quantityConfirmed": 1,
    "quantityFailed": 0,
    "status": "Success",
    "failureReason": "NotDefined",
    "problemMessage": null
  }
]
```

| Field               | Type    | Description                                    |
| ------------------- | ------- | ---------------------------------------------- |
| `productCode`       | string  | The SKU or product code of the item            |
| `quantityConfirmed` | integer | Number of units successfully picked and packed |
| `quantityFailed`    | integer | Number of units that could not be fulfilled    |
| `status`            | enum    | Item-level outcome — `_3PlProviderItemStatus`  |
| `failureReason`     | enum    | Why units failed — `_3PLItemFailureReason`     |
| `problemMessage`    | string  | Free-text additional detail                    |

***

### &#x20;`_3PlProviderItemStatus`&#x20;

| Value             | Meaning                                                                                                              |
| ----------------- | -------------------------------------------------------------------------------------------------------------------- |
| `"Created"`       | The item record was created in the 3PL system — initial state before picking                                         |
| `"Success"`       | All requested units of this item were picked and packed successfully                                                 |
| `"Failed"`        | All requested units of this item failed to be fulfilled                                                              |
| `"PartialResult"` | Some units of this item were packed; others failed — `quantityConfirmed` and `quantityFailed` must both be populated |

#### `quantityConfirmed` + `quantityFailed` = quantity ordered

When `status` is `PartialResult` at item level, the sum of `quantityConfirmed` and `quantityFailed` must equal the total quantity that was ordered for that SKU.

```json
{
  "productCode": "SKU-00456",
  "quantityConfirmed": 2,
  "quantityFailed": 1,
  "status": "PartialResult",
  "failureReason": "Damaged"
}
```

Here: 2 out of 3 ordered units were packed; 1 was damaged.

***

### &#x20;`_3PLItemFailureReason`&#x20;

This enum describes why specific units of an item could not be fulfilled. It drives ESW's downstream customer service and reallocation logic.

| Value                  | When to use                                                                                          | ESW's typical response                                                      |
| ---------------------- | ---------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- |
| `"NotDefined"`         | No failure applicable — item succeeded. Always use with `Success` status.                            | N/A — no failure                                                            |
| `"NotFound"`           | The item could not be located in the warehouse — pick face empty, mislocation                        | ESW may trigger a stock search or reallocation from another location        |
| `"CancelledItem"`      | The item was cancelled before picking (by ESW, the retailer, or the shopper)                         | ESW acknowledges the cancellation — refund process may have already started |
| `"OperationalCancel"`  | The 3PL's system cancelled the item for an operational reason (e.g. end of shift, system constraint) | ESW will investigate and may re-release the item for picking                |
| `"Damaged"`            | The item was located but is in an unsellable condition                                               | ESW triggers a quality/damage workflow; item is typically quarantined       |
| `"ReallocatedExclude"` | The item was reallocated to a different order or excluded from this pick wave                        | ESW processes the reallocation; the original order item is rescheduled      |
| `"EndOfDay"`           | The picking session ended before this item could be processed                                        | ESW will re-release the item for the next picking wave                      |

#### Choosing the right failure reason

```
Item could not be picked — why?
        │
        ├── Location was empty / item not where it should be
        │     → "NotFound"
        │
        ├── Item found but physically damaged or unsellable
        │     → "Damaged"
        │
        ├── Order or item was cancelled before picking started
        │     → "CancelledItem"
        │
        ├── 3PL cancelled the item pick for an internal reason
        │     → "OperationalCancel"
        │
        ├── Item was moved to a different pick wave or order
        │     → "ReallocatedExclude"
        │
        └── Shift/session ended before item was reached
              → "EndOfDay"
```

***

### Handling Partial Results

`PartialResult` is the most nuanced status because it can occur at both the order level and the item level simultaneously.

#### Order-level `PartialResult` + item-level `PartialResult`

{% code expandable="true" %}

```json
{
  "status": "PartialResult",
  "itemsResult": [
    {
      "productCode": "SKU-00123",
      "quantityConfirmed": 1,
      "quantityFailed": 0,
      "status": "Success"
    },
    {
      "productCode": "SKU-00456",
      "quantityConfirmed": 2,
      "quantityFailed": 1,
      "status": "PartialResult",
      "failureReason": "Damaged",
      "problemMessage": "One unit has a broken clasp — quarantined to shelf B-12-04"
    }
  ]
}
```

{% endcode %}

This example: the wallet (SKU-00123) fully succeeded; 2 of 3 belts (SKU-00456) were packed, but 1 was damaged.

#### Rules for `PartialResult`

* `itemsResult` **must** be populated — ESW cannot process a `PartialResult` without knowing which items succeeded and which failed
* Every ordered item must appear in `itemsResult` — do not omit items that succeeded
* `quantityConfirmed + quantityFailed` must equal the ordered quantity for each item
* Use item-level `status: "Success"` for fully successful items, `"Failed"` for total failures, `"PartialResult"` for partial quantity outcomes

***

### Examples

{% hint style="info" icon="arrow-progress" %}

#### Example A — Fully successful pick/pack

{% endhint %}

All three items in a three-line order were found and packed.

{% code expandable="true" %}

```json
POST /v1/PickPack/Confirm

{
  "confirmationId": "PPK-2024-78542-001",
  "confirmationDateTime": "2024-11-03T09:15:00.000Z",
  "eswOrderReference": "ESW-20241101-00012345",
  "tenantCode": "MYBRAND",
  "pickPackResponse": {
    "status": "Confirmed",
    "failureReason": "None",
    "itemsResult": [
      {
        "productCode": "SKU-00123",
        "quantityConfirmed": 1,
        "quantityFailed": 0,
        "status": "Success",
        "failureReason": "NotDefined"
      },
      {
        "productCode": "SKU-00456",
        "quantityConfirmed": 2,
        "quantityFailed": 0,
        "status": "Success",
        "failureReason": "NotDefined"
      },
      {
        "productCode": "SKU-00789",
        "quantityConfirmed": 1,
        "quantityFailed": 0,
        "status": "Success",
        "failureReason": "NotDefined"
      }
    ]
  }
}
```

{% endcode %}

***

{% hint style="info" icon="arrow-progress" %}

#### Example B — One item not found (partial result)

{% endhint %}

The wallet and scarf were packed. The belt could not be located.

{% code expandable="true" %}

```json
{
  "confirmationId": "PPK-2024-78543-001",
  "confirmationDateTime": "2024-11-03T09:22:00.000Z",
  "eswOrderReference": "ESW-20241101-00012346",
  "tenantCode": "MYBRAND",
  "pickPackResponse": {
    "status": "PartialResult",
    "itemsResult": [
      {
        "productCode": "SKU-00123",
        "quantityConfirmed": 1,
        "quantityFailed": 0,
        "status": "Success",
        "failureReason": "NotDefined"
      },
      {
        "productCode": "SKU-00789",
        "quantityConfirmed": 1,
        "quantityFailed": 0,
        "status": "Success",
        "failureReason": "NotDefined"
      },
      {
        "productCode": "SKU-00456",
        "quantityConfirmed": 0,
        "quantityFailed": 2,
        "status": "Failed",
        "failureReason": "NotFound",
        "problemMessage": "Pick face B-03-12 empty — item not found at expected location"
      }
    ]
  }
}
```

{% endcode %}

***

{% hint style="info" icon="arrow-progress" %}

#### Example C — Damaged item, partial quantity

{% endhint %}

3 belts ordered. 2 packed successfully. 1 found damaged.

{% code expandable="true" %}

```json
{
  "confirmationId": "PPK-2024-78544-001",
  "confirmationDateTime": "2024-11-03T09:30:00.000Z",
  "eswOrderReference": "ESW-20241101-00012347",
  "tenantCode": "MYBRAND",
  "pickPackResponse": {
    "status": "PartialResult",
    "itemsResult": [
      {
        "productCode": "SKU-00456",
        "quantityConfirmed": 2,
        "quantityFailed": 1,
        "status": "PartialResult",
        "failureReason": "Damaged",
        "problemMessage": "One belt has a broken buckle — moved to quarantine bay QZ-04"
      }
    ]
  }
}
```

{% endcode %}

***

{% hint style="info" icon="arrow-progress" %}

#### Example D — Entire operation failed (transient error)

{% endhint %}

WMS was unable to retrieve the pick instruction due to a network timeout.

```json
{
  "confirmationId": "PPK-2024-78545-001",
  "confirmationDateTime": "2024-11-03T09:35:00.000Z",
  "eswOrderReference": "ESW-20241101-00012348",
  "tenantCode": "MYBRAND",
  "pickPackResponse": {
    "status": "Failed",
    "failureReason": "TransientError",
    "problemMessage": "WMS connection timed out after 30s during pick instruction fetch — no items picked"
  }
}
```

***

{% hint style="info" icon="arrow-progress" %}

#### Example E — Bad address detected at packing

{% endhint %}

Goods were packed but the carrier routing system rejected the destination postcode.

```json
{
  "confirmationId": "PPK-2024-78546-001",
  "confirmationDateTime": "2024-11-03T09:40:00.000Z",
  "eswOrderReference": "ESW-20241101-00012349",
  "tenantCode": "MYBRAND",
  "pickPackResponse": {
    "status": "BadAddress",
    "failureReason": "BadAddress",
    "problemMessage": "DHL routing rejected postcode AA1 1AA for destination UK — address verification required"
  }
}
```

***

{% hint style="info" icon="arrow-progress" %}

#### Example F — End of day, item not reached

{% endhint %}

Picking session closed. One item not yet picked will be rescheduled.

{% code expandable="true" %}

```json
{
  "confirmationId": "PPK-2024-78547-001",
  "confirmationDateTime": "2024-11-03T17:00:00.000Z",
  "eswOrderReference": "ESW-20241101-00012350",
  "tenantCode": "MYBRAND",
  "pickPackResponse": {
    "status": "PartialResult",
    "itemsResult": [
      {
        "productCode": "SKU-00123",
        "quantityConfirmed": 1,
        "quantityFailed": 0,
        "status": "Success",
        "failureReason": "NotDefined"
      },
      {
        "productCode": "SKU-00789",
        "quantityConfirmed": 0,
        "quantityFailed": 1,
        "status": "Failed",
        "failureReason": "EndOfDay",
        "problemMessage": "Pick wave closed at 17:00 — item queued for next wave"
      }
    ]
  }
}
```

{% endcode %}

***

### Retry Strategies by Failure Type

| `status` / `failureReason`     | Retry?                  | Strategy                                                  |
| ------------------------------ | ----------------------- | --------------------------------------------------------- |
| `Confirmed`                    | Never                   | No retry needed                                           |
| `AcceptedForProcessing`        | Never initially         | Wait for follow-up `Confirmed` or failure status          |
| `Failed` + `TransientError`    | ✅ Yes                   | Retry with same `confirmationId` after a short delay      |
| `Failed` + `TimeOut`           | ✅ Yes                   | Retry with same `confirmationId` after a longer delay     |
| `Failed` + `_3PLNotConfigured` | ❌ No                    | Requires ESW configuration fix — escalate                 |
| `BadAddress`                   | ❌ No                    | Requires address correction — do not retry until resolved |
| `BadRequest`                   | ❌ No                    | Requires order reference investigation                    |
| `PartialResult`                | ❌ No (for failed items) | Confirm only the packed items; ESW handles failed items   |

***

### Common Errors

| Status                                        | Cause                               | Fix                                                        |
| --------------------------------------------- | ----------------------------------- | ---------------------------------------------------------- |
| `400` — `confirmationId` is required          | Field missing                       | Include a non-empty `confirmationId`                       |
| `400` — `eswOrderReference` is required       | Field missing                       | Include the ESW order reference                            |
| `400` — `tenantCode` is required              | Field missing                       | Include the `tenantCode`                                   |
| `400` — `pickPackResponse.status` is required | `pickPackResponse` missing `status` | `status` is required inside `pickPackResponse`             |
| `400` — invalid `status` value                | Unknown enum value                  | Use one of the seven valid `PickPackResponseStatus` values |
| `401`                                         | Missing or invalid JWT              | Provide a valid `Authorization: Bearer <token>` header     |


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## 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, and the optional `goal` query parameter:

```
GET https://developer.esw.com/fulfillment-api/resources/tutorials/confirming-pick-and-pack.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
