> 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/returns-api/resources/postman-collection/shopper-return-journey.md).

# Shopper Return Journey

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

This Postman collection covers the **Shopper Return Journey** — the complete sequence of API calls that drives a customer-facing returns experience using the ESW Global Returns Platform (GRP) API v1.0.

{% file src="/files/0Qjn9bztkqETvUaenrEr" %}

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

It models exactly what happens when a shopper initiates a return: checking eligibility, discovering available return methods, finding a nearby drop-off point, registering the return, and finally retrieving a label to send the parcel back.

The seven steps run in order:

```ruby
Country Config → Outbound Order → Return Methods → Drop-off Locations
    → Create Return Order → Get Return Order → Get Return Document
```

Two variables — `carrierServiceRouteId` and `returnOrderNumber` — are populated automatically by test scripts as you progress through the steps, so the journey flows without manual data entry between requests.

***

### 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_GRP_Shopper_Return_Journey.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 GRP Returns API — Shopper Return Journey
```

{% 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`               | GRP API base URL                      | `https://returns-oms-api.sandbox.eshopworld.com` | No            |
| `token`                 | Bearer JWT access token               | `eyJhbGci...`                                    | No            |
| `identifier`            | GRP brand/tenant identifier           | `acme-brand`                                     | No            |
| `countryIso`            | Shopper's delivery country (ISO 3166) | `DE`                                             | No            |
| `orderReference`        | Retailer's outbound order reference   | `ORD-2024-00001`                                 | No            |
| `returnOrderNumber`     | Leave blank — auto-saved from Step 5  | *(empty)*                                        | Yes — Step 5  |
| `documentType`          | Return document type                  | `ReturnLabel`                                    | No            |
| `carrierServiceRouteId` | Leave blank — auto-saved from Step 3  | *(empty)*                                        | Yes — Step 3  |

Click **Save** (`Cmd + S` on Mac / `Ctrl + S` on Windows).

#### The `identifier` variable

The `identifier` appears in the path of every single request — it is the most important variable to set correctly. It identifies your specific GRP brand or tenant. Every URL in the collection follows this pattern:

```
{{baseUrl}}/{{identifier}}/...
```

If requests are returning `403 Forbidden` or `404 Not Found` on every step, the `identifier` is the first thing to verify.

#### Auto-managed variables

**`carrierServiceRouteId`** — After Step 3 runs successfully, the test script reads the first available return method from the response and saves its `carrierServiceRoute.id` to this variable. Step 4 uses it automatically as the `CarrierServiceRouteId` query parameter. If you want to search locations for a different method than the first one returned, update this variable manually before running Step 4.

**`returnOrderNumber`** — After Step 5 runs successfully, the test script reads `returnOrderNumber` from the response and saves it. Steps 6 and 7 use it automatically in their path. You never need to set this manually.

***

### Authentication

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

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

**To verify:**

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

Individual requests show **Inherit auth from parent** in their Authorization tab — they all use the collection-level token.

**Refreshing an expired token:**

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

***

### The Shopper Return Journey

{% code expandable="true" %}

```ruby
┌──────────────────────────────────────────────────────────────────────┐
│                                                                      │
│  Step 1 — Get Country Configuration          GET                    │
│  Is returns enabled for this brand + country?                        │
│  What reason codes and locales are available?  → 200 OK             │
│                           │                                          │
│                           ▼                                          │
│  Step 2 — Retrieve Outbound Order            GET                    │
│  Is the order eligible for return?                                   │
│  Which items can the shopper return?           → 200 OK             │
│                           │                                          │
│                           ▼                                          │
│  Step 3 — Search Return Methods              POST                   │
│  What return options are available?                                  │
│  (Postal, PUDO, Collections)                   → 200 OK             │
│  ↳ carrierServiceRouteId auto-saved                                  │
│                           │                                          │
│              ┌────────────┴────────────┐                             │
│              │ PUDO or Collections?    │ Postal only?                │
│              ▼                         ▼                             │
│  Step 4 — Find Drop-off Locations    (skip)                          │
│  Where can the shopper drop off        GET → 200 OK                  │
│  the parcel?                                                         │
│  ↳ Use location id in Step 5                                         │
│              │                         │                             │
│              └────────────┬────────────┘                             │
│                           ▼                                          │
│  Step 5 — Create Return Order          POST                         │
│  Register the return with items,                                     │
│  reason codes, method, drop-off        → 201 Created                │
│  ↳ returnOrderNumber auto-saved                                      │
│                           │                                          │
│                           ▼                                          │
│  Step 6 — Get Return Order             GET                          │
│  Confirm status, check label                                         │
│  readiness before downloading          → 200 OK                     │
│                           │                                          │
│                           ▼                                          │
│  Step 7 — Get Return Document          GET                          │
│  Download base64-encoded label                                       │
│  (ReturnLabel or LastMileLabel)        → 200 OK                     │
│                                                                      │
└──────────────────────────────────────────────────────────────────────┘
```

{% endcode %}

#### Step 4 is conditional

