> 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/agents.md).

# AGENTS.md

Operating instructions for AI coding agents integrating with ESW APIs: discovery, environments, STS authentication, request conventions, safety rules, and per-API gotchas.

Instructions for AI coding agents (Claude Code, Copilot, Cursor, Codex and similar) that read these docs to build or operate an ESW integration. `llms.txt` tells you **what pages exist**. This page tells you **how to act**: which environment to use, how to authenticate, which calls are safe to run, and which rules cause most first-call failures.

Humans can read it too. It is a condensed, opinionated summary; when it conflicts with an API's own reference page, the reference page wins.

## 1. Rules that override everything else

1. **Default to sandbox.** Never call a production host unless the user explicitly asks for production in this session and has supplied production credentials.
2. **Never print, log, commit, or echo secrets.** Read `client_id`, `client_secret`, and tokens from environment variables or a secrets manager. Redact them in any output, including error messages and generated test fixtures.
3. **Ask before irreversible calls.** Anything in the "Irreversible" column of [Section 6](#6-operation-risk-classes) needs explicit user confirmation, even in sandbox.
4. **Do not invent fields, enums, or `metadataItems` names.** If a field is not in the OpenAPI spec or reference page, do not send it. `metadataItems` values must be agreed with ESW and can render as shopper-visible text.
5. **Do not construct ESW URLs the API returns.** Use `redirectUrl`, label URLs, and similar values exactly as returned.

## 2. How to read these docs

| Need                                  | Where                                                                                                                                                                                                                                                                                                |
| ------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Page index                            | `https://developer.esw.com/llms.txt`                                                                                                                                                                                                                                                                 |
| Any page as markdown                  | Append `.md` to its URL, e.g. `https://developer.esw.com/checkout-api/checkout-api/overview.md`                                                                                                                                                                                                      |
| Ask a question of the docs            | `GET https://developer.esw.com/index.md?ask=<question>&goal=<user_goal>`                                                                                                                                                                                                                             |
| OpenAPI specs (JSON)                  | `https://developer.esw.com/openapi/<api>.json`, see [OpenAPI specs](#openapi-specs) below                                                                                                                                                                                                            |
| Runnable end-to-end flows             | The Quick Start Guide: `https://developer.esw.com/getting-started/get-started-with-esw/quick-start-guide.md`                                                                                                                                                                                         |
| Docs MCP server (read-only search)    | `https://developer.esw.com/~gitbook/mcp` (HTTP transport)                                                                                                                                                                                                                                            |
| Docs through Context7                 | Library `/llmstxt/developer_esw_llms_txt` ([context7.com](https://context7.com/llmstxt/developer_esw_llms_txt)), if the user already runs the Context7 MCP server                                                                                                                                    |
| Install these rules as an agent skill | [esw-api-integration.zip](https://developer.esw.com/esw-api-integration.zip) or [SKILL.md](https://365533231-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FxduVPl4LIDP7RqqGRQT3%2Fuploads%2Fp2fuoruf9SO6fIFdlzBA%2FSKILL.md?alt=media) — see [Agent skill](#agent-skill) below |

Prefer, in order: the OpenAPI spec for field names and types, the API's **API Methods** and **Concepts** pages for behavior, then **Use Cases** for multi-call sequences. Each API also has a **Decision Tables** page; use it to pick the right endpoint before writing code.

### Agent skill

This page is also packaged as an [Agent Skills](https://agentskills.io/) skill, `esw-api-integration` (version 1.1). Install it so your coding agent applies these rules automatically, without having to find this page first.

1. Download [esw-api-integration.zip](https://developer.esw.com/esw-api-integration.zip). The address redirects to the current version, so follow redirects.
2. Unzip it into your agent's skills folder, for example `~/.claude/skills/` for Claude Code or `.agents/skills/` in your repository. This creates `esw-api-integration/SKILL.md`; the folder name must stay `esw-api-integration`, because it has to match the skill's name.

Prefer a single file? Download [SKILL.md](https://365533231-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FxduVPl4LIDP7RqqGRQT3%2Fuploads%2Fp2fuoruf9SO6fIFdlzBA%2FSKILL.md?alt=media) and save it as `esw-api-integration/SKILL.md` in the same folder.

Step-by-step setup for Claude Code, Cursor, GitHub Copilot and Codex is in [Build with AI coding agents](https://developer.esw.com/getting-started/get-started-with-esw/ai-coding-agents).

The skill is a condensed copy. When it and this page disagree, this page wins.

### OpenAPI specs

Fetch the JSON spec for field names, types and paths. Each address below redirects (`302`) to the current spec, which for most APIs is the live sandbox spec, so follow redirects (for example `curl -L`). Use the address exactly as shown: adding a query string breaks the redirect. The Swagger UI pages are for people: they load the spec with JavaScript and are hard to parse.

| API                | Spec (JSON)                                                                                            | Swagger UI (sandbox)                                                                                            |
| ------------------ | ------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------- |
| Checkout PreOrder  | [checkout-preorder.json](https://developer.esw.com/openapi/checkout-preorder.json)                     | [Swagger UI](https://api.sandbox.esw.com/checkout/swagger/index.html?urls.primaryName=Checkout.Api+PreOrder_v3) |
| Order Confirmation | [checkout-order-confirmation.json](https://developer.esw.com/openapi/checkout-order-confirmation.json) | [Swagger UI](https://api.sandbox.esw.com/checkout/swagger/index.html?urls.primaryName=Checkout.Api+Retailer_v3) |
| Order              | [order-api.json](https://developer.esw.com/openapi/order-api.json)                                     | [Swagger UI](https://api.sandbox.esw.com/api-docs/tenanted-order)                                               |
| Package            | [package.json](https://developer.esw.com/openapi/package.json)                                         | [Swagger UI](https://logistics-package-api.sandbox.eshopworld.com/swagger/index.html?urls.primaryName=V4)       |
| Package Management | [package-management.json](https://developer.esw.com/openapi/package-management.json)                   | —                                                                                                               |
| Customs Catalog    | [customs-catalog.json](https://developer.esw.com/openapi/customs-catalog.json)                         | [Swagger UI](https://logistics-customscatalog-api.sandbox.eshopworld.com/swagger/index.html)                    |
| Pricing Advisor    | [pricing-advisor.json](https://developer.esw.com/openapi/pricing-advisor.json)                         | [Swagger UI](https://pricing-advisor-api.sandbox.eshopworld.com/swagger/index.html)                             |
| Fulfillment        | [fulfillment.json](https://developer.esw.com/openapi/fulfillment.json)                                 | [Swagger UI](https://api.sandbox.esw.com/api-docs/fulfillment)                                                  |
| GRP Returns        | [returns.json](https://developer.esw.com/openapi/returns.json)                                         | [Swagger UI](https://api.sandbox.esw.com/api-docs/grp-returns)                                                  |
| Inventory          | [inventory.json](https://developer.esw.com/openapi/inventory.json)                                     | —                                                                                                               |

For scripts, the full address is `https://developer.esw.com/openapi/<file name above>`. Take base URLs from [Section 4](#4-environments-and-base-urls), not from a spec's `host` or `servers` field: some specs, such as Order and Pricing Advisor, do not list one.

## 3. Setup: what you need from the user

Before writing integration code, confirm you have these. If any are missing, ask; do not guess.

| Value                       | Env var (suggested) | Notes                                                                                                           |
| --------------------------- | ------------------- | --------------------------------------------------------------------------------------------------------------- |
| Sandbox client ID           | `ESW_CLIENT_ID`     | Issued by ESW                                                                                                   |
| Sandbox client secret       | `ESW_CLIENT_SECRET` | Secret. Never write it to a file in the repo                                                                    |
| Tenant code                 | `ESW_TENANT_CODE`   | Retailer identifier used in most API paths. Package uses `brandCode`; Returns uses an `identifier` path segment |
| Enabled scopes              | `ESW_SCOPES`        | Space-separated. Only what the task needs                                                                       |
| Webhook URL (Checkout only) | —                   | Public HTTPS endpoint that can receive `POST` from ESW                                                          |

Credentials are valid for six months with a one-month rollover window.

## 4. Environments and base URLs

| Service            | Sandbox base URL                                              |
| ------------------ | ------------------------------------------------------------- |
| STS (tokens only)  | `https://security-sts.sandbox.eshopworld.com/connect/token`   |
| Checkout           | `https://api.sandbox.esw.com`                                 |
| Order              | `https://api.sandbox.esw.com`                                 |
| Package Management | `https://api.sandbox.esw.com`                                 |
| GRP Returns        | `https://api.sandbox.esw.com`                                 |
| Fulfillment        | `https://api.sandbox.esw.com`                                 |
| Package            | `https://logistics-package-api.sandbox.eshopworld.com`        |
| Customs Catalog    | `https://logistics-customscatalog-api.sandbox.eshopworld.com` |
| Pricing Advisor    | `https://pricing-advisor-api.sandbox.eshopworld.com`          |

Production STS is `https://security-sts.production.eshopworld.com/connect/token`. **Production API base URLs are not published here.** Get them from the user (who gets them from their ESW representative). Do not derive them by swapping `sandbox` for `production`.

STS hosts only issue tokens. Never send an API request to an STS host.

## 5. Authentication

ESW uses OAuth 2.0 **client credentials** against the ESW Security Token Service (STS).

```sh
curl --request POST "https://security-sts.sandbox.eshopworld.com/connect/token" \
  --header "Content-Type: application/x-www-form-urlencoded" \
  --data-urlencode "grant_type=client_credentials" \
  --data-urlencode "scope=$ESW_SCOPES" \
  --data-urlencode "client_id=$ESW_CLIENT_ID" \
  --data-urlencode "client_secret=$ESW_CLIENT_SECRET"
```

Response:

```json
{ "access_token": "<jwt>", "expires_in": 3600, "token_type": "Bearer", "scope": "..." }
```

Then send `Authorization: Bearer <access_token>` on every API call.

**Rules**

* The token body is **form URL encoded**. JSON bodies and multipart (`--form`) bodies are rejected.
* Cache the token per scope set and reuse it. Refresh about 60 seconds before `expires_in` elapses. Do not request a token per call.
* On `401` from an API: refresh once, then retry **only if the request is idempotent** (see Section 6).
* Request the narrowest scopes the task needs.

**Smoke test** (read-only, safe to run): after getting a token with `pricing.advisor.api.all`,

```sh
curl "https://pricing-advisor-api.sandbox.eshopworld.com/api/4.0/MultiplierAdvice/$ESW_TENANT_CODE/DE" \
  --header "Authorization: Bearer $ESW_TOKEN" --header "Accept: application/json"
```

A `200` confirms credentials, scope, and tenant code together.

### Scopes

| Scope                                            | Grants                 | API             |
| ------------------------------------------------ | ---------------------- | --------------- |
| `checkout.preorder.api.all`                      | PreOrder               | Checkout        |
| `order.transaction.create.api.all`               | Create order           | Order           |
| `order.transaction.get.api.all`                  | Get order              | Order           |
| `order.transaction.update.api.all`               | Update actions         | Order           |
| `order.transaction.cancel.api.all`               | Cancel actions         | Order           |
| `order.transaction.allocation.api.all`           | Allocation             | Order           |
| `pricing.advisor.api.all`                        | Pricing Advisor        | Pricing Advisor |
| `logistics.catalog.api.upload`                   | Catalog upload         | Customs Catalog |
| `logistics.package.api.all`                      | Package API            | Package         |
| `logistics.returns.api.all`                      | Returns API            | Returns         |
| `logistics.inventory.cart.availability.api.read` | Inventory availability | Inventory       |

For Fulfillment and Package Management scopes, ask the user to confirm the scope ESW enabled on their client.

### Auth troubleshooting

| Symptom              | Cause                                       | Action                                        |
| -------------------- | ------------------------------------------- | --------------------------------------------- |
| STS `invalid_client` | Wrong ID/secret, or wrong environment's STS | Check the STS host matches the credentials    |
| STS `invalid_scope`  | Scope misspelled or not enabled             | Fix spelling; otherwise the user must ask ESW |
| API `401`            | Token missing, malformed, or expired        | Refresh token                                 |
| API `403`            | Token lacks scope, or wrong `tenantCode`    | Request correct scope; verify tenant code     |
| API `429`            | Rate limited                                | Wait `Retry-After` seconds, then retry        |

## 6. Operation risk classes

Classify every call before you make it.

| Class                | Examples                                                                                                                                                                              | Agent behavior                                            |
| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------- |
| **Read-only**        | Pricing Advisor `GET`, Get Order, Retrieve Package, Inventory availability, Returns order lookup                                                                                      | Run freely in sandbox. Safe to retry                      |
| **Reversible write** | PreOrder (creates a checkout session), Create Package, Update Package, Customs Catalog upload, Create Return                                                                          | Run in sandbox when the task needs it. Do not blind-retry |
| **Irreversible**     | Order `LineItemActivity` `Fulfil` or `Cancel`, Order `OrderActivity` `Cancel`, appeasements, replacements, Fulfillment Pick & Pack confirm containing cancelled items, Delete Package | **Ask the user first**, every time                        |

Why some "passive-looking" calls are irreversible:

* Order article statuses `Shipped` and `Cancelled` are final.
* Cancelling an order or line item starts a refund of any pre-authorized payment.
* A Pick & Pack confirmation that reports a cancelled item triggers a refund, even though it reads like a status report.
* A `4xx`/`5xx` from the retailer's Order Confirmation endpoint cancels the shopper's order.

## 7. Request conventions

* **Money and weights differ by API.** Checkout and Order send `amount` as a **string** with a `.` decimal separator and no symbols: `"89.95"`. Customs Catalog, Package and Returns send amounts and weights as JSON **numbers**. Check the spec for the API you are calling; never assume one format across APIs.
* **Codes:** ISO 4217 currency (`"EUR"`) and ISO 3166-1 alpha-2 country (`"DE"`), uppercase.
* **IDs:** `lineItemId` is a string (`"1"`), unique within the order and stable for its lifetime.
* **Write idempotency (Order API):** every write needs a UUID v4 `transactionReference`. Generate it **once per logical action**, store it before sending, and send the **same** value on every retry of that action. Generate a new one only for a new action.
* **`quantity`:** always send it on line items, even where a schema marks it optional.
* **Retailer identifier:** most paths include `{tenantCode}`. The Package API uses `brandCode` instead, and the Returns API uses an `identifier` path segment. A `403` on a correct scope usually means the wrong identifier.
* **Errors:** Checkout validation returns a list of `{ code, message }`. Two-digit codes point at a field path; five-digit codes are domain rules. Pricing Advisor uses RFC 7807 ProblemDetails.

## 8. Async behavior

Several ESW calls acknowledge before they finish. Do not treat `202` as success.

| Call                   | Initial response | How to confirm                                                                                                                                                        |
| ---------------------- | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Order create           | `202`            | Poll `GET /v2/{tenantCode}/Order/{brandOrderReference}` every 2 s for up to 2 min until every article is `Created`. A `404` inside that window means still processing |
| Customs Catalog upload | `202`            | Follow the polling pattern on the Customs Catalog **Async Processing** page                                                                                           |
| Order Confirmation     | —                | Not an outbound call. ESW `POST`s to the retailer's endpoint after payment                                                                                            |

## 9. API quick map

### Checkout

* `POST /checkout/api/v3/{tenantCode}/PreOrder` → `201` with `preOrderCode`, `redirectUrl`. Redirect the shopper to `redirectUrl` unchanged.
* Required: `retailerCartId` (unique per cart), `deliveryCountryIso`, ≥1 `lineItems`; each product needs `productCode`, `title`, `description`, `productUnitPriceInfo.price`.
* `product.description` (not `title`) is the shopper-facing product name in checkout.
* If you send `cartDiscountPriceInfo`, its `price` cannot be exactly `0.00`. Omit the object instead.
* Sandbox webhook override: `metadataItems` `OrderConfirmationUri_TestOnly` and `OrderConfirmationBase64EncodedAuth_TestOnly`. Names are case-sensitive and only work in test environments.
* Webhook handlers must authenticate the request, be idempotent (use `Idempotency-Key` when present, else `RetailerCartId`), respond `2xx` within 3 seconds, and may return `{ "orderNumber": "..." }`.

### Order

* Create `POST /v2/{tenantCode}/Order`; read `GET /v2/{tenantCode}/Order/{brandOrderReference}`.
* Line actions `POST .../LineItemActivity`; whole-order actions `POST .../OrderActivity`.
* `Fulfil` (British spelling) with `reasonCode: ItemFulfilled` at dispatch, not at pick/pack.
* `orderType`: `Checkout`, `NonCheckoutEcommerce`, or `Offline`. Send prices in retailer currency.
* `actionedBy`: `Retailer`, `Shopper`, or `EShopWorld`.

### Pricing Advisor

* `GET /api/4.0/MultiplierAdvice/{tenantCode}/{countryIso}` and the Standard Advice equivalents. Read-only; the best first call for verifying setup.

### Package

* Identifies the retailer with `brandCode`, not `tenantCode`. Amounts and weights are JSON numbers.
* `PUT` (update) returns `201`, not `200`. Accept any `2xx`.
* Handle the label outcome in the create response before treating a package as shippable.

### Customs Catalog, Fulfillment, Returns, Inventory

Use each API's Quick Start and **Decision Tables** page. Fulfillment reports warehouse outcomes from a 3PL; Returns (GRP) requires the order to be registered before a return can be created.

## 10. Retries

| Response                            | Idempotent request (GET, PUT)       | Non-idempotent (POST writes)                                                                |
| ----------------------------------- | ----------------------------------- | ------------------------------------------------------------------------------------------- |
| `401`                               | Refresh token, retry once           | Refresh token, then read state before deciding to resend                                    |
| `429`                               | Wait `Retry-After`, retry           | Wait `Retry-After`, then read state before resending                                        |
| `500`/`502`/`503`                   | Exponential backoff, max 3 attempts | Read state first; resend only if the write did not land                                     |
| `400`/`403`/`404`/`409`/`412`/`422` | Do not retry. Fix the request       | Do not retry. Fix the request, or read state: `409` often means the action already happened |

Before resending any write, follow the per-API recipe in [Idempotency and Retries](https://developer.esw.com/getting-started/get-started-with-esw/quick-start-guide/best-practices/idempotency-and-retries). It lists each API's deduplication key, what a duplicate returns, and the read to make first. Order activities are quantity-based: a resent `Fulfil` or `Cancel` can act on another unit, so compare article counts before resending.

## 11. Things agents must not do

* Do not call production by default or swap hostnames to reach it.
* Do not send API requests to STS hosts.
* Do not commit `.env` files, Postman environments with secrets, or captured tokens.
* Do not fulfil or cancel order items before order creation is confirmed.
* Do not resend an Order activity, a cancel, or a Pick & Pack confirmation without first reading current state.
* Do not treat any page labelled "Copy of …" as authoritative; prefer the non-copy page.
* Do not mark an integration "done" without running the relevant Quick Start flow end-to-end in sandbox.


---

# 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 by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://developer.esw.com/agents.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

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.
