Scenario: Take a sales order from entry to invoice
Goal. An order arrives from your web shop, EDI hub or CRM. You create it in D365 as a sales order for the customer, confirm it, ship it (post the packing slip), invoice it and read back where it stands. Every posting happens exactly once, even when your system sends a step again after a timeout.
When to use. Any order-to-cash integration in which the external system drives the D365 sales documents. Each posting step can be checked first with a validation-only preview. For goods that come back, continue with Sales return to credit note; to collect the invoice, see Customer payment and settlement; to bill something that is not on an order, see Free text invoice. To take back a packing slip posted in error before it is invoiced, see Cancel a posted sales packing slip. When orders arrive as files, put Inbound file to posted document in front of this flow.
Prerequisites
-
Security. The D365 user your Entra application is mapped to needs these privileges (see Security model):
MGCLatticeSalesOrderCaptureServiceMaintainforcreateSalesOrderandcreateSalesOrderBatch;MGCLatticeAccountsReceivableServiceMaintainforconfirmSalesOrder,postSalesPackingSlip,invoiceSalesOrderand their batch variants;MGCLatticeAccountsReceivableServiceView(orMGCLatticePostingPreviewExecute) forgetSalesOrderStatus.
MGCLatticeIntegrationOperatorRoleandMGCLatticeIntegrationAdministratorRolehold all of them, and their Lattice entity duties cover theLatticeCustomerPackingSlipJournalLinesread-back. The standard entities this page also uses (SalesOrderHeadersV2,SalesOrderLines,SalesOrderConfirmationLines,SalesInvoiceHeaders,SalesInvoiceLines) are Microsoft entities: they need the standard D365 privileges. -
D365 configuration in the legal entity:
- number sequences for sales orders, confirmations, packing slips and invoices (Accounts receivable > Setup > Accounts receivable parameters, Number sequences tab);
- the customer (USMF
US-001) with a customer posting profile, and the released item (USMFD0001); - on-hand inventory where the line ships from (in USMF, site
1/ warehouse11stocksD0001), unless the item allows negative physical inventory; - a sales price: on the line, from a trade agreement or from the item's base price (see Sales prices and price inquiry).
-
Lattice setup. A valid license: an expired or invalid license refuses the create and posting operations with
LicenseNotValid. To send your own order keys instead of D365 order numbers, set up an external system (System administration > Setup > Lattice > External systems) as described in Cross-reference keys. If an administrator turned on the inbound order policy (System administration > Setup > Lattice > Inbound order policy parameters, off by default), orders created through the integration can be held for price tolerance or confirmed automatically; see Integration operations.
Steps
Every service call is POST https://<your-environment>/api/services/MGCLatticeAccountsReceivableServiceGroup/<service>/<operation>
with an OAuth 2.0 client-credentials token (Trial and day-one guide) and the request in
_request. Give each step its own new RequestId and store it with the step: a resend of the same step must
carry the same RequestId, and a RequestId already used by another operation is refused.
-
Create the order. Choose one of two ways.
a. One call:
createSalesOrder(serviceMGCLatticeSalesOrderCaptureService, path/api/services/MGCLatticeAccountsReceivableServiceGroup/MGCLatticeSalesOrderCaptureService/createSalesOrder). The order is created with the standard D365 defaulting and validation, in one transaction.{"_request": {"RequestId": "5d0c2b4e-7f1a-4c3d-9e8b-1a2f3c4d5e60","LegalEntityId": "USMF","UserContext": "Admin","CustAccount": "US-001","CustomerRef": "PO-88231","Lines": [{ "ItemId": "D0001", "Qty": 10, "UnitPrice": 100, "SiteId": "1", "WarehouseId": "11" }]}}DocumentIdandPostedDocumentIdare the new order number (for example001367); the message readsSales order 001367 created for customer US-001 with 1 line(s).AUnitPriceof 0 or none takes the trade agreement or default price. This operation has no preview.b. The standard entities.
POST https://<your-environment>/data/SalesOrderHeadersV2with the header, thenPOST .../data/SalesOrderLineswith each line:{ "dataAreaId": "USMF", "OrderingCustomerAccountNumber": "US-001", "InvoiceCustomerAccountNumber": "US-001","CurrencyCode": "USD", "PaymentTermsName": "Net30", "SalesOrderName": "Web order PO-88231" }{ "dataAreaId": "USMF", "SalesOrderNumber": "001367", "ItemNumber": "D0001", "OrderedSalesQuantity": 10,"SalesPrice": 100, "ShippingSiteId": "1", "ShippingWarehouseId": "11" }The header create returns
SalesOrderNumber. These two calls are not covered by a LatticeRequestId.What the next steps need: the order number and, to post single lines, each line's number. Read it with
GET .../data/SalesOrderLines?cross-company=true&$filter=dataAreaId eq 'USMF' and SalesOrderNumber eq '001367'&$select=LineNumber,ItemNumber,OrderedSalesQuantity. A request line may also name its order line byItemIdalone when only one line of that item still has a quantity to post. -
Preview the posting (optional). Send the confirmation of step 3 with
"PreviewMode": true. The service checks the request, finds the order and resolves the lines and quantities, then answersSuccesstrue,PreviewModetrue,PreviewAppliedtrue,PreviewFidelity1and the messagePreview (ValidationOnly): sales order confirmation request for 001367 is valid. Nothing was posted.No document is posted, no number is used and theRequestIdstays free, so you can post with it next.postSalesPackingSlipandinvoiceSalesOrderpreview the same way. A preview does not run the posting itself: a stock or posting-setup problem only shows on the real post. -
Confirm.
confirmSalesOrder(serviceMGCLatticeSalesOrderPostingService, path/api/services/MGCLatticeAccountsReceivableServiceGroup/MGCLatticeSalesOrderPostingService/confirmSalesOrder). WithoutLinesthe whole order is confirmed. WithLines, only the selected lines are confirmed, each for itsQty; this confirms 5 of the 10 ordered:{"_request": {"RequestId": "8a6f1e2d-3b4c-4d5e-a6f7-081920a1b2c3","LegalEntityId": "USMF","UserContext": "Admin","SalesOrderId": "001367","PostingDate": "2026-10-07","Lines": [ { "LineNum": 1, "ItemId": "D0001", "Qty": 5 } ]}}DocumentIdis the order andPostedDocumentIdthe confirmation number:Sales order 001367 confirmed with confirmation CCF-00000128. -
Post the packing slip.
postSalesPackingSlip, same service and request shape. WithoutLines, everything not yet delivered ships; withLines, the selected lines and quantities.PostedDocumentIdis the packing slip number:Packing slip SPK-00002040 posted for sales order 001367. -
Invoice.
invoiceSalesOrder, same service and request shape. WithoutLines, an order with packing slips is invoiced for the delivered quantity not yet invoiced (an order without packing slips for its ordered quantity).PostedDocumentIdis the invoice number:Invoice CIV-00000744 posted for sales order 001367.Keep it: it is the invoice a customer payment is settled against. -
Read the status.
getSalesOrderStatusonMGCLatticeSalesOrderPostingService:{ "_request": { "RequestId": "c41d7e90-25f6-4b8a-9c3d-6e7f8a9b0c1d", "LegalEntityId": "USMF","UserContext": "Admin", "DocumentId": "001367" } }The answer carries
DocumentStatus(the sales status, for exampleInvoiced) andStatusDetails, a list ofName/Valuepairs:SalesStatus,CustAccount, andConfirmDate,PackingSlipDateandInvoiceDateonce those documents exist. The query writes nothing.
Many orders at once. createSalesOrderBatch, confirmSalesOrderBatch, postSalesPackingSlipBatch and
invoiceSalesOrderBatch take { "_requests": [ ... ] }, each item a full request with its own RequestId.
Every item runs on its own: the answer has TotalCount, SuccessCount, FailureCount, Status (3 all
succeeded, 4 all failed, 6 PartialSuccess) and one Results entry per item with its ItemIndex,
Success, ErrorCode, DocumentId and PostedDocumentId.
How to verify
- The response:
Successtrue,Status3,DocumentIdthe order, andPostedDocumentIdthe confirmation, packing slip or invoice number. An operation that posts nothing fails instead of answering success. - The status query: after the confirmation
StatusDetailshasConfirmDate(a confirmation does not changeDocumentStatus, which staysBackorder); after the packing slip it hasPackingSlipDate, andDocumentStatusisDeliveredonce everything is delivered; after the invoice it hasInvoiceDateandInvoiced. - OData read-back (add
cross-company=trueand filter ondataAreaId eq 'USMF'):SalesOrderConfirmationLines(ConfirmedSalesQuantitybySalesOrderNumberandLineNumber),LatticeCustomerPackingSlipJournalLines(QuantitybySalesOrderNumberandLineNumber),SalesInvoiceHeaders(InvoiceNumberbySalesOrderNumber) andSalesInvoiceLines(InvoicedQuantitybyInvoiceNumber). - In D365: Sales and marketing > Sales orders > All sales orders, open the order; its document status shows the last document posted, and the confirmation, packing slip and invoice journals open from the order's action pane.
- Call log: System administration > Inquiries > Lattice > Services > Service call log has one row per call with its status, error code and posted document (previews are flagged).
- Business events, when activated:
MGCLatticeSalesOrderConfirmedBusinessEvent,MGCLatticeSalesPackingSlipPostedBusinessEventandMGCLatticeSalesOrderInvoicePostedBusinessEvent(events).
When it goes wrong
ErrorCode | Typical cause in this flow | What to do |
|---|---|---|
ValidationFailed (1) | No RequestId or unknown LegalEntityId; no order named (SalesOrderId is required.); a request line that matches no order line, names a line twice, has an ItemId that differs from the line, or asks for more than is left (Request line 1 asks to post 12.00 of line 1.00 of order 001367, but only 10.00 is left to post.); all lines unselected; createSalesOrder without CustAccount, lines, ItemId or Qty; an external key that does not resolve | Correct the request and send it again. Nothing was posted. |
DocumentNotFound (2) | The order is not in the request's legal entity (Sales order 001999 was not found.); createSalesOrder for a customer that does not exist | Check the number and LegalEntityId. |
BusinessRule (4) | D365 refused the posting (for example not enough on-hand for the packing slip, a missing number sequence or posting profile; D365's reasons are in Errors), or there was nothing to post (Invoice of sales order 001367 posted nothing: no Invoice journal was created. Check that the order has open lines to post.) | Fix the data or setup in D365, then resend. A failed posting is rolled back completely. |
TransientRuntime (6) | A runtime fault; IsRetryable is true | Resend the same request with the same RequestId, with backoff. |
DuplicateRequest (7) | The RequestId was already used for another operation (for example the confirmation's RequestId sent to postSalesPackingSlip) | Use a new RequestId for each step. |
RequestInProgress (8) | The same RequestId is still running | Retry later; it is not run twice. |
RequestNeedsReview (9) | An earlier attempt with this RequestId stopped without a result | Check whether it posted (status query, journals); the idempotency reconciliation sweep resolves the stranded request. |
PreviewUnsupported (10) | PreviewMode true sent to createSalesOrder | Create the order without preview. |
LicenseNotValid (12) | The Lattice license is expired or invalid; previews still run | Renew the license, then resend. |
Retries are safe. Sending a successful request again with the same RequestId posts nothing: it answers
Success true with the original DocumentId and PostedDocumentId, the message
Duplicate request detected. Original request processed at ... and, in InformationalMessages,
This is a cached response from a previous execution of the same RequestId. A failed request is not
remembered, so the same RequestId runs again once you have fixed the cause. Inside a batch, an item that
repeats an earlier item's RequestId replays that item. HTTP 401, 403 and 404 come from authentication,
security or an undeployed service group before Lattice runs; see Trial and day-one guide.
Reference
- API reference: sections 1 (Sales Order Posting Service), 1a (Sales Order Capture Service), 19 (Bulk Operations), 20 (Status Query Operations) and 30 (Posting Preview)
- Endpoint payload specification:
MGCLatticeSalesOrderPostingServiceandMGCLatticeSalesOrderCaptureService - Posting preview
- Security model
- Cross-reference keys
- Integration operations
- Post a journal exactly-once
- Sales return to credit note, Customer payment and settlement