Step 4 is only needed when the return method is `PUDO` or `Collections`. The Step 3 test script tells you explicitly:

```ruby
ℹ PUDO available — run Step 4 to find drop-off locations for the shopper.
```

or:

```ruby
ℹ No PUDO method — Step 4 can be skipped. Proceed directly to Step 5.
```

#### What feeds into what

Each step produces information that the next step needs:

| From   | Produces                                      | Used in                           |
| ------ | --------------------------------------------- | --------------------------------- |
| Step 1 | Reason codes list                             | Step 5 `returnItems[].reasonCode` |
| Step 2 | Item `productCode` and `lineItemId`           | Steps 3 and 5 `returnItems`       |
| Step 3 | `carrierServiceRoute.id` (auto-saved)         | Step 4 `CarrierServiceRouteId`    |
| Step 3 | `returnMethod`, `paidBy`, `carrierIdentifier` | Step 5 body                       |
| Step 4 | Location `id` and address details             | Step 5 `dropOffLocation`          |
| Step 5 | `returnOrderNumber` (auto-saved)              | Steps 6 and 7 path                |

***

### Step 1 — Get Country Configuration

**Method:** `GET` **URL:** `{{baseUrl}}/{{identifier}}/configuration/countries/{{countryIso}}`

#### What it does

Retrieves the brand and country-level configuration for returns. Run this first to confirm returns are enabled for the shopper's country and to discover what reason codes they can select during the return process.

#### No body required

This is a `GET` request. All parameters are in the URL path, resolved from collection variables.

#### Key response fields

| Field                                               | What it tells you                                                                                |
| --------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| `returnsOmsEnabled`                                 | If `false`, returns are disabled — do not proceed                                                |
| `portalSettings.customerReturnReasonCodes`          | The reason codes available to the shopper (e.g. `WRONG_SIZE`, `DAMAGED`) — use in Step 5         |
| `portalSettings.returnsPortalConfigurationSettings` | Portal-level settings such as return window in days                                              |
| `portalSettings.blockDefectiveDangerGoods`          | If `true`, dangerous/defective goods cannot be returned                                          |
| `locales`                                           | Supported language codes for this country — use `cultureInfoCode` in Step 5 `cultureLanguageIso` |

#### Saved example responses

| Example                        | When it applies                                          |
| ------------------------------ | -------------------------------------------------------- |
| `200 OK — Configuration Found` | Returns enabled, reason codes and locales available      |
| `404 Not Found`                | No configuration for this identifier/country combination |

#### Test results

| Test                                   | Passes when                   |
| -------------------------------------- | ----------------------------- |
| `Country configuration returned (200)` | Status is 200                 |
| `Returns OMS is enabled`               | `returnsOmsEnabled` is `true` |

> The second test — checking `returnsOmsEnabled` is `true` — will fail red if returns are disabled for this brand/country. This is a deliberate gate: attempting the rest of the journey without returns being enabled will fail at Step 5.

#### Console output

```
Brand:               ACME Fashion (ACM)
Country:             DE
Returns enabled:     true
Return reason codes: 4
  WRONG_SIZE — Wrong size
  DAMAGED — Item arrived damaged
  NOT_AS_DESCRIBED — Not as described
  CHANGED_MIND — Changed my mind
Supported locales:   de-DE, en-GB
✓ Returns enabled. Proceed to Step 2.
```

If returns are disabled:

```
⚠ Returns are disabled for this brand/country. Shopper cannot initiate a return.
```

***

### Step 2 — Retrieve Outbound Order

**Method:** `GET` **URL:** `{{baseUrl}}/{{identifier}}/orders/{{orderReference}}?orderItems=true`

#### What it does

Fetches the original outbound order to confirm it is eligible for return and to get the full list of items with their `productCode` and `lineItemId` — both needed for Steps 3 and 5.

#### The `orderItems` query parameter

The `?orderItems=true` query parameter is always enabled. Without it, the response omits the item details and you cannot build the return items payload. Do not disable it.

#### Key response fields to check

| Field                              | What to look for                                              |
| ---------------------------------- | ------------------------------------------------------------- |
| `returnPeriodExpired`              | `true` = shopper cannot initiate a return                     |
| `noOfArticlesAvailableForReturn`   | Must be > 0 to proceed                                        |
| `noOfArticlesUnavailableForReturn` | Items blocked due to `prohibitedForReturns` or `personalized` |
| `items[].productCode`              | Required for Steps 3 and 5                                    |
| `items[].lineItemId`               | Required for Steps 3 and 5                                    |
| `items[].prohibitedForReturns`     | `true` = this item cannot be returned                         |
| `items[].personalized`             | `true` = this item cannot be returned                         |
| `returnOrders`                     | Existing return orders for this order                         |

#### Test results

| Test                                           | Passes when                          |
| ---------------------------------------------- | ------------------------------------ |
| `Order retrieved (200)`                        | Status is 200                        |
| `Return period has not expired`                | `returnPeriodExpired` is `false`     |
| `At least one article is available for return` | `noOfArticlesAvailableForReturn` > 0 |

