🧮 D365 SCM pricing API now calculates multiline discounts

Microsoft has expanded the documented scope of Unified Pricing Management’s pricing calculation API: as of October 2, 2026, multiline or cart-level discounts are explicitly supported. One day earlier, they appeared in the unsupported column.

The change matters for B2B portals, configurators, quotation tools, and partner integrations. An external application can submit multiple lines as one transaction, let Supply Chain Management evaluate their shared context, and retrieve prices and discounts without creating a sales order.

It does not turn this API into a Commerce checkout engine. Microsoft still excludes basket pricing with promotion concurrency, e-commerce storefronts, and high-frequency or high-volume calls.

Multiline discount calculation architecture with Unified Pricing Management.

Original diagram. Lines travel together inside priceLookupContext; SCM returns a per-product breakdown without persisting an order.

What changed exactly

The official scope table now makes this distinction:

SupportedStill unsupported
Single-line price calculation per productCommerce basket pricing and promotion concurrency
Simple discountsHigh-frequency or high-volume calls
Multiline or cart-level discountsE-commerce or storefront scenarios
Unified Pricing Management rules and attributesReplacing Commerce Scale Unit APIs
Quantity-based pricing and variant ranges—

The distinction between a “cart-level discount” and “basket pricing” matters. The former describes rules that need a transaction’s full set of lines. The latter belongs to Commerce scenarios with specific concurrency, performance, and basket semantics. The SCM API can evaluate lines together without acquiring the operating contract of a POS or web store.

Microsoft does not label the API as preview. Published prerequisites are:

  • Dynamics 365 Supply Chain Management 10.0.47 or later;
  • Unified Pricing Management enabled;
  • an application registered in Microsoft Entra ID;
  • the same application registered in SCM and mapped to a suitably privileged user.

The documentation announces no feature-specific SKU or meter. Existing Supply Chain Management licensing and the security context of the integration user continue to apply.

Architecture and contract

The external application does not create SalesTable or SalesLine. It invokes a synchronous JSON service:

POST https://<environment>/api/services/GUPPricingServiceGroup/GUPPricingService/getActivePrices
Authorization: Bearer <token>
Content-Type: application/json

The flow is:

  1. The application obtains an OAuth 2.0 token from Microsoft Entra ID for the https://<environment>/.default scope.
  2. SCM resolves the client ID through System administration > Setup > Microsoft Entra ID applications and runs under the mapped user.
  3. GUPPricingService turns HeaderContext and LineContexts into a transaction context for Unified Pricing Management.
  4. The engine evaluates prices, adjustments, and discounts effective at the requested time.
  5. The response contains a Transaction and an ItemResults array with each product’s price and breakdown.

The service calculates; it does not document sales-order persistence or inventory reservation. If a consuming system must commit the quote, use a separate document flow and decide how validity is controlled between calculation and confirmation.

Two request modes

The contract offers two mutually exclusive paths:

InputUse
productIdsSimple product lookup, with default quantity 1
priceLookupContext + LineContextsMultiple lines, quantities, units, warehouses, delivery modes, and attributes

Do not send productIds and LineContexts in the same request. Multiline discounts require LineContexts, because each line participates in the transaction context.

The main calculation controls are:

  • dataAreaId: company, required when ChannelId isn’t supplied;
  • activeDate: effective timestamp; defaults to system date and time;
  • calculateSimpleDiscountOnly: true limits calculation to simple discounts; false treats input as a transaction for full calculation;
  • includeVariantPriceRange: returns variant minimum and maximum when true.

Complete multiline request

This example submits two products with different quantities and requests full calculation:

{
  "request": {
    "dataAreaId": "usrt",
    "activeDate": "2026-10-03T08:00:00+02:00",
    "priceLookupContext": {
      "HeaderContext": {
        "CustomerAccount": "004009",
        "InventorySiteId": "CENTRAL",
        "InventoryLocationId": "DC-CENTRAL",
        "SalesOrderProperties": [
          {
            "Name": "order type",
            "Value": "3"
          }
        ]
      },
      "LineContexts": [
        {
          "ProductRecordId": 22565421974,
          "UnitOfMeasureSymbol": "ea",
          "Quantity": 3,
          "SalesLineProperties": [
            {
              "Name": "material",
              "Value": "brass"
            }
          ]
        },
        {
          "ProductRecordId": 22565421975,
          "UnitOfMeasureSymbol": "ea",
          "Quantity": 2
        }
      ]
    },
    "calculateSimpleDiscountOnly": false,
    "includeVariantPriceRange": false
  }
}

Names are case-sensitive. ProductRecordId is the product record identifier, not ItemId. Quantity defaults to 1. Site and warehouse first inherit from the channel and, without a channel, from the customer; send "" to override inheritance with a blank value.

Header context

HeaderContext can supply:

  • CustomerAccount;
  • ChannelId instead of dataAreaId for channel-based calculation;
  • common site and warehouse;
  • affiliations and loyalty tiers;
  • SalesOrderProperties for header attributes.

Affiliations already linked to the customer are applied automatically. Send them explicitly only to introduce different or additional context.

