> 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/postman-collection/3pl-fulfilment-flow.md).

# 3PL Fulfilment Flow

### <img src="/files/3Hzf3CWYWyHDFskuSRKJ" alt="" data-size="line"> What This Collection Does

This Postman collection covers the complete **3PL Fulfilment Flow** — the sequence of API calls that a third-party logistics provider (3PL) uses to synchronize stock, confirm an order has been packed, and notify the system that a shipment has left the warehouse.

{% file src="/files/IkA2wceihysLttyQZHW3" %}

{% hint style="info" %}
You need [**Postman**](https://postman.com/) to use this collection
{% endhint %}

It uses the **ESW Fulfilment API v1** and covers three steps in operational order:

```ruby
Sync Inventory → Confirm Pick & Pack → Submit ASN
```

All three endpoints accept `POST` requests and return `202 Accepted` — the API is fully asynchronous. Requests are queued and processed in the background; no polling is needed between steps.

***

### Import the Collection

{% stepper %}
{% step %}

#### Open Postman&#x20;

{% endstep %}

{% step %}

#### Click the three dots <i class="fa-ellipsis">:ellipsis:</i>

Click **Import** from the three dots in the top left corner
{% endstep %}

{% step %}

#### Upload file <i class="fa-file-code">:file-code:</i>

Drag and drop `ESW_3PL_Fulfilment_Flow.postman_collection.json` into the import window, or click **Upload Files** and select it
{% endstep %}

{% step %}

#### Confirm

Click **Import** to confirm

{% hint style="info" %}
The collection will appear in your left sidebar under **Collections** as:

{% code expandable="true" %}

```ruby
ESW Fulfilment API — 3PL Fulfilment Flow
```

{% endcode %}

Expand it to see all ten requests listed in step order.
{% endhint %}
{% endstep %}
{% endstepper %}

***

### Set Up Variables

The collection uses variables. Set them once and every request picks them up automatically.

**To set them:**

1. Click the collection name in the sidebar
2. Click the **Variables** tab at the top of the right panel

<figure><img src="/files/BxAYyK1cWJMHZSnQlmD4" alt=""><figcaption></figcaption></figure>

Fill in the column

| Variable              | What to enter                    | Example                    | Auto-managed?            |
| --------------------- | -------------------------------- | -------------------------- | ------------------------ |
| `baseUrl`             | The API base URL                 | `https://api.test.esw.com` | No                       |
| `token`               | Your Bearer JWT                  | `eyJhbGci...`              | No                       |
| `tenantCode`          | Your retailer's tenant code      | `ACME`                     | No                       |
| `fulfillmentCentreId` | The fulfilment centre identifier | `FC-IEDUB-001`             | No                       |
| `provider`            | The 3PL provider identifier      | `ACM-3PL`                  | No                       |
| `eswOrderReference`   | eShopWorld's order reference     | `ESW-987654321`            | No                       |
| `packageReference`    | The retailer's package reference | `PKG-2024-00001`           | No                       |
| `confirmationId`      | Leave blank                      | *(empty)*                  | Yes — Step 2 pre-request |

Click **Save** (`Cmd + S` on Mac / `Ctrl + S` on Windows) after filling in your values.

#### How `confirmationId` is auto-managed

Before Step 2 sends, its pre-request script generates a unique `confirmationId` using the current Unix timestamp:

```
confirmationId → CONF-1714556400123
```

This is saved to the collection variable and used in the request body via `{{confirmationId}}`. You never need to set this manually. It is also logged to the Console so you have a record of the ID used.

***

### Authentication

The collection uses **Bearer Token** authentication at the collection level. Every request automatically sends:

```
Authorization: Bearer {{token}}
```

**To verify it's configured:**

1. Click the collection name in the sidebar
2. Click the **Authorization** tab
3. **Auth Type** should show **Bearer Token**
4. **Token** field should show `{{token}}`

Individual requests inherit this — their **Authorization** tab will show **Inherit auth from parent**.

**Token expiry:**

If Step 2 returns `401 Unauthorized`, your JWT has expired. Steps 1 and 3 do not return 401 but a stale token would still cause failures. To refresh:

1. Click the collection name → **Variables**
2. Update the **Current Value** of `token`
3. Click **Save**

***

### 3PL Fulfilment Flow

{% code expandable="true" %}

```ruby
┌──────────────────────────────────────────────────────────────────────┐
│                                                                      │
│  Step 1 — Sync Inventory                           POST              │
│  Synchronise SKU quantities and article statuses                     │
│  at the fulfilment centre                          → 202 Accepted    │
│                                                                      │
│  Supports all article statuses:                                      │
│  Active · Expected · Quarantine · Suspended                          │
│                           │                                          │
│                           ▼                                          │
│  Step 2 — Confirm Pick & Pack                      POST              │
│  Confirm the order has been picked and packed,                       │
│  with per-item quantities confirmed or failed      → 202 Accepted    │
│                                                                      │
│  confirmationId auto-generated by pre-request script                 │
│  Supports full, partial, and failed pack outcomes                    │
│                           │                                          │
│                           ▼                                          │
│  Step 3 — Submit ASN                               POST              │
│  Notify the system the order has physically shipped                  │
│  with carrier, package, and item-level detail      → 202 Accepted    │
│                                                                      │
│  Supports full and partial shipments                                 │
│  Pre-request detects partial shipment and warns                      │
│                                                                      │
└──────────────────────────────────────────────────────────────────────┘
```

{% endcode %}

#### All three steps are asynchronous

Every request returns `202 Accepted` — meaning the request has been **queued** for processing, not completed. You do not need to poll between steps. Run them in sequence once each event has physically occurred:

| Step   | Run when                                           |
| ------ | -------------------------------------------------- |
| Step 1 | Stock levels have changed at the fulfilment centre |
| Step 2 | An order has been physically picked and packed     |
| Step 3 | The carrier has collected the package              |

#### Pre-request diagnostics

Every step includes a pre-request script that runs **before** the request is sent. It parses the request body and logs a summary to the Postman Console — giving you a pre-flight check before any data is submitted to the API. This is especially useful for catching partial shipments or quarantined SKUs before they reach the server.

***

### Step 1 — Sync Inventory

**Method:** `POST` **URL:** `{{baseUrl}}/v1/Inventory/Sync`

#### What it does

Synchronizes SKU quantities and article statuses at a specific fulfilment centre. Used by retailers or 3PL providers to keep ESW's stock records accurate before order fulfilment begins.

You do not need to include every SKU in every sync. Send only the SKUs whose quantities or statuses have changed since the last sync.

#### The request body

Click **Body** → confirm **raw** and **JSON** are selected. The pre-filled body includes four sample SKUs covering all four `articleStatus` values:

{% code expandable="true" %}

```json
{
  "tenantCode": "{{tenantCode}}",
  "fulfillmentCentreId": "{{fulfillmentCentreId}}",
  "provider": "{{provider}}",
  "items": [
    {
      "sku": "WMN-DRESS-001",
      "variantProductCode": "WMN-DRESS-001-M-RED",
      "upc": "012345678901",
      "ean": "5901234123457",
      "size": "M",
      "color": "Red",
      "articleStatus": "Active",
      "quantity": 45
    },
    {
      "sku": "MEN-JACKET-042",
      "articleStatus": "Quarantine",
      "quantity": 8
    }
  ]
}
```

{% endcode %}

Replace the sample SKUs with your actual product data before sending.

#### `articleStatus` values

| Status       | Meaning                                    | Available for fulfilment? |
| ------------ | ------------------------------------------ | ------------------------- |
| `Active`     | SKU is in stock and ready for picking      | ✅ Yes                     |
| `Expected`   | SKU is en route to the fulfilment centre   | ❌ Not yet                 |
| `Quarantine` | SKU is held for quality inspection         | ❌ No                      |
| `Suspended`  | SKU is discontinued or temporarily blocked | ❌ No                      |

#### Pre-request script

Before sending, the script parses the `items` array and logs a breakdown by status:

```ruby
=== Inventory Sync — Pre-flight ===
Tenant:               ACME
Fulfilment centre:    FC-IEDUB-001
Provider:             ACM-3PL
Total SKUs in sync:   4
  Active:     2
  Expected:   0
  Quarantine: 1
  Suspended:  1
⚠ 1 SKU(s) in Quarantine — not available for fulfilment.
⚠ 1 SKU(s) Suspended — excluded from fulfilment.
```

Warnings appear for Quarantine and Suspended items so you can confirm they are intentional before the request fires.

#### Expected response — 202 Accepted

The response body is empty. `202` means the sync request has been queued. Stock levels are updated asynchronously in the background.

#### Saved example responses

| Example                                | Code | When it applies                          |
| -------------------------------------- | ---- | ---------------------------------------- |
| `202 Accepted — Sync Queued`           | 202  | Normal success — sync queued             |
| `400 Bad Request — Validation Failure` | 400  | A required field is missing (e.g. `sku`) |
| `500 Internal Server Error`            | 500  | Platform error                           |

#### Test results

| Test                            | Passes when            |
| ------------------------------- | ---------------------- |
| `Inventory sync accepted (202)` | Response status is 202 |

#### Console output after sending

```
✓ Inventory sync queued successfully.
  Stock levels will be updated asynchronously.
  Proceed to Step 2 once order is picked and packed.
```

***

### Step 2 — Confirm Pick & Pack

**Method:** `POST` **URL:** `{{baseUrl}}/v1/PickPack/Confirm`

#### What it does

Sent by the 3PL to confirm that an order has been picked and packed and is ready for carrier collection. The payload describes the overall outcome and includes a per-item result showing quantities confirmed and failed.

#### The request body

{% code expandable="true" %}

```json
{
  "confirmationId": "{{confirmationId}}",
  "confirmationDateTime": "{{$isoTimestamp}}",
  "eswOrderReference": "{{eswOrderReference}}",
  "tenantCode": "{{tenantCode}}",
  "pickPackResponse": {
    "status": "Confirmed",
    "failureReason": "None",
    "problemMessage": null,
    "itemsResult": [
      {
        "productCode": "WMN-DRESS-001",
        "quantityOrdered": 1,
        "quantityConfirmed": 1,
        "quantityFailed": 0,
        "status": "Success",
        "failureReason": "NotDefined",
        "problemMessage": null
      },
      {
        "productCode": "KID-SNEAKER-07",
        "quantityOrdered": 2,
        "quantityConfirmed": 2,
        "quantityFailed": 0,
        "status": "Success",
        "failureReason": "NotDefined",
        "problemMessage": null
      }
    ]
  }
}
```

{% endcode %}

**Before sending, update:**

* `eswOrderReference` — pre-filled from your variable, confirm it matches the order being fulfilled
* `pickPackResponse.status` — reflect the actual outcome of the pick and pack operation
* `itemsResult` — one entry per product code, with accurate `quantityConfirmed` and `quantityFailed` values
* `confirmationDateTime` — resolved automatically by `{{$isoTimestamp}}` (Postman's built-in ISO 8601 timestamp)

#### `confirmationId` — auto-generated

Do not set `confirmationId` manually in the Variables tab. The pre-request script generates a unique value using the current Unix timestamp each time Step 2 runs:

```
Generated confirmationId: CONF-1714556400123
```

This is written to the `{{confirmationId}}` variable and resolved in the request body automatically. It is also logged to the Console so you have an audit record.

#### `pickPackResponse.status` values

| Status                  | When to use                                           |
| ----------------------- | ----------------------------------------------------- |
| `Confirmed`             | All items successfully picked and packed              |
| `PartialResult`         | Some items were packed; others could not be fulfilled |
| `Failed`                | The entire pick and pack operation failed             |
| `AcceptedForProcessing` | Acknowledged but processing has not yet begun         |
| `BadRequest`            | Invalid data was encountered during packing           |
| `BadAddress`            | Delivery address could not be validated               |
| `TimeOut`               | Operation timed out                                   |

#### `pickPackResponse.failureReason` values

| Value               | Meaning                                        |
| ------------------- | ---------------------------------------------- |
| `None`              | No failure — use with `Confirmed` status       |
| `TransientError`    | Temporary system error — retry is appropriate  |
| `BadAddress`        | Delivery address validation failed             |
| `TimeOut`           | Pick and pack timed out                        |
| `_3PLNotConfigured` | 3PL is not configured in the eShopWorld system |

#### `itemsResult[].status` values

| Value           | Meaning                                            |
| --------------- | -------------------------------------------------- |
| `Success`       | Item was successfully confirmed                    |
| `Failed`        | Item could not be confirmed                        |
| `PartialResult` | Item was partially confirmed                       |
| `Created`       | Item record has been created but not yet processed |

#### `itemsResult[].failureReason` values

| Value                | Meaning                                                            |
| -------------------- | ------------------------------------------------------------------ |
| `NotDefined`         | No failure — use with `Success` status                             |
| `NotFound`           | SKU not found in the fulfilment centre                             |
| `CancelledItem`      | Item was cancelled before packing                                  |
| `OperationalCancel`  | Cancelled for an operational reason                                |
| `Damaged`            | Item was found to be damaged during picking                        |
| `ReallocatedExclude` | Item was reallocated to a different order                          |
| `EndOfDay`           | End of warehouse operating day reached before item could be packed |

#### Pre-request script

Before sending, the script generates the `confirmationId` and logs a pre-flight summary:

```
Generated confirmationId: CONF-1714556400123
=== Pick & Pack Confirm — Pre-flight ===
Order reference:    ESW-987654321
Tenant:             ACME
Overall status:     Confirmed
Items in result:    2
Total confirmed:    3
Total failed:       0
```

If any items failed:

```
⚠ 1 item(s) failed to pack. Check itemsResult failureReason values.
```

If the overall status is `Failed`:

```
⚠ Overall status is Failed: TransientError
```

#### Expected response — 202 Accepted

The response body is empty. `202` means the confirmation has been queued.

#### Saved example responses

| Example                                | Code | When it applies                    |
| -------------------------------------- | ---- | ---------------------------------- |
| `202 Accepted — Confirmation Queued`   | 202  | Full confirmation accepted         |
| `202 Accepted — Partial Result`        | 202  | Partial pack confirmation accepted |
| `400 Bad Request — Validation Failure` | 400  | A required field is missing        |
| `401 Unauthorized`                     | 401  | Token missing or expired           |

#### Test results

| Test                                      | Passes when            |
| ----------------------------------------- | ---------------------- |
| `Pick & pack confirmation accepted (202)` | Response status is 202 |

#### Console output after sending

```
✓ Pick & pack confirmation queued.
  confirmationId: CONF-1714556400123
  Proceed to Step 3 once the carrier has collected the package.
```

***

### Submit Advanced Shipping Notice (ASN)

**Method:** `POST` **URL:** `{{baseUrl}}/v1/PickPack/AdvancedShippingNotice`

#### What it does

Notifies ESW that an order has physically left the warehouse. Sent by the 3PL or retailer once the carrier has collected the package. The ASN includes carrier identification, package measurements, and item-level shipment quantities.

#### The request body

{% code expandable="true" %}

```json
{
  "shipmentId": "SHIP-{{$timestamp}}",
  "eswOrderReference": "{{eswOrderReference}}",
  "shipmentDateTime": "{{$isoTimestamp}}",
  "shipmentStatus": "Shipped",
  "carrierId": "12",
  "packageReference": "{{packageReference}}",
  "carrierSCAC": "DHLE",
  "carrierReference": "1Z999AA10123456784",
  "shipFrom": {
    "name": "ACME Dublin Fulfilment Centre",
    "locationCode": "{{fulfillmentCentreId}}"
  },
  "packageWeight": {
    "weight": 1.85,
    "weightUnit": "KG"
  },
  "packageDimensions": {
    "length": 35.0,
    "width": 15.0,
    "height": 25.0,
    "dimensionUnit": "CM"
  },
  "items": [
    {
      "sku": "WMN-DRESS-001",
      "quantityOrdered": 1,
      "quantityShipped": 1,
      "unitOfMeasure": "EA",
      "itemStatus": "Shipped"
    },
    {
      "sku": "KID-SNEAKER-07",
      "quantityOrdered": 2,
      "quantityShipped": 2,
      "unitOfMeasure": "EA",
      "itemStatus": "Shipped"
    }
  ]
}
```

{% endcode %}

**Before sending, update:**

* `carrierId` — the ESW carrier identifier (must match the carrier that collected the package)
* `carrierSCAC` — the Standard Carrier Alpha Code for the carrier (see table below)
* `carrierReference` — the carrier's own tracking number for this parcel
* `shipFrom.name` — the name of the dispatch location
* `packageWeight` — actual package weight after packing
* `packageDimensions` — actual package dimensions after packing
* `items` — SKUs and shipped quantities matching what was actually shipped

#### Dynamic variables in this request

| Variable            | What it generates                    | Example                    |
| ------------------- | ------------------------------------ | -------------------------- |
| `{{$timestamp}}`    | Unix timestamp (seconds since epoch) | `1714556400`               |
| `{{$isoTimestamp}}` | ISO 8601 UTC datetime                | `2024-05-01T10:00:00.000Z` |

These are Postman built-ins. `{{$timestamp}}` ensures every `shipmentId` is unique without any manual input. `{{$isoTimestamp}}` sets `shipmentDateTime` to the exact time the request is sent.

#### `shipmentStatus` values

| Value              | When to use                                                                    |
| ------------------ | ------------------------------------------------------------------------------ |
| `Shipped`          | Package has been collected and is in transit — use for standard full shipments |
| `PartiallyShipped` | Only some items were included in this package — more shipments will follow     |
| `Unshipped`        | Package has not yet been collected — use to pre-notify the system              |

#### `carrierSCAC` reference

The SCAC is a 2–4 letter code that uniquely identifies the carrier. Common values:

| Carrier     | SCAC   |
| ----------- | ------ |
| DHL Express | `DHLE` |
| FedEx       | `FXFE` |
| UPS         | `UPSN` |
| TNT         | `TNTE` |
| Aramex      | `ARMX` |

Contact your ESW account team for the full list of supported carriers and their SCACs.

#### Handling partial shipments

If only some items are included in this package, update the body as follows:

1. Set `shipmentStatus` to `PartiallyShipped`
2. Set `quantityShipped` to the number actually shipped for each item
3. For items not in this shipment, set `quantityShipped` to `0`
4. Submit another ASN for the remaining items when they ship

The pre-request script automatically detects when `quantityShipped` is less than `quantityOrdered` and logs a warning:

```
⚠ Partial shipment detected (1 of 3 items).
  Set shipmentStatus to PartiallyShipped if not already done.
```

#### Pre-request script

```
=== Advanced Shipping Notice — Pre-flight ===
Order reference:    ESW-987654321
Package reference:  PKG-2024-00001
Shipment status:    Shipped
Carrier SCAC:       DHLE
Carrier reference:  1Z999AA10123456784
Ship from:          ACME Dublin Fulfilment Centre
Items in ASN:       2
Total ordered:      3
Total shipped:      3
```

#### Expected response — 202 Accepted

The response body is empty. `202` means the ASN has been queued for processing.

#### Saved example responses

| Example                                | Code | When it applies               |
| -------------------------------------- | ---- | ----------------------------- |
| `202 Accepted — ASN Queued`            | 202  | Full shipment ASN accepted    |
| `202 Accepted — Partial Shipment`      | 202  | Partial shipment ASN accepted |
| `400 Bad Request — Validation Failure` | 400  | A required field is missing   |
| `500 Internal Server Error`            | 500  | Platform error                |

#### Test results

| Test                 | Passes when            |
| -------------------- | ---------------------- |
| `ASN accepted (202)` | Response status is 202 |

#### Console output after sending

```
✓ Advanced Shipping Notice queued successfully.

=== 3PL Fulfilment Flow Complete ===
Tenant:             ACME
Order reference:    ESW-987654321
Package reference:  PKG-2024-00001
Confirmation ID:    CONF-1714556400123
Steps completed:    Sync Inventory → Confirm Pick & Pack → Submit ASN
```

***

### Run Collection

The Collection Runner executes all three steps automatically in sequence — useful for end-to-end testing or verifying the integration after a configuration change.

#### Opening the runner

1. Hover over the collection name in the left sidebar
2. Click the **⋯** (three dots) menu that appears
3. Select **Run**

#### Recommended settings

| Setting                             | Value    | Why                                                                           |
| ----------------------------------- | -------- | ----------------------------------------------------------------------------- |
| **Iterations**                      | `1`      | One fulfilment cycle per run                                                  |
| **Delay**                           | `500` ms | Small buffer between steps — all requests are async so no long wait is needed |
| **Persist responses for a session** | ✓ On     | Lets you review each response after the run                                   |
| **Stop run if an error occurs**     | ✓ On     | Stops if a step returns non-202 so you don't proceed with a failed state      |
| **Keep variable values**            | ✓ On     | Ensures `confirmationId` set in Step 2 is visible in the final summary        |

#### After the run

Click any request row in the results panel to see its response body, test outcome, and Console output for that step. The full workflow summary is printed in Step 3's Console output on success.

***

### Reading Test Results and Console Output

#### Test Results tab

After sending any request, click **Test Results** in the response panel.

**All tests across the collection:**

| Step   | Test                                      | Passes when            |
| ------ | ----------------------------------------- | ---------------------- |
| Step 1 | `Inventory sync accepted (202)`           | Response status is 202 |
| Step 2 | `Pick & pack confirmation accepted (202)` | Response status is 202 |
| Step 3 | `ASN accepted (202)`                      | Response status is 202 |

Since all three endpoints are asynchronous and return empty bodies on success, the tests focus entirely on the status code. All diagnostic detail comes from the Console.

#### Postman Console

Open via **View → Postman Console** or `Cmd + Alt + C` (Mac) / `Ctrl + Alt + C` (Windows/Linux).

> Open the Console **before** running requests. Output from requests sent before the Console was open is not retained.

**What the Console shows per step:**

| Step   | Pre-request output                                                                                              | Post-send output                               |
| ------ | --------------------------------------------------------------------------------------------------------------- | ---------------------------------------------- |
| Step 1 | SKU count by status, warnings for Quarantine and Suspended                                                      | Success/failure message                        |
| Step 2 | Auto-generated `confirmationId`, order reference, pack status, total confirmed vs failed, warnings for failures | Success/failure message with `confirmationId`  |
| Step 3 | Order and package references, carrier details, total ordered vs shipped, partial shipment warning if applicable | Success/failure message, full workflow summary |

***

### Field Reference

#### Step 1 — `InventorySyncRequest`

**Required:** `tenantCode`, `fulfillmentCentreId`, `provider`, `items`

**Required per item:** `sku`, `articleStatus`

| Field                        | Type    | Required | Description                                        |
| ---------------------------- | ------- | -------- | -------------------------------------------------- |
| `tenantCode`                 | string  | Yes      | Retailer's ESW tenant code                         |
| `fulfillmentCentreId`        | string  | Yes      | Identifier of the fulfilment centre                |
| `provider`                   | string  | Yes      | 3PL provider identifier                            |
| `items[].sku`                | string  | Yes      | Retailer's unique SKU identifier                   |
| `items[].articleStatus`      | enum    | Yes      | `Active` · `Expected` · `Quarantine` · `Suspended` |
| `items[].quantity`           | integer | No       | Current stock quantity at the fulfilment centre    |
| `items[].variantProductCode` | string  | No       | Variant-level product code                         |
| `items[].upc`                | string  | No       | Universal Product Code                             |
| `items[].ean`                | string  | No       | European Article Number                            |
| `items[].size`               | string  | No       | Size descriptor (e.g. `M`, `32`, `EU42`)           |
| `items[].color`              | string  | No       | Colour descriptor                                  |

***

#### Step 2 — `PickPackConfirm`

**Required:** `confirmationId`, `eswOrderReference`, `tenantCode`, `pickPackResponse`

**Required within `pickPackResponse`:** `status`

| Field                                              | Type    | Required | Description                                             |
| -------------------------------------------------- | ------- | -------- | ------------------------------------------------------- |
| `confirmationId`                                   | string  | Yes      | Unique ID for this confirmation — auto-generated        |
| `confirmationDateTime`                             | string  | No       | ISO 8601 UTC datetime — auto-set by `{{$isoTimestamp}}` |
| `eswOrderReference`                                | string  | Yes      | eShopWorld order reference                              |
| `tenantCode`                                       | string  | Yes      | Retailer's tenant code                                  |
| `pickPackResponse.status`                          | enum    | Yes      | Overall pick/pack outcome (see enum table in Section 8) |
| `pickPackResponse.failureReason`                   | enum    | No       | Reason if status is not `Confirmed`                     |
| `pickPackResponse.problemMessage`                  | string  | No       | Free-text description of any problem                    |
| `pickPackResponse.itemsResult[].productCode`       | string  | No       | Product code for this item                              |
| `pickPackResponse.itemsResult[].quantityOrdered`   | integer | No       | Quantity on the original order                          |
| `pickPackResponse.itemsResult[].quantityConfirmed` | integer | No       | Quantity successfully packed                            |
| `pickPackResponse.itemsResult[].quantityFailed`    | integer | No       | Quantity that could not be packed                       |
| `pickPackResponse.itemsResult[].status`            | enum    | No       | `Created` · `Success` · `Failed` · `PartialResult`      |
| `pickPackResponse.itemsResult[].failureReason`     | enum    | No       | Reason for item-level failure                           |
| `pickPackResponse.itemsResult[].problemMessage`    | string  | No       | Free-text item problem description                      |

***

#### Step 3 — `AdvancedShippingNoticeRequest`

| Field                             | Type    | Required | Description                                                      |
| --------------------------------- | ------- | -------- | ---------------------------------------------------------------- |
| `shipmentId`                      | string  | No       | Unique shipment ID — auto-generated via `{{$timestamp}}`         |
| `eswOrderReference`               | string  | No       | eShopWorld order reference                                       |
| `shipmentDateTime`                | string  | No       | ISO 8601 UTC shipment datetime — auto-set by `{{$isoTimestamp}}` |
| `shipmentStatus`                  | enum    | No       | `Shipped` · `PartiallyShipped` · `Unshipped`                     |
| `carrierId`                       | string  | No       | eShopWorld carrier identifier (integer as string)                |
| `packageReference`                | string  | No       | Retailer's package reference                                     |
| `carrierSCAC`                     | string  | No       | Standard Carrier Alpha Code (2–4 letters)                        |
| `carrierReference`                | string  | No       | Carrier's own tracking number                                    |
| `shipFrom.name`                   | string  | No       | Name of dispatch location                                        |
| `shipFrom.locationCode`           | string  | No       | Location code — pre-filled from `{{fulfillmentCentreId}}`        |
| `packageWeight.weight`            | number  | No       | Package weight after packing                                     |
| `packageWeight.weightUnit`        | enum    | No       | `KG` · `LB`                                                      |
| `packageDimensions.length`        | number  | No       | Package length                                                   |
| `packageDimensions.width`         | number  | No       | Package width                                                    |
| `packageDimensions.height`        | number  | No       | Package height                                                   |
| `packageDimensions.dimensionUnit` | enum    | No       | `CM` · `IN`                                                      |
| `items[].sku`                     | string  | No       | SKU of the shipped item                                          |
| `items[].quantityOrdered`         | integer | No       | Quantity on the original order                                   |
| `items[].quantityShipped`         | integer | No       | Quantity included in this shipment                               |
| `items[].unitOfMeasure`           | string  | No       | Unit of measure — typically `EA` (each)                          |
| `items[].itemStatus`              | enum    | No       | Currently only `Shipped` is valid                                |

***

### Common Scenarios

#### "Running the standard end-to-end fulfilment cycle"

1. Set all eight variables in the Variables tab
2. Update the `items` array in Step 1 with the current stock snapshot
3. Run Step 1
4. Once the order is physically picked and packed, update `itemsResult` in Step 2 with actual quantities
5. Run Step 2
6. Once the carrier collects, update carrier details and `items` in Step 3
7. Run Step 3

***

#### "Syncing only the SKUs that have changed"

Remove all unchanged SKUs from the Step 1 body. The inventory sync is a delta update — you don't need to send the full catalogue on every call. For example, if only one SKU moved from `Expected` to `Active`, send only that SKU:

{% code expandable="true" %}

```json
{
  "tenantCode": "{{tenantCode}}",
  "fulfillmentCentreId": "{{fulfillmentCentreId}}",
  "provider": "{{provider}}",
  "items": [
    {
      "sku": "WMN-DRESS-001",
      "articleStatus": "Active",
      "quantity": 45
    }
  ]
}
```

{% endcode %}

***

#### "Confirming a partially packed order"

Some items couldn't be packed — for example, one was damaged during picking. In Step 2:

1. Set `pickPackResponse.status` to `PartialResult`
2. Set `pickPackResponse.failureReason` to `None` (failure is at item level, not overall)
3. In `itemsResult`, set the damaged item's `status` to `Failed`, `quantityFailed` to the affected quantity, and `failureReason` to `Damaged`

```json
{
  "productCode": "KID-SNEAKER-07",
  "quantityOrdered": 2,
  "quantityConfirmed": 1,
  "quantityFailed": 1,
  "status": "PartialResult",
  "failureReason": "Damaged",
  "problemMessage": "One unit found damaged during pick. Removed from shipment."
}
```

***

#### "Reporting a complete pick and pack failure"

If the 3PL could not process the order at all — for example due to a system timeout — set `pickPackResponse.status` to `Failed` and include the appropriate `failureReason`:

```json
{
  "status": "Failed",
  "failureReason": "TimeOut",
  "problemMessage": "WMS connection timed out during pick operation.",
  "itemsResult": []
}
```

The pre-request script will log a warning before sending. Contact your ESW account team for guidance on retry and resubmission procedures after a failed confirmation.

***

#### "Submitting a partial shipment ASN"

Only some items are in this package — the rest ship later:

1. Set `shipmentStatus` to `PartiallyShipped`
2. Set `quantityShipped` to the number actually in this package for each item
3. For items not in this package, set `quantityShipped` to `0`
4. Submit a second ASN for the remaining items when their package ships

The pre-request script will detect the partial shipment automatically and log a warning if `shipmentStatus` is still set to `Shipped`.

***

#### "Running the full flow for multiple orders in one session"

Use the Collection Runner's **Data file** feature:

1. Create a CSV with one row per order:

```
   eswOrderReference,packageReference
   ESW-987654321,PKG-2024-00001
   ESW-987654322,PKG-2024-00002
```

2. Open the Collection Runner
3. Under **Data**, click **Select File** and choose the CSV
4. Set **Iterations** to the number of rows
5. Set **Delay** to `1000` ms
6. Click **Run**

Postman will substitute each row's values into the corresponding variables for each iteration.

***

### Troubleshooting

**`401 Unauthorized` on Step 2** Your JWT token has expired. Update the `token` variable **Current Value** and click **Save**. Steps 1 and 3 do not return 401 directly but a stale token should be refreshed before running any step.

***

**`400 Bad Request` on Step 1** A required field is missing from the `items` array. The response body `errors` object will name the specific field and item index (e.g. `items[0].sku`). The two required fields per item are `sku` and `articleStatus`. All other fields are optional.

***

**`400 Bad Request` on Step 2** One of the four required fields is missing: `confirmationId`, `eswOrderReference`, `tenantCode`, or `pickPackResponse`. Also check that `pickPackResponse.status` is present — it is required within the nested object. The `confirmationId` should be auto-generated by the pre-request script; if it's missing, check that the Console is open and the script ran.

***

**`400 Bad Request` on Step 3** Check the `errors` object in the response body for the specific field. Common causes: `carrierId` contains a non-numeric string, `packageWeight.weightUnit` is not `KG` or `LB`, `packageDimensions.dimensionUnit` is not `CM` or `IN`, or `items[].itemStatus` is set to something other than `Shipped`.

***

**`500 Internal Server Error` on any step** A platform-side error. Wait 30–60 seconds and retry. If the error persists, contact your eShopWorld account team with the timestamp of the request.

***

**Console shows nothing after sending** The Console must be open before the request is sent. Open it via **View → Postman Console** (`Cmd + Alt + C` on Mac / `Ctrl + Alt + C` on Windows/Linux), then re-run the request.

***

**`confirmationId` is empty in the Step 2 body** The pre-request script generates the ID before the request sends — if it is missing, the script may not have run. Check the Console for the `Generated confirmationId:` log line. If it's absent, the script may have encountered a syntax error. Open the **Pre-request Script** tab on Step 2 to inspect it. As a workaround, manually set a unique value in the `confirmationId` **Current Value** in the Variables tab.

***

**The pre-request summary in Step 1 warns about Quarantine or Suspended SKUs** This is a diagnostic warning, not a blocker — the request will still send. Quarantine and Suspended SKUs are valid `articleStatus` values. The warning is to confirm the statuses are intentional. If they are, proceed normally. If a SKU should be `Active`, update its `articleStatus` in the body before sending.

***

**`{{$timestamp}}` or `{{$isoTimestamp}}` appears literally in the request body** These are Postman built-in dynamic variables. If they are not being resolved, confirm you are using Postman desktop or the current web version — very old versions of Postman may not support them. As a workaround, replace them with static values: a Unix timestamp (e.g. `1714556400`) for `shipmentId` and an ISO 8601 string (e.g. `2024-05-01T10:00:00Z`) for `shipmentDateTime`.


---

# 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/postman-collection/3pl-fulfilment-flow.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.