Both eligibility tests are deliberate gates. If either fails, the shopper cannot complete a return and subsequent steps should not be run.

#### Console output

```
Order country:              DE
Order date:                 2024-04-01T00:00:00Z
Return period expired:      false
Available for return:       2
Unavailable for return:     0
Existing return orders:     0
Order items (2):
  WMN-DRESS-001 (lineItemId: LI-001) — ✓ eligible
  KID-SNEAKER-07 (lineItemId: LI-002) — ✓ eligible
✓ Order is eligible for return. Proceed to Step 3.
```

***

### Step 3 — Search Return Methods

**Method:** `POST` **URL:** `{{baseUrl}}/{{identifier}}/return-methods/{{orderReference}}`

#### What it does

Searches for return methods available to the shopper for the items they want to return. The response tells you which methods (Postal, PUDO, Collections) are available, who pays, and which carrier service route applies. This information is used to build the Step 5 payload.

#### The request body

{% code expandable="true" %}

```json
{
  "returnItems": [
    {
      "id": "LI-001",
      "productCode": "WMN-DRESS-001",
      "lineItemId": "LI-001"
    },
    {
      "id": "LI-002",
      "productCode": "KID-SNEAKER-07",
      "lineItemId": "LI-002"
    }
  ]
}
```

{% endcode %}

Replace the sample values with the eligible items from the Step 2 response. Use `productCode` and `lineItemId` from `items[]`. Set `id` to match `lineItemId` — it must be unique within the array.

**Partial return:** Include only the items the shopper wants to return, not the full order.

#### Key response fields

| Field                                             | What it tells you                          |
| ------------------------------------------------- | ------------------------------------------ |
| `returnMethods[].returnMethod`                    | `Postal`, `PUDO`, or `Collections`         |
| `returnMethods[].paidBy`                          | `Unpaid`, `Retailer`, or `Shopper`         |
| `returnMethods[].carrierServiceRoute.id`          | The `carrierServiceRouteId` for Step 4     |
| `returnMethods[].carrierServiceRoute.carrierCode` | Carrier SCAC code                          |
| `selectedReturnAdminCentreCode`                   | The warehouse that will receive the return |

#### Auto-saved variable

After a successful response, the test script reads the first method's `carrierServiceRoute.id` and saves it to `{{carrierServiceRouteId}}`. Step 4 uses this automatically.

If you want to search locations for a different return method (e.g. the second method returned), manually update `carrierServiceRouteId` in the Variables tab before running Step 4.

#### Test results

| Test                                   | Passes when                                  |
| -------------------------------------- | -------------------------------------------- |
| `Return methods returned (200)`        | Status is 200                                |
| `At least one return method available` | `returnMethods` array has at least one entry |

#### Console output

```
Origin country:      IE
Destination country: DE
Return admin centre: IEDUB
Available methods (2):
  Method: Postal | Paid by: Retailer | Carrier: DHLE | RouteId: ROUTE-001
  Method: PUDO   | Paid by: Retailer | Carrier: DHLE | RouteId: ROUTE-002
✓ carrierServiceRouteId saved for Step 4: ROUTE-001
  Update this variable if you want to search locations for a different method.
ℹ PUDO available — run Step 4 to find drop-off locations for the shopper.
```

***

### Step 4 — Find Drop-off Locations

**Method:** `GET` **URL:** `{{baseUrl}}/location/?identifier={{identifier}}&CountryCode={{countryIso}}&PostalCode=10115&RouteType=Pudo&CarrierServiceRouteId={{carrierServiceRouteId}}&MaxDistanceInKms=5&MaxLocations=10`

#### When to run this step

Only run Step 4 if the shopper's chosen return method is `PUDO` or `Collections`. The Console output from Step 3 tells you whether to run it. If the return method is `Postal` only, skip directly to Step 5.

#### Query parameters

The **Params** tab shows ten parameters — five required (active) and five optional (some disabled by default):

**Required — active by default:**

| Parameter               | Value                       | Description                                         |
| ----------------------- | --------------------------- | --------------------------------------------------- |
| `identifier`            | `{{identifier}}`            | GRP brand/tenant identifier                         |
| `CountryCode`           | `{{countryIso}}`            | Shopper's country code                              |
| `PostalCode`            | `10115`                     | **Update this** to the shopper's actual postal code |
| `RouteType`             | `Pudo`                      | Location type — `Pudo` or `Store`                   |
| `CarrierServiceRouteId` | `{{carrierServiceRouteId}}` | Auto-populated from Step 3                          |

**Optional — disabled by default (enable in Params tab):**

| Parameter   | Description                                |
| ----------- | ------------------------------------------ |
| `City`      | Further narrows results to a specific city |
| `Latitude`  | Latitude for geo-coordinate search         |
| `Longitude` | Longitude for geo-coordinate search        |

**Optional — active by default:**

