> 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/pricing-advisor-api/resources/tutorials/standard-pricing-advice.md).

# Standard Pricing Advice

{% hint style="info" %}

### Prerequisites

* A valid Bearer JWT token
* Your `tenantCode` — the brand identifier used throughout the platform
  {% endhint %}

***

{% tabs %}
{% tab title="Code Walkthrough" %}
{% @code-walkthrough/code-walkthrough title="Sample Response Code" language="json" filename="Standard Pricing Advice" code="{
// Unique ID — store for audit/archived retrieval
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",

// Version 42 — if this increases, reprice your products
"version": "42",

"tenantIdentifier": "my-brand-001",
"countryIso": "US",

// Advice was last updated 14 March 2025 at 10:30 UTC
"lastUpdated": "2025-03-14T10:30:00Z",

"fxRates": \[
{
"from": "EUR",   // Brand's home currency
"to": "USD",     // US market currency
"rate": 1.0872   // Multiply EUR price by 1.0872 to get USD
}
],

"categories": \[
{
// Category ID — match against your product's category field
"id": "ApparelAccessories",

```
  // Estimated US import duty: 12.5%
  "estimatedDuty": 12.5,

  // US has no federal VAT, so tax = 0 here
  "estimatedTax": 0.0,

  // Platform service/compliance fee: 0.5%
  "estimatedFee": 0.5,

  // Retailer has configured a 2.3% adjustment for this market
  "retailerAdjustment": 2.3,

  // How to round final USD prices — see Tutorial 5
  "roundingConfigurations": [
    {
      "currencyIso": "USD",
      "currencyExponent": 2,
      "direction": "Up",
      "model": "Standard"
    }
  ],

  // How to display USD prices — see Tutorial 5
  "currencyDisplays": [
    {
      "currencyIso": "USD",
      "currencySymbol": "$",
      "currencyExponent": 2,
      "decimalSeparator": ".",
      "thousandSeparator": ",",
      "showTrailingZeros": true,
      "configurationString": "$ #,##0.00"
    }
  ]
}
```

],

"merchandisePricingModel": {
// The source prices already include duty and tax
"id": "DutyAndTaxIncluded",
"applicableRates": \["estimatedDuty", "estimatedTax"]

```
// This means: only apply estimatedFee and retailerAdjustment
// Do NOT add estimatedDuty or estimatedTax — they are already in the price
```

}
}" steps="\[
{"label":"Store the config ID","description":"<code>id</code> is a stable UUID — store it on every order record so you can retrieve the exact config that priced it during audits or disputes.","lines":"2-3","tip":"Pair the ID with a session/request ID so you can reconstruct exactly which config priced a given order"},
{"label":"Detect config changes","description":"If <code>version</code> has incremented since your last fetch, you must reprice all products — rates, duties, or fees may have changed.","lines":"5-6","tip":"Cache {id, version} locally and skip a full re-parse when neither has changed"},
{"label":"Scope fields","description":"<code>tenantIdentifier</code> scopes this config to one brand, <code>countryIso</code> to the US market. <code>lastUpdated</code> is useful for logging but is not a substitute for the version counter.","lines":"8-12","tip":"Index configs by tenantIdentifier + countryIso if you serve multiple brands or markets"},
{"label":"FX rate","description":"Multiply the EUR source price by <code>rate: 1.0872</code> to convert to USD. <code>from</code> is the brand's home currency, <code>to</code> is the market currency.","lines":"14-20","tip":"Always apply FX conversion before adding any percentage-based rates — order of operations matters"},
{"label":"Match category to your product","description":"<code>id: ApparelAccessories</code> must match exactly the category field on your product record — this selects the entire duty, tax, fee, and rounding ruleset for that product.","lines":"24-25","tip":"Fall back to a default category rather than throwing when no match is found"},
{"label":"Understand the rate breakdown","description":"Four rates apply: <code>estimatedDuty: 12.5%</code> (US import duty), <code>estimatedTax: 0%</code> (no federal VAT in the US), <code>estimatedFee: 0.5%</code> (platform fee), <code>retailerAdjustment: 2.3%</code> (brand-configured market adjustment).","lines":"27-37","tip":"Always check merchandisePricingModel before applying these — some may already be embedded in the source price"},
{"label":"Apply rounding","description":"<code>currencyExponent: 2</code> means USD prices have 2 decimal places. <code>direction: Up</code> always rounds up — so $12.341 becomes $12.35.","lines":"39-47","tip":"Always round as the very last step — rounding mid-calculation compounds error"},
{"label":"Format for display","description":"Use <code>currencySymbol: $</code>, <code>thousandSeparator: ,</code>, <code>decimalSeparator: .</code> and <code>currencyExponent: 2</code> to render prices. <code>showTrailingZeros: true</code> means $12.50 not $12.5. <code>configurationString: $ #,##0.00</code> is a machine-readable pattern for i18n libraries.","lines":"49-60","tip":"Cross-check formatter output against configurationString in unit tests to catch locale regressions"},
{"label":"Pricing model — critical","description":"<code>id: DutyAndTaxIncluded</code> means the source price already has <code>estimatedDuty</code> and <code>estimatedTax</code> baked in — listed in <code>applicableRates</code>. Only apply <code>estimatedFee</code> and <code>retailerAdjustment</code> on top. Adding duty or tax again would double-count them.","lines":"64-70","tip":"Always read merchandisePricingModel before deciding which rates to apply — it changes the entire calculation"}
]" fullWidth="false" %}
{% endtab %}
{% endtabs %}

{% stepper %}
{% step %}

### When to Use Standard Advice

{% hint style="info" icon="hand-holding-circle-dollar" %}
Standard Advice is the right choice when you need the full breakdown of the components that make up a localised price — FX rate, estimated duty, estimated tax, estimated fee, and retailer adjustment — and intend to apply them yourself to a source price.
{% endhint %}

Use Standard Advice when you need to:

* Display an itemized breakdown of price components to a user
* Apply different rates to different product categories
* Integrate with a bespoke pricing engine that handles its own rounding or margin logic
* Audit or explain why a given localized price was calculated as it was

If you only need a single ready-to-use conversion factor per category and currency pair without building the calculation yourself, consider Multiplier Advice instead — it pre-computes the combined multiplier for you.
{% endstep %}

{% step %}

### The Three Access Patterns

The Standard Advice resource exposes three endpoints. All are read-only `GET` requests.

| Pattern                   | Endpoint                                                | Use case                                                |
| ------------------------- | ------------------------------------------------------- | ------------------------------------------------------- |
| All countries for a brand | `GET /api/4.0/StandardAdvice/{tenantCode}`              | Bootstrap or refresh your full pricing table on startup |
| One country for a brand   | `GET /api/4.0/StandardAdvice/{tenantCode}/{countryIso}` | Fetch or refresh advice for a specific market           |
| Specific archived record  | `GET /api/4.0/StandardAdvice/{id}/archived`             | Retrieve a historical version by its unique ID          |

All endpoints require a Bearer JWT in the `Authorization` header.

```http
Authorization: Bearer <your_jwt_token>
```

{% endstep %}

{% step %}

### Fetch Pricing Advice for All Countries

Returns an array of `PricingAdviceResponse` objects — one per country the brand has active pricing advice configured for.

```http
GET /api/4.0/StandardAdvice/{tenantCode}
Authorization: Bearer <token>
```

**Path parameters:**

| Parameter    | Type   | Required | Description              |
| ------------ | ------ | -------- | ------------------------ |
| `tenantCode` | string | ✅        | Your brand's tenant code |

**Example:**

```http
GET /api/4.0/StandardAdvice/my-brand-001
Authorization: Bearer <token>
```

**Responses:**

| Status             | Meaning                                                   |
| ------------------ | --------------------------------------------------------- |
| `200 OK`           | Array of `PricingAdviceResponse` — one object per country |
| `400 Bad Request`  | Invalid `tenantCode` format                               |
| `401 Unauthorized` | Missing or invalid JWT                                    |
| `403 Forbidden`    | JWT does not have permission for this tenant              |

{% hint style="info" %}

#### **When to use**

Call this endpoint on application startup or cache refresh to load pricing data for all markets in one round trip. Store the result keyed by `countryIso` for fast lookup at runtime.
{% endhint %}
{% endstep %}

{% step %}

### Fetch Pricing Advice for a Single Country

Returns a single `PricingAdviceResponse` for the latest available advice for a specific brand and country combination.

```http
GET /api/4.0/StandardAdvice/{tenantCode}/{countryIso}
Authorization: Bearer <token>
```

**Path parameters:**

| Parameter    | Type   | Required | Description                                                  |
| ------------ | ------ | -------- | ------------------------------------------------------------ |
| `tenantCode` | string | ✅        | Your brand's tenant code                                     |
| `countryIso` | string | ✅        | ISO 3166-1 alpha-2 country code, e.g. `"US"`, `"JP"`, `"DE"` |

**Example:**

```http
GET /api/4.0/StandardAdvice/my-brand-001/US
Authorization: Bearer <token>
```

**Responses:**

| Status             | Meaning                                                         |
| ------------------ | --------------------------------------------------------------- |
| `200 OK`           | A single `PricingAdviceResponse` for the requested country      |
| `400 Bad Request`  | Invalid `tenantCode` or `countryIso`                            |
| `401 Unauthorized` | Missing or invalid JWT                                          |
| `403 Forbidden`    | JWT does not have permission for this tenant                    |
| `404 Not Found`    | No pricing advice configured for this brand/country combination |

{% hint style="info" %}

#### **When to use this**

use this pattern to refresh a single country's pricing data without reloading the full set — for example, after receiving a notification that pricing has been updated for a specific market.
{% endhint %}
{% endstep %}

{% step %}

### Fetch a Specific Archived Record by ID

Returns a `PricingAdviceResponse` for a historical (archived) version of the advice, looked up by its unique `id`. This allows you to retrieve the exact pricing data that was active at a point in time.

```http
GET /api/4.0/StandardAdvice/{id}/archived
Authorization: Bearer <token>
```

**Path parameters:**

| Parameter | Type   | Required | Description                                         |
| --------- | ------ | -------- | --------------------------------------------------- |
| `id`      | string | ✅        | The unique identifier of the specific advice record |

**Example:**

```http
GET /api/4.0/StandardAdvice/a1b2c3d4-e5f6-7890-abcd-ef1234567890/archived
Authorization: Bearer <token>
```

**Responses:**

| Status             | Meaning                                           |
| ------------------ | ------------------------------------------------- |
| `200 OK`           | The archived `PricingAdviceResponse` with that ID |
| `401 Unauthorized` | Missing or invalid JWT                            |
| `403 Forbidden`    | JWT does not have permission                      |
| `404 Not Found`    | No record found with that ID                      |

{% hint style="info" %}

#### **When to use this**

Use the `id` from a previously stored `PricingAdviceResponse` to reconstruct the exact pricing state at any point in history — useful for audits, price investigations, and debugging reported price discrepancies.
{% endhint %}
{% endstep %}

{% step %}

### The Full `PricingAdviceResponse`

Every Standard Advice response — whether for one country or many — returns `PricingAdviceResponse` objects with this structure:

json

```json
{
  "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "version": "42",
  "tenantIdentifier": "my-brand-001",
  "countryIso": "US",
  "lastUpdated": "2025-03-14T10:30:00Z",
  "fxRates": [ ... ],
  "categories": [ ... ],
  "merchandisePricingModel": { ... }
}
```

<details>

<summary><strong>Top-level fields</strong></summary>

| Field                     | Type                        | Description                                                                                                                 |
| ------------------------- | --------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| `id`                      | string                      | Unique identifier for this specific piece of advice — store this if you need to retrieve it later via the archived endpoint |
| `version`                 | string                      | Monotonically increasing version number — use this to detect when advice has been updated                                   |
| `tenantIdentifier`        | string                      | The brand/tenant this advice belongs to                                                                                     |
| `countryIso`              | string                      | ISO 3166-1 alpha-2 country code this advice applies to                                                                      |
| `lastUpdated`             | string (ISO 8601 UTC)       | When this advice was last updated — use this for freshness checks                                                           |
| `fxRates`                 | array of `FxRateResponse`   | Exchange rate(s) from the brand's home currency to the country's local currency                                             |
| `categories`              | array of `CategoryResponse` | Per-product-category pricing data — estimated duty, tax, fee, retailer adjustment, rounding rules, and currency display     |
| `merchandisePricingModel` | `PricingModelResponse`      | Describes which rates are already embedded in the input price — critical for avoiding double-counting                       |

</details>
{% endstep %}

{% step %}

### Understanding `fxRates`

`fxRates` is an array of foreign exchange rate objects. Each entry describes the conversion rate between one currency and another.

```json
"fxRates": [
  {
    "from": "EUR",
    "to": "USD",
    "rate": 1.0872
  }
]
```

**`FxRateResponse` fields:**

| Field  | Type            | Description                                                                 |
| ------ | --------------- | --------------------------------------------------------------------------- |
| `from` | string          | The source currency ISO code (typically the brand's home currency)          |
| `to`   | string          | The destination currency ISO code (the country's local currency)            |
| `rate` | number (double) | The exchange rate — multiply a `from` amount by this to get the `to` amount |

**Multiple FX rate entries** may appear when a country accepts more than one currency, or when the brand sells in multiple home currencies. Identify the pair relevant to your calculation using the `from` and `to` fields.

**Practical usage:**

```ruby
localPrice = homePrice × fxRate.rate
```

{% endstep %}

{% step %}

### Understanding `categories`

`categories` is an array of `CategoryResponse` objects. Each entry represents a product category and contains the estimated rates and display configuration that apply to products in that category when sold in this country.

{% code expandable="true" %}

```json
"categories": [
  {
    "id": "ApparelAccessories",
    "estimatedDuty": 12.5,
    "estimatedTax": 20.0,
    "estimatedFee": 0.5,
    "retailerAdjustment": 2.3,
    "roundingConfigurations": [ ... ],
    "currencyDisplays": [ ... ]
  },
  {
    "id": "Electronics",
    "estimatedDuty": 0.0,
    "estimatedTax": 20.0,
    "estimatedFee": 1.2,
    "retailerAdjustment": 1.8,
    "roundingConfigurations": [ ... ],
    "currencyDisplays": [ ... ]
  }
]
```

{% endcode %}

<details>

<summary><code>CategoryResponse</code> Fields</summary>

| Field                    | Type   | Description                                                                                                                    |
| ------------------------ | ------ | ------------------------------------------------------------------------------------------------------------------------------ |
| `id`                     | string | The product category identifier — matches the `category` field on your products (e.g. `"ApparelAccessories"`, `"Electronics"`) |
| `estimatedDuty`          | number | Estimated import duty rate as a percentage, e.g. `12.5` means 12.5%                                                            |
| `estimatedTax`           | number | Estimated tax (e.g. VAT, GST) as a percentage                                                                                  |
| `estimatedFee`           | number | Estimated additional fees (e.g. handling, compliance) as a percentage                                                          |
| `retailerAdjustment`     | number | A margin or adjustment configured by the retailer as a percentage                                                              |
| `roundingConfigurations` | array  | How to round the final price for this category and currency — see Tutorial 5                                                   |
| `currencyDisplays`       | array  | How to format and display prices for this category — see Tutorial 5                                                            |

</details>

#### How the Rate Fields Relate

All four numeric rate fields are **percentages**, not decimal multipliers:

| Field                | Meaning                | Example value | Applied as                   |
| -------------------- | ---------------------- | ------------- | ---------------------------- |
| `estimatedDuty`      | Import duty            | `12.5`        | 12.5% of the converted price |
| `estimatedTax`       | VAT / GST              | `20.0`        | 20% of the converted price   |
| `estimatedFee`       | Service/compliance fee | `0.5`         | 0.5% of the converted price  |
| `retailerAdjustment` | Retailer margin offset | `2.3`         | 2.3% of the converted price  |

> **Important:** before applying these rates, check `merchandisePricingModel.applicableRates` (**Step 9**). If a rate is listed there, it is already embedded in the source price and should **not** be added again.

#### Matching Products to Categories

Match each product to a `CategoryResponse` using its `category` field. If no matching category is found in the response, either the product category is not configured for that country, or a fallback/default rate from another category should be applied according to your business rules.
{% endstep %}

{% step %}

### Understanding `merchandisePricingModel`

`merchandisePricingModel` is one of the most important — and most commonly misunderstood — fields in the response. It tells you which rates from `categories` are **already included** in the source price your brand has provided.

```json
"merchandisePricingModel": {
  "id": "DutyAndTaxIncluded",
  "applicableRates": ["estimatedDuty", "estimatedTax"]
}
```

**`PricingModelResponse` fields:**

| Field             | Type             | Description                                                                                          |
| ----------------- | ---------------- | ---------------------------------------------------------------------------------------------------- |
| `id`              | string           | A label identifying the pricing model, e.g. `"DutyAndTaxIncluded"`, `"LandedCost"`, `"ExcludingAll"` |
| `applicableRates` | array of strings | The rate field names that are already baked into the source price and must **not** be applied again  |

If you add `estimatedDuty` to a source price that already includes duty, you will double-count the duty and overcharge the customer.

#### How to Use It

Check `applicableRates` before building your price calculation. Any rate name listed there should be **skipped** — it is already in the price.

**Example:**

```ruby
applicableRates = ["estimatedDuty", "estimatedTax"]

Rates to apply   = all category rates EXCEPT those in applicableRates
                 = ["estimatedFee", "retailerAdjustment"]
```

**Pseudocode for a correct price calculation:**

{% code expandable="true" %}

```ruby
applicableRates = advice.merchandisePricingModel.applicableRates
category        = find category matching product.category

ratesToApply = []
if "estimatedDuty" not in applicableRates:
    ratesToApply += category.estimatedDuty
if "estimatedTax" not in applicableRates:
    ratesToApply += category.estimatedTax
if "estimatedFee" not in applicableRates:
    ratesToApply += category.estimatedFee
if "retailerAdjustment" not in applicableRates:
    ratesToApply += category.retailerAdjustment

totalRatePercent = sum(ratesToApply)
convertedPrice   = homePrice × fxRate.rate
localisedPrice   = convertedPrice × (1 + totalRatePercent / 100)
```

{% endcode %}
{% endstep %}

{% step %}

### Checking Freshness with `version` and `lastUpdated`

Every `PricingAdviceResponse` includes two fields for tracking when advice was last changed.

```json
{
  "version": "42",
  "lastUpdated": "2025-03-14T10:30:00Z"
}
```

| Field         | Type                  | Description                                                                            |
| ------------- | --------------------- | -------------------------------------------------------------------------------------- |
| `version`     | string                | A monotonically increasing version number — increments each time the advice is updated |
| `lastUpdated` | string (ISO 8601 UTC) | The UTC timestamp of the last update                                                   |

#### Recommended Usage

**For change detection:** store the `version` from your last fetch. On the next fetch, compare the new `version` — if it has increased, the advice has changed and your cached data should be invalidated and recalculated.

```ruby
if newAdvice.version > cachedAdvice.version:
    invalidateCache(countryIso)
    recalculatePrices(newAdvice)
```

{% hint style="info" icon="arrow-progress" %}
**Polling cadence:** pricing advice is typically updated on a schedule (e.g. when FX rates are refreshed or when duty rates change). A polling interval of 15–60 minutes is usually sufficient — check with your ESW integration contact for the specific update frequency for your brand.
{% endhint %}
{% endstep %}
{% endstepper %}

***

<details>

<summary>Annotated Full Response Example</summary>

{% code expandable="true" %}

```json
{
  // Unique ID — store for audit/archived retrieval
  "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",

  // Version 42 — if this increases, reprice your products
  "version": "42",

  "tenantIdentifier": "my-brand-001",
  "countryIso": "US",

  // Advice was last updated 14 March 2025 at 10:30 UTC
  "lastUpdated": "2025-03-14T10:30:00Z",

  "fxRates": [
    {
      "from": "EUR",   // Brand's home currency
      "to": "USD",     // US market currency
      "rate": 1.0872   // Multiply EUR price by 1.0872 to get USD
    }
  ],

  "categories": [
    {
      // Category ID — match against your product's category field
      "id": "ApparelAccessories",

      // Estimated US import duty: 12.5%
      "estimatedDuty": 12.5,

      // US has no federal VAT, so tax = 0 here
      "estimatedTax": 0.0,

      // Platform service/compliance fee: 0.5%
      "estimatedFee": 0.5,

      // Retailer has configured a 2.3% adjustment for this market
      "retailerAdjustment": 2.3,

      // How to round final USD prices — see Tutorial 5
      "roundingConfigurations": [
        {
          "currencyIso": "USD",
          "currencyExponent": 2,
          "direction": "Up",
          "model": "Standard"
        }
      ],

      // How to display USD prices — see Tutorial 5
      "currencyDisplays": [
        {
          "currencyIso": "USD",
          "currencySymbol": "$",
          "currencyExponent": 2,
          "decimalSeparator": ".",
          "thousandSeparator": ",",
          "showTrailingZeros": true,
          "configurationString": "$ #,##0.00"
        }
      ]
    }
  ],

  "merchandisePricingModel": {
    // The source prices already include duty and tax
    "id": "DutyAndTaxIncluded",
    "applicableRates": ["estimatedDuty", "estimatedTax"]

    // This means: only apply estimatedFee and retailerAdjustment
    // Do NOT add estimatedDuty or estimatedTax — they are already in the price
  }
}
```

{% endcode %}

</details>

### Common Patterns

#### Caching Strategy

Avoid calling the API on every individual price calculation. The advice is updated periodically, not in real time. The recommended pattern is:

```ruby
On startup:    GET /StandardAdvice/{tenantCode}          → cache all countries
Every N mins:  GET /StandardAdvice/{tenantCode}/{country} → refresh per country
On demand:     GET /StandardAdvice/{tenantCode}/{country} → after cache miss
```

***

### Common Errors

| Status             | Likely Cause                                | Action                                                                                   |
| ------------------ | ------------------------------------------- | ---------------------------------------------------------------------------------------- |
| `400 Bad Request`  | Malformed `tenantCode` or `countryIso`      | Confirm `countryIso` is a valid ISO 3166-1 alpha-2 code; check `tenantCode` for typos    |
| `401 Unauthorized` | Missing or expired JWT                      | Refresh your token and retry                                                             |
| `403 Forbidden`    | JWT is valid but not scoped to this tenant  | Confirm the token was issued for `my-brand-001`; check permissions with your ESW contact |
| `404 Not Found`    | No advice configured for this brand/country | The market may not be enabled for your brand; confirm with your ESW integration team     |


---

# 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/pricing-advisor-api/resources/tutorials/standard-pricing-advice.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.