Line context

Each LineContexts element can provide:

  • product, unit, and quantity;
  • line-specific site and warehouse;
  • delivery mode;
  • SalesLineProperties consumed by pricing rules.

SalesOrderProperties and SalesLineProperties do not support two values for the same attribute in one request. Use separate requests to compare alternatives.

Reading the response

ItemResults returns an entry for each calculated product. Documented fields include:

FieldMeaning
BasePriceProduct base price
TradeAgreementPricePrice from applicable trade agreements
AdjustedPricePrice after adjustments
CustomerContextualPriceFinal contextual customer price
DiscountAmountTotal applied discount
CurrencyCode, UnitOfMeasure, QuantityResult context
AttainablePriceLinesPrice method and origin trace
DiscountLinesOffers, percentages, and effective amounts

For multiline integration, do not read only CustomerContextualPrice. Retain DiscountLines, OfferId, SaleLineNumber, and effective amounts to diagnose why a promotion did or did not apply. Do not assume array order is a stable key; correlate using product and line identifiers exposed by the contract.

Step-by-step implementation

1. Prepare Unified Pricing Management

Enable Unified Pricing Management in a sandbox and configure rules, groups, priorities, concurrency, and attributes. Validate the outcome inside SCM first. The API consumes the configured engine; it cannot compensate for incomplete rules or incorrect priority.

2. Register the identity

  1. Create or reuse an application in Microsoft Entra ID.
  2. Generate the secret described by the official guide and protect it in a secret manager.
  3. Request a token for https://<environment>/.default.
  4. Register the client ID in SCM and map it to a dedicated user.
  5. Grant only the privileges required by the service and pricing data.

Do not map the application to a human or global administrator account. Rotate the secret before expiry, and keep tokens, secrets, customer identifiers, and commercial terms out of logs.

3. Build the request as one transaction

Group every line that must participate in the multiline rule into one call. Set calculateSimpleDiscountOnly to false, provide an explicit effective time, and keep company, customer or channel, quantities, units, and pricing attributes together.

Splitting lines into independent calls removes the transaction-wide context. Combining lines from different baskets can apply a discount that should not exist.

4. Handle the result and validity

Define which output the consumer uses and what trace it stores. At minimum, retain effective time, company, customer or channel, products, quantities, currency, final prices, and applied offers. Before confirming a document long after calculation, decide whether to recalculate or honor an earlier contractual quote.

5. Design errors and retries

Use your own correlation identifier and bounded retries for transient failures. Do not repeatedly retry functional errors in product, unit, rule, or permissions. Calculation is read-oriented, but a retry storm can exceed Microsoft’s low- to moderate-frequency profile.

Extensibility

Microsoft documents GUPPricingServiceClass extension points for custom input or output:

  • initHeaderPricingObjectHash for header context;
  • initLinePricingObjectHash for line context;
  • setProductPrice for output fields.

A class extension must use [ExtensionOf(classStr(GUPPricingServiceClass))] and call next. Send custom attributes through SalesOrderProperties or SalesLineProperties, validating name, type, and value before use. Do not alter the standard contract or assign different semantics to the same attribute across channels.

Remaining limits

  • SCM 10.0.47 or later and Unified Pricing Management are required.
  • The API targets low- to moderate-frequency system-to-system integrations.
  • It does not replace Commerce Scale Unit for PDP, POS, e-commerce, burst concurrency, or catalog browsing.
  • Commerce basket pricing and promotion concurrency remain excluded.
  • It does not create an order, reserve inventory, or promise that a result remains valid until confirmation.
  • activeDate and defaulted values can change the result; avoid relying silently on the server clock.
  • The contract uses product record IDs and several case-sensitive names.
  • Test performance with realistic line and rule counts instead of extrapolating from one-line requests.

Test matrix

CaseValidation
One line, calculateSimpleDiscountOnly: trueExisting simple behavior remains unchanged
Multiple lines, falseExpected multiline rule applies with a per-line breakdown
Just below and above a thresholdDiscount appears only when eligible
Two units of measureQuantity conversion does not distort the threshold
Customer and channelCorrect context and affiliations apply
Omitted, blank, and explicit site/warehouseDocumented inheritance is observed
Current and future dateRule and trade-agreement validity is correct
Header and line attributesPriority and conditions match SCM
Two concurrent promotionsCommerce limitation is tested, not assumed equivalent
Low, medium, and burst loadLatency, errors, and retries fit the design

Compare output with an equivalent transaction inside SCM. Verify not only final price but also DiscountLines, origin, currency, unit, quantity, and effective rule.

Practical recommendation

This expansion removes an important barrier for internal B2B quoting and portals: they no longer need to price each line independently when a rule depends on the set. Treat the request as an immutable transaction, use calculateSimpleDiscountOnly: false, and retain the returned breakdown.

Keep the boundary clear. For a public storefront, POS, or a high-volume basket with complex promotion concurrency, Commerce Scale Unit remains the correct API. The new support adds multiline calculation to SCM; it does not make SCM the pricing plane for a high-scale store.

References