| Parameter          | Default | Description                         |
| ------------------ | ------- | ----------------------------------- |
| `MaxDistanceInKms` | `5`     | Maximum search radius in kilometers |
| `MaxLocations`     | `10`    | Maximum number of results returned  |

**Before sending, update `PostalCode`** to the shopper's actual postal code. The current value `10115` is the sample Berlin postal code used in the example.

To enable an optional parameter, click the checkbox to its left in the Params tab.

#### Key response fields

Each location in `locations[]` includes:

| Field                                           | Description                                              |
| ----------------------------------------------- | -------------------------------------------------------- |
| `id`                                            | Location identifier — use in Step 5 `dropOffLocation.id` |
| `name`                                          | Display name (e.g. `DHL Packstation 101`)                |
| `address1`, `city`, `postalCode`, `countryCode` | Address fields — use in Step 5 `dropOffLocation`         |
| `distanceInKms`                                 | Distance from the searched postal code                   |
| `openingTimesDescription`                       | Human-readable opening hours                             |
| `latitude`, `longitude`                         | Coordinates for map display                              |
| `carrierServiceRouteId`                         | Matches the route used in the search                     |

#### What to do with the results

Present the locations to the shopper. When the shopper selects one, copy its `id` and address fields into the `dropOffLocation` object in Step 5.

#### Test results

| Test                             | Passes when                              |
| -------------------------------- | ---------------------------------------- |
| `Locations returned (200)`       | Status is 200                            |
| `At least one location returned` | `locations` array has at least one entry |

#### Console output

```
Drop-off locations found: 2
  [1] DHL Packstation 101 — Friedrichstraße 100, Berlin (0.8 km)
      id: LOC-BERLIN-001 | type: Pudo
  [2] DHL Packstation 205 — Unter den Linden 45, Berlin (1.4 km)
      id: LOC-BERLIN-002 | type: Pudo

ℹ Use the location id and address from above to populate dropOffLocation in Step 5.
```

***

### Step 5 — Create Return Order

**Method:** `POST` **URL:** `{{baseUrl}}/{{identifier}}/orders/{{orderReference}}/return-orders`

#### What it does

Creates the return order. This is the core step of the shopper journey — it registers the shopper's intent to return, records the reason codes for each item, assigns the carrier and return method, and generates the `returnOrderNumber` needed for the label.

The test script automatically saves `returnOrderNumber` to the collection variable after a `201 Created` response.

#### The request body

{% code expandable="true" %}

```json
{
  "consumerEmailAddress": "anna.muller@example.com",
  "cultureLanguageIso": "de-DE",
  "returnItems": [
    {
      "id": "LI-001",
      "productCode": "WMN-DRESS-001",
      "lineItemId": "LI-001",
      "reasonCode": "WRONG_SIZE",
      "reasonComment": "Ordered M but it fits like S — need a bigger size."
    },
    {
      "id": "LI-002",
      "productCode": "KID-SNEAKER-07",
      "lineItemId": "LI-002",
      "reasonCode": "NOT_AS_DESCRIBED",
      "reasonComment": "Colour is different from the website photo."
    }
  ],
  "returnMethod": "PUDO",
  "returnType": "Normal",
  "isPaperlessRoute": false,
  "paidBy": "Retailer",
  "carrierIdentifier": "DHLE",
  "dropOffLocation": {
    "id": "LOC-BERLIN-001",
    "type": "Pudo",
    "name": "DHL Packstation 101",
    "address1": "Friedrichstraße 100",
    "city": "Berlin",
    "postcode": "10117",
    "country": "DE"
  },
  "weight": {
    "weightTotal": 0.6,
    "weightUnit": "Kg"
  }
}
```

{% endcode %}

**Before sending, update:**

| Field                       | Where to get the value                                        |
| --------------------------- | ------------------------------------------------------------- |
| `consumerEmailAddress`      | Shopper's email address                                       |
| `cultureLanguageIso`        | From Step 1 `locales[].cultureInfoCode`                       |
| `returnItems[].productCode` | From Step 2 `items[].productCode`                             |
| `returnItems[].lineItemId`  | From Step 2 `items[].lineItemId`                              |
| `returnItems[].reasonCode`  | From Step 1 `portalSettings.customerReturnReasonCodes[].code` |
| `returnMethod`              | From Step 3 `returnMethods[].returnMethod`                    |
| `paidBy`                    | From Step 3 `returnMethods[].paidBy`                          |
| `carrierIdentifier`         | From Step 3 `returnMethods[].carrierServiceRoute.carrierCode` |
| `dropOffLocation`           | From Step 4 location `id` and address fields                  |
| `weight.weightTotal`        | Estimated weight of the return parcel                         |

#### `dropOffLocation` — when to include it

Include `dropOffLocation` only when `returnMethod` is `PUDO` or `Collections`. For `Postal` returns, remove the `dropOffLocation` object from the body entirely.

#### `isPaperlessRoute`

Set to `true` if the shopper will present a QR code at the drop-off point instead of printing a label. The Console will log a note if this is set to `true`. Step 7 may return a QR code document rather than a printable PDF label.

