Scenario: Maintain sales prices and ask for the effective price
Goal. Your pricing system (PIM, CPQ, ERP master) owns customer prices. You push each price to D365 as a sales price trade agreement, with a clean timeline: a price with a later start date replaces the current one from that date, and a repeated push changes nothing. Then you ask D365 which price is effective for a customer, item, date and quantity, exactly as a sales order line would price it.
When to use. Price synchronization into D365 and price checks before an order is sent, for example when your web shop shows the D365 price or when you want to know the price an order line will get in Sales order to invoice. The service sets sales prices only (not discounts or purchase prices), and only from today forward.
Prerequisites
- Security (see Security model):
MGCLatticeSalesTradeAgreementServiceMaintaingrantssetSalesPriceandsetSalesPriceBatch;MGCLatticeIntegrationOperatorRoleandMGCLatticeIntegrationAdministratorRolehold it.MGCLatticePriceInquiryServiceViewgrantsresolveSalesPrice, andMGCLatticeTradeAgreementEntityViewgrants read access to the trade agreement entities (LatticeSalesPriceAgreements). Both are inMGCLatticeIntegrationServiceQueryDuty, which all three integration roles hold, includingMGCLatticeIntegrationReadOnlyRole.
- D365 configuration in the legal entity:
- a trade agreement journal name whose default relation is Sales price (Sales and marketing > Setup > Prices and discounts > Trade agreement journal names); the service posts every price through such a journal;
- the price search activated for the account and item levels you use, for example customer and item (Sales and marketing > Setup > Prices and discounts > Activate price/discount);
- the customer (USMF
US-001), the item (USMFD0001) and the currency (USD).
- Lattice setup.
setSalesPriceneeds a valid license (an expired or invalid one answersLicenseNotValid);resolveSalesPriceis a read and is never refused for the license. To name the customer or item by your own key, set up an external system as in Cross-reference keys.
Steps
Both services are in MGCLatticeAccountsReceivableServiceGroup:
POST https://<your-environment>/api/services/MGCLatticeAccountsReceivableServiceGroup/<service>/<operation>, with
the request in _request and an OAuth 2.0 client-credentials token. Neither request has a RequestId:
resolveSalesPrice is a read, and setSalesPrice is idempotent by content (a repeated price answers
Unchanged). Always send LegalEntityId; without it the calling user's current company is used.
-
Push the first price for a customer and item.
setSalesPriceonMGCLatticeSalesTradeAgreementService:{"_request": {"CorrelationId": "6f5e4d3c-2b1a-4098-8e7d-6c5b4a3f2e1d","LegalEntityId": "USMF","AccountCode": 0,"AccountRelation": "US-001","ItemCode": 0,"ItemRelation": "D0001","CurrencyCode": "USD","UnitPrice": 42.50,"FromDate": "2026-10-07","FindNext": true}}AccountCodeandItemCodeare numbers:0Table (one customer or one item),1GroupId (a price group or item group),2All; a name such as"Table"is refused with HTTP 400.UnitIddefaults to the item's sales unit,PriceUnitto 1 andFromQtyto 0 (all quantities);SiteIdandWarehouseIdmake the price dimension-specific.FromDatemay not be before today; an emptyToDatemeans open-ended.UnitPricemust be greater than zero, unless you send"AllowZeroPrice": truefor a free item.Send
FindNext: truefor a key that has no open-ended price yet, such as a first price: without it the service treats the timeline as not simple and answersRejectedComplex. With it, the price is posted as a plain new row and existing rows are not touched.The result is
MGCLatticeSetSalesPriceResult:Successtrue,Outcome1,OutcomeNameCreated,JournalNum(the posted trade agreement journal),CreatedAgreementRecId(the new price row),AppliedFromDate,AppliedToDateandAppliedUnitPrice. -
Push later changes. Send the same key (account, item, currency, unit, quantity break, site and warehouse) with the new values;
FindNextis not needed while the key has one open-ended price. The service decides:You send Outcome ( Outcome)What D365 gets The same FromDateas the open-ended price, anotherUnitPriceEditedInPlace(2)The existing row's price is changed. A later FromDateSuperseded(3)One journal caps the prior row at the day before FromDate(SupersededAgreementRecId,SupersededNewToDate) and adds the new open-ended row.Exactly the current price (same FromDate,UnitPrice,PriceUnit, open-ended)Unchanged(5)Nothing is posted. An earlier FromDatethan the current price, or a key with no open-ended price or severalRejectedComplex(4)Nothing is posted, unless you send FindNext: true.For example,
"UnitPrice": 44.00with"FromDate": "2026-10-07"edits the price in place, and"UnitPrice": 45.00with"FromDate": "2026-11-01"supersedes it: the 44.00 price then ends on 2026-10-31. -
Push many prices at once (optional).
setSalesPriceBatchtakes{ "_requests": [ ... ] }, each item asetSalesPricerequest. Each item is posted in its own journal and does not affect the others. The answer hasSuccess(trueonly when every item succeeded),TotalCount,SuccessCount,FailureCount, the messageN of M sales prices were applied.and oneResultsentry per item, in request order. -
Ask for the effective price.
resolveSalesPriceonMGCLatticePriceInquiryService:{"_request": {"LegalEntityId": "USMF","CustAccount": "US-001","ItemId": "D0001","Qty": 10,"PriceDate": "2026-11-05","CurrencyCode": "USD"}}The service runs the standard D365 sales price search for the customer (its trade agreements and price group) and falls back to the item's base sales price. It answers
Success,PriceFound,UnitPrice,PriceUnit(the quantity the price is for),SourceAgreementRecId(the trade agreement row that won;0means the item's base price),AppliedCurrencyCode,AppliedDateandAppliedQty.Qtydefaults to 1,PriceDateto today,CurrencyCodeto the customer's currency andUnitIdto the item's sales unit. With the prices of step 2, and no other agreement for this customer and item that wins the search, this request answers45.00, and with"PriceDate": "2026-10-15"it answers44.00. It writes nothing.
How to verify
-
The result:
Successtrueand theOutcomeNameyou expected;JournalNumandCreatedAgreementRecIdname what was posted (anUnchangedresult names the existing row and no journal). -
The price rows:
LatticeSalesPriceAgreementslists the sales price trade agreements;SysRecIdis theCreatedAgreementRecIdof the result:GET https://<your-environment>/data/LatticeSalesPriceAgreements?cross-company=true&$filter=dataAreaId eq 'USMF' and AccountRelation eq 'US-001' and ItemRelation eq 'D0001'&$select=SysRecId,Amount,PriceUnit,CurrencyCode,UnitSymbol,FromDate,ToDate -
The effective price:
resolveSalesPricefor a date in each period returns the expectedUnitPrice, withSourceAgreementRecIdequal to the row that applies. -
In D365: Sales and marketing > Prices and discounts > Trade agreement journals lists the posted journal, named
Lattice sales price push. -
Business events, when activated:
MGCLatticeTradeAgreementPriceCreatedBusinessEvent,MGCLatticeTradeAgreementPriceModifiedBusinessEventandMGCLatticeTradeAgreementPriceSupersededBusinessEventfor what the journal changed, and the scheduled Effective and Expired events as the dates are reached (events).
When it goes wrong
setSalesPrice never throws: every refusal answers Success false, an Outcome of RejectedComplex or
Failed, an ErrorCode and the reasons in Errors.
ErrorCode | Typical cause | What to do |
|---|---|---|
ValidationFailed (1) | A missing member (An item relation is required., A currency is required., A from date is required.); a back-dated price (The from date ... is in the past; sales-price pushes must be forward-dated.); a ToDate before FromDate; a zero or negative UnitPrice without AllowZeroPrice; an unknown LegalEntityId; an external key sent with a group or All code, or one that does not resolve | Correct the request. Nothing was posted. |
DocumentNotFound (2) | With code 0, the customer or item does not exist (Customer US-999 was not found in company usmf.) | Check the id and LegalEntityId. |
BusinessRule (4) | RejectedComplex: The price timeline for this key is not a single open-ended price, so no price was created. Set FindNext to accept a plain create.; or the journal line failed D365's trade agreement validation (unit, currency, item or account), or the journal did not post (Trade agreement journal ... was created but did not post; its validation may have blocked it.) | For RejectedComplex, send FindNext: true when a plain new row is what you want, or review the key's prices first. A journal that did not post is deleted again; fix the cause and push again. |
ConfigurationMissing (11) | No trade agreement journal name with default relation Sales price | Create one (see Prerequisites). |
PreviewUnsupported (10) | PreviewMode true: the operation has no preview | Use resolveSalesPrice to see the current price instead. |
LicenseNotValid (12) | The Lattice license is expired or invalid | Renew the license, then push again. |
TransientRuntime (6) | A runtime fault | Push the same request again; an applied price answers Unchanged. |
In a batch, a missing item answers its own Failed result with ValidationFailed; an empty list answers
Success false with At least one request item is required. and no results.
resolveSalesPrice answers ValidationFailed when the customer or item is missing, the legal entity is unknown
or an external key does not resolve, DocumentNotFound when the customer or item does not exist in the company,
BusinessRule when the pricing engine refused, and TransientRuntime for a runtime fault. When no trade
agreement and no base price exist, it answers Success true with PriceFound false and the message
No trade-agreement or base sales price is defined for this item. If it returns an older price than you pushed,
check the dates and that the price search is activated for that account and item level.
Reference
- API reference: section 27 (Price Inquiry Service) and section 28 (Sales Trade Agreement Service)
- Endpoint payload specification:
MGCLatticePriceInquiryServiceandMGCLatticeSalesTradeAgreementService - Security model
- Cross-reference keys
- Business events
- Sales order to invoice