Skip to main content

Scenario: Receive and invoice a purchase order

Goal. Take a purchase order through the vendor side of procure-to-pay: confirm it to the vendor, post the product receipt when goods arrive (all of them or only part), post the vendor's invoice for what was received, and read the order's progress back at each step. Your warehouse or AP system makes the calls; D365 posts the documents.

When to use. Any purchase order whose receipt and invoice are recorded outside D365, for example a warehouse system that scans deliveries or an invoice-capture tool that reads vendor invoices. The order can come from an approved requisition (Turn a purchase requisition into a purchase order) or be created directly. To pay the posted invoice, continue with Pay vendors; for goods going back to the vendor, see Return goods to a vendor. To take back a product receipt posted in error, see Cancel a posted product receipt; for a prepayment the vendor invoices up front, see Post a vendor prepayment invoice for a purchase order; for invoices entered as pending vendor invoices, see Post a pending vendor invoice.

Prerequisites​

  1. Security. The D365 user your Entra application is mapped to needs MGCLatticeAccountsPayableServiceMaintain for confirmPurchaseOrder, postPurchasePackingSlip and invoicePurchaseOrder (duty MGCLatticeIntegrationServiceOperationsDuty), and MGCLatticeAccountsPayableServiceView for getPurchaseOrderStatus (duty MGCLatticeIntegrationServiceQueryDuty). The integration operator and administrator roles hold both. Creating and reading the order through the standard entities (PurchaseOrderHeadersV2, PurchaseOrderLinesV2 and the read-back entities below) needs D365's own privileges for those entities.
  2. D365 configuration. The vendor (USMF US-101) with a posting profile; a purchase order number sequence; a receiving site and warehouse on each line (a product receipt without a warehouse posts nothing); and, when purchase order change management is on, an approved order before it is confirmed. The vendor's invoice number must be new for that vendor and, unless extended invoice numbers are enabled, at most 20 characters.
  3. Lattice. A valid license for the posting operations. To send your own order keys instead of D365 order numbers, set up an external system and send PurchaseOrderIdExternalKey with ExternalSystem (see Cross-reference keys).

Steps​

All four operations belong to MGCLatticePurchaseOrderPostingService in MGCLatticeAccountsPayableServiceGroup: POST https://<your-environment>/api/services/MGCLatticeAccountsPayableServiceGroup/MGCLatticePurchaseOrderPostingService/<operation>, OAuth 2.0 client-credentials token, request in _request. Use a new RequestId for each step and the same one when you retry that step. In each posting response DocumentId is the purchase order and PostedDocumentId the document the step posted.

  1. Have an open purchase order and know its line numbers. To create one, POST a header to https://<your-environment>/data/PurchaseOrderHeadersV2:

    { "dataAreaId": "USMF", "OrderVendorAccountNumber": "US-101", "CurrencyCode": "USD" }

    and a line to https://<your-environment>/data/PurchaseOrderLinesV2 with the returned PurchaseOrderNumber:

    { "dataAreaId": "USMF", "PurchaseOrderNumber": "00001042", "LineNumber": 1, "ItemNumber": "D0001",
    "OrderedPurchaseQuantity": 10, "PurchasePrice": 50, "ReceivingSiteId": "1", "ReceivingWarehouseId": "11" }

    The posting requests name order lines by LineNum, the line's LineNumber in PurchaseOrderLinesV2.

  2. Confirm the order with confirmPurchaseOrder. A confirmation always covers the whole order, so send no Lines:

    { "_request": { "RequestId": "6b1e0d47-2c8a-4f53-9e61-0d4c7a2b8f15", "LegalEntityId": "USMF",
    "UserContext": "Admin", "PurchaseOrderId": "00001042", "PostingDate": "2026-10-07" } }

    PostedDocumentId is the confirmation number.

  3. Post the product receipt with postPurchasePackingSlip. PackingSlipId is the receipt number from the delivery note and is required. With Lines, only the named lines and quantities are received; without Lines, everything not yet received is. Half of the line arrived:

    { "_request": { "RequestId": "f03a9c12-7d4b-4e8f-b2a6-5c1e9d07a3b4", "LegalEntityId": "USMF",
    "UserContext": "Admin", "PurchaseOrderId": "00001042", "PackingSlipId": "GRN-104201",
    "PostingDate": "2026-10-07", "Lines": [ { "LineNum": 1, "ItemId": "D0001", "Qty": 5 } ] } }

    PostedDocumentId is GRN-104201. The other 5 stay open on the order.

  4. Post the vendor invoice with invoicePurchaseOrder. InvoiceId is the vendor's invoice number and is required. An order with product receipts is invoiced from them: either send no Lines (everything received and not yet invoiced is invoiced), or send every line that has a received, uninvoiced quantity with exactly that quantity:

    { "_request": { "RequestId": "2c7d5e90-41af-4b6c-8d03-e9a1f6b42c7d", "LegalEntityId": "USMF",
    "UserContext": "Admin", "PurchaseOrderId": "00001042", "InvoiceId": "VINV-88412",
    "PostingDate": "2026-10-07", "Lines": [ { "LineNum": 1, "ItemId": "D0001", "Qty": 5 } ] } }

    PostedDocumentId is VINV-88412. An order without product receipts is invoiced for its ordered quantity, or for the lines and quantities you send.

  5. Check progress with getPurchaseOrderStatus (same service and group; read-only, no idempotency):

    { "_request": { "RequestId": "9a4f2b18-6e3c-4d71-a05b-3f8c2e6d1b90", "LegalEntityId": "USMF", "DocumentId": "00001042" } }

    DocumentStatus is the order's status (Backorder, Received, Invoiced, Canceled). StatusDetails lists PurchStatus and OrderAccount, then ConfirmDate, ReceiptDate and InvoiceDate (as yyyy-MM-dd) once a confirmation, receipt or invoice exists. After this partial receipt and invoice the order is still Backorder, because 5 are open.

  6. Repeat steps 3 and 4 for the rest of the delivery, each with a new receipt number, invoice number and RequestId.