#### Enum values reference

**`returnMethod`**

```
Postal        Shopper posts the parcel using a printed label
PUDO          Shopper drops off at a pickup point (requires dropOffLocation)
Collections   Carrier collects from shopper's address (requires dropOffLocation)
```

**`returnType`**

```
Normal              Standard return
Undeliverable       Original delivery could not be completed
BlindReturn         Return without a prior return order
EUCoolingOff        EU cooling-off period return (regulatory)
```

**`paidBy`**

```
Unpaid      Shopper pays the return postage
Retailer    Retailer covers the return postage
Shopper     Shopper pays (explicit)
```

**`weight.weightUnit`**

```
Kg    Lb
```

#### Saved example responses

| Example                                | Code | When it applies                                        |
| -------------------------------------- | ---- | ------------------------------------------------------ |
| `201 Created — Return Order Created`   | 201  | Normal success                                         |
| `409 Conflict — Already Exists`        | 409  | A return order already exists for this order reference |
| `400 Bad Request — Validation Failure` | 400  | A required field is missing or invalid                 |

#### Test results

| Test                             | Passes when                              |
| -------------------------------- | ---------------------------------------- |
| `Return order created (201)`     | Status is 201                            |
| `Response has returnOrderNumber` | Body has a non-empty `returnOrderNumber` |

#### Console output

```
✓ returnOrderNumber saved: RTN-2024-98765
Return order number:   RTN-2024-98765
Return method:         PUDO
Return type:           Normal
Paid by:               Retailer
Carrier:               DHLE
Paperless route:       false
Items in return:       2
Drop-off location:     DHL Packstation 101, Friedrichstraße 100, Berlin

ℹ Proceed to Step 6 to retrieve the return order, then Step 7 to get the label.
```

***

### Step 6 — Get Return Order

**Method:** `GET` **URL:** `{{baseUrl}}/{{identifier}}/orders/{{orderReference}}/return-orders/{{returnOrderNumber}}`

#### What it does

Retrieves the return order created in Step 5. Use this to confirm the order is in a valid state before attempting to download the label in Step 7.

`returnOrderNumber` is auto-populated from Step 5 — no manual input needed.

#### Key response fields

| Field                            | What to look for                                     |
| -------------------------------- | ---------------------------------------------------- |
| `status`                         | Current status of the return order (see table below) |
| `returnAdminCentreCode`          | The warehouse that will receive the return           |
| `createdOn`                      | When the return order was created                    |
| `lastUpdated`                    | When the return order was last modified              |
| `returnItems[].returnItemStatus` | Per-item status                                      |

#### Return order status values

| Status                | Meaning                              | Safe to proceed to Step 7?       |
| --------------------- | ------------------------------------ | -------------------------------- |
| `Created`             | Return order registered ✓            | Yes                              |
| `Received`            | Parcel received at admin centre      | Yes                              |
| `OnHold`              | Return is held pending investigation | Contact support first            |
| `Released`            | Hold has been released               | Yes                              |
| `Processed`           | Return has been processed            | Yes                              |
| `AddedToManifest`     | Added to outbound manifest           | Yes                              |
| `SentToRetailer`      | Dispatched to retailer               | Yes                              |
| `ReceivedByRetailer`  | Retailer has received the return     | Yes                              |
| `RemovedFromManifest` | Removed from manifest                | Check with support               |
| `Cancelled`           | Return order cancelled               | No — label will not be available |
| `Refunded`            | Shopper has been refunded            | Yes                              |

#### Test results

| Test                           | Passes when             |
| ------------------------------ | ----------------------- |
| `Return order retrieved (200)` | Status is 200           |
| `Response has status field`    | Body has a `status` key |

#### Console output

```
Return order number:   RTN-2024-98765
Status:                Created
Return method:         PUDO
Return admin centre:   IEDUB
Created on:            2024-05-01T10:15:00Z
Last updated:          2024-05-01T10:15:00Z
Items in return:       2
✓ Return order created. Proceed to Step 7 to retrieve the label.
```

Status-based warnings:

```
⚠ Return order has been cancelled. Label retrieval in Step 7 may fail.
⚠ Return order is on hold. Contact eShopWorld support before proceeding.
```

***

### Step 7 — Get Return Document

**Method:** `GET` **URL:** `{{baseUrl}}/{{identifier}}/return-orders/{{returnOrderNumber}}/{{documentType}}`

#### What it does

Retrieves the return label document for the shopper. The label is returned as a **base64-encoded string** in the `label` field of the response. The shopper prints it (for a label-based return) or scans it as a QR code (for a paperless return) at the drop-off point.

Both `returnOrderNumber` and `documentType` are resolved from collection variables. `documentType` defaults to `ReturnLabel`.

#### `documentType` values

| Value           | Description                                                            |
| --------------- | ---------------------------------------------------------------------- |
| `ReturnLabel`   | Standard return shipping label — shopper prints and attaches to parcel |
| `LastMileLabel` | Last-mile carrier label used in specific carrier integrations          |

To switch between document types, update the `documentType` **Current Value** in the Variables tab before sending.

#### `documentStatus` values

The `documentStatus` field in the response tells you whether the label is ready:

| Status       | Meaning                                              | What to do                           |
| ------------ | ---------------------------------------------------- | ------------------------------------ |
| `Ready`      | Label generated — `label` field contains base64 data | Decode and present to shopper        |
| `Processing` | Label generation still in progress                   | Wait 10–30 seconds and re-run Step 7 |
| `Failed`     | Label generation failed                              | Contact eShopWorld support           |

#### Decoding the label

The `label` field is a base64-encoded string. To use it:

**In a browser:**

{% tabs %}
{% tab title="JavaScript" %}
{% code expandable="true" %}

```javascript
const base64 = response.label;
const bytes = atob(base64);
// Convert to a Blob for display or download
const blob = new Blob([bytes], { type: response.contentType });
const url = URL.createObjectURL(blob);
window.open(url); // open PDF in new tab
```

{% endcode %}
{% endtab %}

{% tab title="Node.js" %}
{% code expandable="true" %}

```javascript
const buffer = Buffer.from(response.label, 'base64');
fs.writeFileSync('return-label.pdf', buffer);
```

{% endcode %}
{% endtab %}

{% tab title="Python" %}
{% code expandable="true" %}

```python
import base64
label_bytes = base64.b64decode(response['label'])
with open('return-label.pdf', 'wb') as f:
    f.write(label_bytes)
```

{% endcode %}
{% endtab %}
{% endtabs %}

The `contentType` field tells you the file format — typically `application/pdf`.

#### Carrier tracking information

When `documentStatus` is `Ready`, the response also includes `carrierInfoDetails`:

| Field                      | Description                                   |
| -------------------------- | --------------------------------------------- |
| `carrierId`                | ESW carrier identifier                        |
| `carrierName`              | Carrier display name (e.g. `DHL Express`)     |
| `carrierReference`         | Carrier's reference number for this parcel    |
| `carrierTrackingReference` | Tracking number — present this to the shopper |

#### Saved example responses

| Example                           | Code | When it applies                                  |
| --------------------------------- | ---- | ------------------------------------------------ |
| `200 OK — Label Ready`            | 200  | `documentStatus` is `Ready` — label is available |
| `200 OK — Label Still Processing` | 200  | `documentStatus` is `Processing` — retry shortly |
| `404 Not Found`                   | 404  | Return order or document not found               |

#### Test results

| Test                              | Passes when                                            |
| --------------------------------- | ------------------------------------------------------ |
| `Document request returned (200)` | Status is 200                                          |
| `Document is Ready or Processing` | `documentStatus` is `Ready`, `Processing`, or `Failed` |

#### Console output — label ready

```
Document type:    ReturnLabel
Document status:  Ready
Content type:     application/pdf
Document ID:      doc-abc123
Carrier:          DHL Express
Carrier ref:      1Z999AA10123456784
Tracking ref:     JVBER123456DE
✓ Label ready. base64 label length: 84320 chars.
  Decode the label field to get the PDF/PNG bytes.

=== Shopper Return Journey Complete ===
Order reference:       ORD-2024-00001
Return order number:   RTN-2024-98765
Steps: Config → Order → Methods → Locations → Create → Get → Label
```

#### Console output — still processing

```
⏳ Label is still processing. Wait 10–30 seconds and re-run Step 7.
```

***

### Run Collection

The Collection Runner executes all seven steps automatically in sequence.

#### Opening the runner

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

#### Recommended settings

| Setting                             | Value     | Why                                                                                                                                      |
| ----------------------------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| **Iterations**                      | `1`       | One shopper journey per run                                                                                                              |
| **Delay**                           | `1000` ms | Gives the GRP time to process Step 5 before Steps 6 and 7 run                                                                            |
| **Persist responses for a session** | ✓ On      | Lets you review each response after the run                                                                                              |
| **Stop run if an error occurs**     | ✓ On      | Stops if eligibility checks fail (Step 1 `returnsOmsEnabled`, Step 2 return period) — prevents creating a return for an ineligible order |
| **Keep variable values**            | ✓ On      | Ensures `returnOrderNumber` set in Step 5 carries through to Steps 6 and 7                                                               |

#### Handling Step 4

Step 4 is conditional. If the return method is Postal-only, Step 4 will fail because the pre-filled postal code won't find PUDO locations. Two options:

* **Disable Step 4** in the runner before starting if you know the return method is Postal
* **Let it run** — a Step 4 failure does not affect Steps 5–7 if you have `Stop run if an error occurs` turned off for that step specifically

The safest approach for a mixed test run is to disable Step 4 in the runner and run it manually when needed.

***