How to verify​

  • Each posting step: Success true, Status 3, DocumentId = the order, PostedDocumentId = the confirmation, the receipt number you sent, or the invoice number you sent.
  • getPurchaseOrderStatus shows the ConfirmDate, ReceiptDate and InvoiceDate details.
  • The posted quantities, through the standard entities (add dataAreaId eq 'USMF' and cross-company=true): PurchaseOrderConfirmationLines (OrderedPurchaseQuantity by PurchaseOrderNumber and LineNumber), ProductReceiptLines (ReceivedPurchaseQuantity by PurchaseOrderNumber, PurchaseOrderLineNumber and ProductReceiptNumber) and VendInvoiceTransBiEntities (Qty by PurchID, PurchaseLineLineNumber and InvoiceId).
  • In D365: Procurement and sourcing > Purchase orders > All purchase orders, open the order; its confirmation, product receipt and invoice journals are listed from the action pane.
  • Resending a step with the same RequestId returns the same PostedDocumentId and posts nothing again.

When it goes wrong​

ErrorCode arrives as an integer (1 ValidationFailed, 2 DocumentNotFound, 4 BusinessRule, 10 PreviewUnsupported); Errors carries the D365 messages. Request checks fail before anything is posted.

  • ValidationFailed on confirm, "send the request without Lines": a purchase order confirmation cannot confirm part of an order. Send the request without Lines.
  • ValidationFailed, PackingSlipId or InvoiceId required: send the receipt or invoice number.
  • ValidationFailed, "only ... is left to post": the quantity is more than the line has left to receive (or to invoice). Read the order's lines and send at most the open quantity.
  • ValidationFailed on invoice, "must name every line with a received, uninvoiced quantity": the order has product receipts, so the invoice must cover exactly what was received and not invoiced. Send no Lines, or correct the quantities.
  • ValidationFailed, line not found or does not match: LineNum names no line of the order, or the ItemId you sent with it is not that line's item.
  • DocumentNotFound: no such purchase order in LegalEntityId.
  • BusinessRule, "posted nothing": D365 found nothing to post, for example the order is not confirmed yet, has no open quantity, or waits for change-management approval.
  • BusinessRule, receipt or invoice number "already used": that number was posted before for the vendor. Use the vendor's real, new number.
  • Preview: PreviewMode: true is refused with PreviewUnsupported; these operations have no preview.
  • Retries: TransientRuntime and RequestInProgress are safe to retry with the same RequestId. A request stopped mid-flight for more than an hour answers RequestNeedsReview: check in D365 whether the document posted, then have an operator close the claim (see Idempotency reconcile runbook).

Reference​