### 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 | `Country configuration returned (200)`         | Status is 200                                 |
| Step 1 | `Returns OMS is enabled`                       | `returnsOmsEnabled` is `true`                 |
| Step 2 | `Order retrieved (200)`                        | Status is 200                                 |
| Step 2 | `Return period has not expired`                | `returnPeriodExpired` is `false`              |
| Step 2 | `At least one article is available for return` | `noOfArticlesAvailableForReturn` > 0          |
| Step 3 | `Return methods returned (200)`                | Status is 200                                 |
| Step 3 | `At least one return method available`         | `returnMethods` length > 0                    |
| Step 4 | `Locations returned (200)`                     | Status is 200                                 |
| Step 4 | `At least one location returned`               | `locations` length > 0                        |
| Step 5 | `Return order created (201)`                   | Status is 201                                 |
| Step 5 | `Response has returnOrderNumber`               | Body has non-empty `returnOrderNumber`        |
| Step 6 | `Return order retrieved (200)`                 | Status is 200                                 |
| Step 6 | `Response has status field`                    | Body has a `status` key                       |
| Step 7 | `Document request returned (200)`              | Status is 200                                 |
| Step 7 | `Document is Ready or Processing`              | `documentStatus` is one of three valid values |

#### 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   | Key console output                                                                                                 |
| ------ | ------------------------------------------------------------------------------------------------------------------ |
| Step 1 | Brand name, country, enabled status, all reason codes and descriptions, supported locales                          |
| Step 2 | Return period status, article counts, per-item eligibility with ✓ / ✗ indicators                                   |
| Step 3 | All available methods with carrier and route ID; PUDO availability note; `carrierServiceRouteId` save confirmation |
| Step 4 | Numbered list of locations with name, address and distance; instruction to use location `id` in Step 5             |
| Step 5 | Return order number (saved), method, type, paid by, carrier, paperless flag, drop-off location name                |
| Step 6 | Status, admin centre, timestamps, item count; status-specific proceed/warn guidance                                |
| Step 7 | Document status, content type, carrier tracking reference; base64 label length; full journey summary on completion |

***

### Field Reference

#### Step 3 — `returnItems` (Search Return Methods)

| Field         | Required | Description                                                    |
| ------------- | -------- | -------------------------------------------------------------- |
| `id`          | Yes      | Unique identifier within the array — set to match `lineItemId` |
| `productCode` | Yes      | From Step 2 `items[].productCode`                              |
| `lineItemId`  | Yes      | From Step 2 `items[].lineItemId`                               |

#### Step 5 — `CreateReturnOrderWebRequest`

| Field                  | Required | Description                                                              |
| ---------------------- | -------- | ------------------------------------------------------------------------ |
| `consumerEmailAddress` | No       | Shopper's email address                                                  |
| `cultureLanguageIso`   | No       | ISO language code from Step 1 `locales[].cultureInfoCode` (e.g. `de-DE`) |
| `returnItems`          | No       | Array of items to return (see below)                                     |
| `returnMethod`         | No       | `Postal` · `PUDO` · `Collections`                                        |
| `returnType`           | No       | `Normal` · `Undeliverable` · `BlindReturn` · `EUCoolingOff`              |
| `isPaperlessRoute`     | No       | `true` for QR code drop-off instead of printed label                     |
| `paidBy`               | No       | `Unpaid` · `Retailer` · `Shopper`                                        |
| `carrierIdentifier`    | No       | Carrier SCAC code from Step 3 `carrierServiceRoute.carrierCode`          |
| `dropOffLocation`      | No       | Required for PUDO and Collections — populate from Step 4                 |
| `weight.weightTotal`   | No       | Estimated weight of return parcel                                        |
| `weight.weightUnit`    | No       | `Kg` or `Lb`                                                             |
| `shippingReference`    | No       | Pre-assigned shipping reference if known                                 |
| `barcodeReference`     | No       | Pre-assigned barcode reference if known                                  |
| `carrierReference`     | No       | Pre-assigned carrier reference if known                                  |

#### Step 5 — `returnItems` array

| Field           | Required | Description                                                    |
| --------------- | -------- | -------------------------------------------------------------- |
| `id`            | No       | Unique identifier within the array — set to match `lineItemId` |
| `productCode`   | No       | From Step 2 `items[].productCode`                              |
| `lineItemId`    | No       | From Step 2 `items[].lineItemId`                               |
| `reasonCode`    | No       | From Step 1 `portalSettings.customerReturnReasonCodes[].code`  |
| `reasonComment` | No       | Shopper's free-text comment explaining the reason              |

#### Step 5 — `dropOffLocation` object

| Field       | Description                                        |
| ----------- | -------------------------------------------------- |
| `id`        | From Step 4 `locations[].id`                       |
| `type`      | From Step 4 `locations[].type` — `Pudo` or `Store` |
| `name`      | From Step 4 `locations[].name`                     |
| `address1`  | From Step 4 `locations[].address1`                 |
| `address2`  | From Step 4 `locations[].address2` (if present)    |
| `address3`  | From Step 4 `locations[].address3` (if present)    |
| `city`      | From Step 4 `locations[].city`                     |
| `postcode`  | From Step 4 `locations[].postalCode`               |
| `sortcode`  | From Step 4 `locations[].sortCode` (if present)    |
| `region`    | From Step 4 `locations[].region` (if present)      |
| `country`   | From Step 4 `locations[].countryCode`              |
| `telephone` | From Step 4 `locations[].telephone` (if present)   |

***

### Common Scenarios

#### "Standard PUDO return — full flow"

1. Set `identifier`, `countryIso`, and `orderReference` in Variables
2. Run Step 1 — note reason codes for Step 5
3. Run Step 2 — note `productCode` and `lineItemId` per item
4. Run Step 3 — PUDO method returned, `carrierServiceRouteId` auto-saved
5. Update Step 4 `PostalCode` to shopper's postal code, run Step 4 — note location `id`
6. In Step 5 body, update items, reason codes, `dropOffLocation` from Step 4 result
7. Run Step 5 — `returnOrderNumber` auto-saved
8. Run Step 6 — confirm status is `Created`
9. Run Step 7 — decode `label` field to get PDF

***

#### "Postal return — skip Step 4"

If Step 3 returns only `Postal` and no `PUDO`:

1. Run Steps 1–3 as normal
2. **Skip Step 4** — no drop-off location needed
3. In Step 5, set `returnMethod` to `Postal` and remove the `dropOffLocation` object from the body
4. Continue with Steps 5–7

***

#### "Partial return — shopper returning one of two items"

In Step 3, include only the item the shopper is returning in `returnItems`. In Step 5, include only that item. The system records a partial return against the order.

***

#### "Paperless return — shopper uses QR code"

In Step 5, set `isPaperlessRoute` to `true`. The Console will note this. In Step 7, the `label` field may contain a QR code image rather than a PDF label, and `contentType` may be `image/png` instead of `application/pdf`.

***

#### "Label is still Processing in Step 7"

Label generation is asynchronous. If `documentStatus` is `Processing`:

1. Wait 15–30 seconds
2. Re-run Step 7
3. Repeat until `documentStatus` is `Ready`

Do not re-run Steps 5 or 6 — the return order already exists.

***

#### "Testing for multiple countries"

Change `countryIso` in the Variables tab between runs. Each country may return different reason codes, return methods, and supported locales in Step 1. Run the full seven steps for each country to verify the configuration.

***

### Troubleshooting

**`403 Forbidden` on every request** The `identifier` variable is wrong. This is the most common setup error. Verify the exact identifier string with your ESW account team — it is case-sensitive and appears in every URL path.

***

**`404 Not Found` on Step 1** No returns configuration exists for this `identifier` and `countryIso` combination. Check both values and confirm with your ESW account team that the brand/country has been configured in the GRP.

***

**Step 1 test `Returns OMS is enabled` fails** `returnsOmsEnabled` is `false` — returns have been disabled for this brand/country in the GRP. Contact your ESW account team to enable returns before proceeding.

***

**`404 Not Found` on Step 2** The `orderReference` does not exist in the GRP. Confirm the order was ingested correctly via the Outbound & Shipment Ingestion workflow. The GRP cannot create return orders for orders it has not received.

***

**Step 2 tests fail — return period expired or no eligible articles** `returnPeriodExpired` is `true` or `noOfArticlesAvailableForReturn` is `0`. These are business-rule failures, not API errors. The shopper cannot initiate a return for this order. Do not proceed.

***

**`400 Bad Request` on Step 5** A field is missing or invalid. Common causes:

* `reasonCode` not in the allowed list from Step 1 — verify exact code strings
* `returnMethod` casing wrong — must be `Postal`, `PUDO`, or `Collections` (capital first letter)
* `cultureLanguageIso` format wrong — must be `language-COUNTRY` (e.g. `de-DE`, not `de` or `DE`)
* `dropOffLocation` missing when `returnMethod` is `PUDO` or `Collections`
* `dropOffLocation` present when `returnMethod` is `Postal` — remove it for postal returns

***

**`409 Conflict` on Step 5** A return order already exists for this `orderReference`. Each order can only have one active return order. Retrieve the existing one using Step 6 or the Returns Operations collection instead of creating a new one.

***

**`returnOrderNumber` is empty in Steps 6 and 7** Step 5 did not return `201` or the test script did not run. Check the Step 5 Test Results tab and Console. As a fallback, manually set `returnOrderNumber` **Current Value** in the Variables tab to the return order number from the Step 5 response body.

***

**Step 7 returns `404 Not Found`** Either the `returnOrderNumber` is wrong or the return order does not have a document associated with it yet. Run Step 6 first to confirm the return order exists and its status. If the status is `Created`, wait a moment and retry Step 7.

***

**`documentStatus` is `Failed` in Step 7** Label generation failed on the platform side. Contact ESW support with the `returnOrderNumber` and the `documentId` from the Step 7 response.

***

**Console shows nothing** Open the Console before running requests: **View → Postman Console** (`Cmd + Alt + C` on Mac / `Ctrl + Alt + C` on Windows/Linux). Console output is not retained from requests sent before it was opened.


---

# 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/returns-api/resources/postman-collection/shopper-return-journey.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.
