Skip to main content

Scenario: Pay vendors and settle their invoices

Goal. Pay what you owe your vendors and close the invoices you paid. Either D365 proposes the payments for the invoices that are due and you post the proposal, or your bank or treasury system makes the payment and you record it in D365 and settle it against the invoice it pays. Either way the invoice ends settled, with nothing left open.

When to use. The last step of procure-to-pay, after the invoices are posted and approved, for example with Receive and invoice a purchase order or Register vendor invoices and approve them later. Use Part A when D365 decides what to pay (and, for check payments, draws the checks), Part B when the payment was decided and executed outside D365. Pay each invoice through one of the two paths, not both. Part C records an advance paid against a purchase order before it is invoiced, and Part D reverses a payment recorded in error.

Prerequisites​

  1. Security. The D365 user your Entra application is mapped to needs:

    • MGCLatticeAccountsPayableServiceMaintain for generatePaymentProposal and generateVendorPayments (MGCLatticeAccountsPayableServiceGroup);
    • MGCLatticeGeneralLedgerServiceMaintain for postVendorPaymentJournal and settleVendorTransactions (MGCLatticeGeneralLedgerServiceGroup);
    • MGCLatticePaymentReverseMaintain ("Payments - reverse customer and vendor payments (high-risk)") for reverseVendorPayment. It is in the high-risk duty MGCLatticeIntegrationServiceHighRiskDuty of the integration administrator role only.

    The first two privileges are in duty MGCLatticeIntegrationServiceOperationsDuty (integration operator and administrator roles). Reading LatticeVendorTransactions and LatticeVendorSettlements comes with MGCLatticeDataEntitiesView, which all three integration roles hold. The standard VendorPaymentJournalLines entity needs D365's own privileges.

  2. Open, approved invoices. Invoices posted outright, approved after an invoice register, or posted from a purchase order. An invoice still waiting in the vendor invoice pool is not approved and is not proposed.

  3. A vendor payment journal name of type vendor disbursement (Payment, USMF VendPay) with a voucher number sequence (General ledger > Journal setup > Journal names).

  4. Method of payment and bank account. USMF ELECTRONIC posts directly. CHECK (payment type Check, export format Check) is a check payment: a line can post only after Generate payments has drawn its check number, which Lattice does with generateVendorPayments (Part A) or GeneratePayments (Part B). The check is drawn on the bank account the line pays from, which needs a check layout with checks left (USMF USMF OPER: free numbering, the next check is the highest check number + 1). Methods of payment that export a payment file (a file format or an Electronic reporting format) are not generated through Lattice: generate those payments in the vendor payment journal. The bank account is a bank account id (USMF USMF OPER), not a main account.

  5. For a prepayment (Part C): the prepayment posting profile in the Accounts payable parameters (Posting profile with prepayment journal voucher), and an open purchase order of the vendor. When the parameters calculate sales tax on prepayments, the line takes the vendor's sales tax group and the default item sales tax group, as the payment journal does.

  6. For a reversal (Part D): the general ledger number sequences for reversal vouchers and trace numbers, and the Accounts payable closing voucher number sequence. For a check, when the cash and bank management parameters require check reversals to be reviewed (Use review process for payment reversals; USMF does), a journal name of type Check reversal with a voucher number sequence (USMF CheckRev).

  7. Lattice. A valid license for the write operations. To send your own vendor keys, set up an external system and send AccountNumExternalKey with ExternalSystem (see Cross-reference keys).

Steps​

Calls are POST https://<your-environment>/api/services/<ServiceGroup>/<ServiceClass>/<operation> with an OAuth 2.0 client-credentials token and the request in _request. Use a new RequestId per request and the same one when you retry it.

Part A: pay what is due with a payment proposal​

  1. Run the proposal with generatePaymentProposal (MGCLatticeAccountsPayableServiceGroup/MGCLatticeVendorPaymentProposalService). It creates a vendor payment journal and runs the standard vendor payment proposal into it: the open, approved vendor transactions due from FromDueDate through ToDueDate (default today), optionally only those with MethodOfPayment, become payment lines.

    { "_request": { "RequestId": "47c2e9a0-1b5d-4f83-96e4-0a7d3c18b2f6", "LegalEntityId": "USMF",
    "UserContext": "Admin", "JournalNameId": "VendPay", "FromDueDate": "2026-10-01", "ToDueDate": "2026-10-31" } }

    DocumentId and PostedDocumentId are the journal number, and the message gives the counts: "Vendor payment journal ... was created with 3 payment line(s) for 4 proposed open transaction(s) due through ... The journal is not posted." When nothing is due, the response still succeeds, names no journal (DocumentId empty) and says that no payment journal was created. The proposal marks each proposed invoice for settlement by its payment line, so an invoice in an open proposal journal is not proposed again.

  2. Review the lines through the standard entity: GET https://<your-environment>/data/VendorPaymentJournalLines?$filter=JournalBatchNumber eq '<journal>' and dataAreaId eq 'USMF'&cross-company=true.

  3. Generate the checks and post the journal. Lattice leaves the proposal journal unposted so that someone can check it. Then post it in D365 (Accounts payable > Payments > Vendor payment journal, select the journal, open its Lines, Generate payments for check lines, and Post), or let your system do it with generateVendorPayments (same service):

    { "_request": { "RequestId": "91c3e5a7-2b4d-4f6a-8c0e-3d5f7b9a1c4e", "LegalEntityId": "USMF",
    "UserContext": "Admin", "JournalNum": "<journal>", "PostAfterGenerate": true } }

    It generates the payments of the journal as the lines page's Generate payments does for check payments. Each vendor line with payment status None or Rejected whose method of payment needs generation before it can post (USMF CHECK) gets the next check number of the check layout of the bank account it pays from as its payment reference; its payment status becomes Sent, and the check is recorded in the bank account's checks with status Payment, the vendor, the amount and the payment voucher. Lines that post without generation (USMF ELECTRONIC) are left as they are. MethodOfPayment limits the run to the lines with that method, and BankAccountId to the lines that pay from that bank account. With PostAfterGenerate the journal is posted in the same transaction: if the posting fails, no check is used either. Without it the journal stays open with its checks drawn.

    DocumentId is the journal; PostedDocumentId the first payment voucher when the journal was posted, otherwise the first check number. The message lists the checks ("Generated 2 check payment(s) in vendor payment journal ...: check(s) 792, 793. The journal is posted; the first payment voucher is ..."), and InformationalMessages has one line per check with its vendor, amount, voucher and bank account. The check document is not printed: the standard check report prints only to a network printer or to a file sent to a user's browser, which a service call cannot use. Print check copies in D365 when you need them (the journal's Print > Check copy, or the bank account's checks). A journal whose lines need no generation, or whose checks are already generated, can also be posted with postLedgerJournal (Post an existing journal).

    Posting pays the vendors and settles the invoices the proposal marked.

Part B: record a payment made elsewhere and settle it​

  1. Post the payment with postVendorPaymentJournal (MGCLatticeGeneralLedgerServiceGroup/MGCLatticePaymentJournalService). It creates a vendor payment journal with your lines and posts it. A vendor payment debits the vendor (AmountCurDebit); BankAccountId is the bank it was paid from. A vendor payment line takes no InvoiceId: you name the invoice when you settle.

    { "_request": { "RequestId": "e8b31d74-0c6a-4925-bf18-6d4a2e90c157", "LegalEntityId": "USMF",
    "UserContext": "Admin", "JournalNameId": "VendPay", "Description": "Bank run 2026-10-07",
    "PostingDate": "2026-10-07",
    "Lines": [ { "AccountNum": "US-101", "AmountCurDebit": 750.00, "CurrencyCode": "USD",
    "BankAccountId": "USMF OPER", "MethodOfPayment": "ELECTRONIC", "Description": "Payment VINV-2026-0388",
    "TransDate": "2026-10-07" } ] } }

    DocumentId and PostedDocumentId are the journal number; success is reported only when the journal posted.

    A check payment posts in the same call with "GeneratePayments": true on the request: each line whose method of payment needs generation ("MethodOfPayment": "CHECK") gets the next check number of its BankAccountId's check layout before the journal posts, in one transaction, and the message lists the check numbers (Vendor payment journal ... posted successfully with check(s) 794.). Such a line must send BankAccountId. Without GeneratePayments (the default) D365 refuses a check line at posting with "Check number must be filled in" (BusinessRule), and the journal is deleted.

  2. Find the payment's voucher. A vendor payment is settled by its voucher. Read the vendor's payment transactions of the day: GET https://<your-environment>/data/LatticeVendorTransactions?$filter=VendorAccountNumber eq 'US-101' and TransactionType eq Microsoft.Dynamics.DataEntities.LedgerTransType'Payment' and TransactionDate eq 2026-10-07 and dataAreaId eq 'USMF'&$select=Voucher,TransactionCurrencyAmount,SysRecId&cross-company=true. Your payment is the row with its amount; the newest one has the highest SysRecId.

  3. Settle it against the invoice with settleVendorTransactions (MGCLatticeGeneralLedgerServiceGroup/MGCLatticePaymentJournalService). Name the payment by PaymentVoucher (or PaymentTransRecId, the transaction's SysRecId) and each invoice by InvoiceId (or InvoiceTransRecId). SettleAmountCur is the amount to settle on that invoice; 0 or omitted settles its whole open balance, and no more than the open balance is ever settled.

    { "_request": { "RequestId": "5a0f7c3e-d218-4b96-a4e0-8c1b6f29d375", "LegalEntityId": "USMF",
    "UserContext": "Admin", "AccountNum": "US-101", "PaymentVoucher": "VPAY-000123",
    "Lines": [ { "InvoiceId": "VINV-2026-0388", "SettleAmountCur": 750.00 } ] } }

    DocumentId is the payment's voucher; a settlement creates no document, so PostedDocumentId is empty.

  4. Read the settlement back from LatticeVendorSettlements ($filter=VendorAccountNumber eq 'US-101' and dataAreaId eq 'USMF'&cross-company=true). Each side of a settlement has its own row: TransRecId is the settled transaction and OffsetRecId the one it was settled against, so the pair of the invoice's and the payment's SysRecId (in either order) is your settlement, with SettledTransactionCurrencyAmount and SettlementVoucher. The invoice's SysRecId is on its LatticeVendorTransactions row (InvoiceId eq 'VINV-2026-0388', the row that is not a payment).

Part C: pay an advance against a purchase order​

  1. Post the payment as a prepayment with postVendorPaymentJournal, as in step 4, with the line marked IsPrepayment and the order in PurchaseOrderId (or PurchaseOrderIdExternalKey with ExternalSystem):

    { "_request": { "RequestId": "b3d5f7a9-1c2e-4f60-8a4c-6e8f0a2c4e71", "LegalEntityId": "USMF",
    "UserContext": "Admin", "JournalNameId": "VendPay", "Description": "Advance PO 00001042",
    "PostingDate": "2026-10-07",
    "Lines": [ { "AccountNum": "US-101", "IsPrepayment": true, "PurchaseOrderId": "00001042",
    "AmountCurDebit": 100.00, "CurrencyCode": "USD", "BankAccountId": "USMF OPER",
    "MethodOfPayment": "ELECTRONIC", "Description": "Advance for 00001042", "TransDate": "2026-10-07" } ] } }

    The prepayment posts with the prepayment posting profile of the Accounts payable parameters (without one, D365 uses the default posting profile and Warnings says so). The order is checked before the journal is created: it must exist, be invoiced by the line's vendor, and be neither invoiced nor canceled. After posting, the payment is marked against the order as the order's settlement does, and InformationalMessages names the order. When the order is invoiced (Receive and invoice a purchase order), its invoice settles the prepayment. When the vendor sends a prepayment invoice for the order instead, post it with Post a vendor prepayment invoice for a purchase order and pay it like any invoice.

Part D: reverse a payment​

  1. Reverse the payment with reverseVendorPayment (MGCLatticeGeneralLedgerServiceGroup/MGCLatticePaymentJournalService). It reverses a posted payment voucher of the vendor with the standard reversal that covers it:

    The payment wasReversed by
    Made by check (a check of the payment's bank account)The check reversal (Payment reversal on the Checks page). When the cash and bank management parameters require check reversals to be reviewed in a check reversal journal (USMF does), it creates that journal with the canceling line and the check becomes Pending cancellation; the journal is left for review, or posted in the same request with PostReversalJournal.
    Not posted to a bank accountThe vendor transaction reversal. A settled payment is refused: unsettle it first.
    Posted to a bank account, not by check (as in step 4)Nothing: D365 has no standard reversal for it, so the operation refuses it with BusinessRule. Correct it in D365.
    { "_request": { "RequestId": "d7f9b1c3-5e6a-4b8d-9f2a-4c6e8a0b2d93", "LegalEntityId": "USMF",
    "UserContext": "Admin", "AccountNum": "US-101", "Voucher": "VPAY-000123",
    "ReversalDate": "2026-10-08", "ReasonCode": "ERROR", "ReasonComment": "Duplicate payment" } }

    ReversalDate defaults to today and cannot be before the payment date. ReasonCode is required when the parameters demand one (cash and bank management Require reasons for payment reversals for a check). DocumentId is the reversed payment voucher and PostedDocumentId the reversal voucher.

    For a check under the review setting, ReversalJournalNameId names the check reversal journal name (USMF CheckRev; without it, the legal entity's only one), and "PostReversalJournal": true posts that journal in the same request, which reverses the payment and its settlements and cancels the check (PostedDocumentId is then the reversal voucher). Without it the journal is left for review on Cash and bank management > Payment reversal approvals > Check reversals, the check is Pending cancellation, and PostedDocumentId is the check reversal journal; posting that journal in D365 completes the reversal.

How to verify​

  • Proposal: Success true and a journal number; VendorPaymentJournalLines for that journal has as many lines as the message reports. After you post it, the proposed invoices are settled.
  • Checks: every check line of the journal has its CheckNumber (and PaymentReference) in VendorPaymentJournalLines; in D365, the journal lines show payment status Sent, and Cash and bank management > Bank accounts, select USMF OPER, then Checks, lists each check with status Payment, the vendor and the voucher. After posting, LatticeVendorTransactions and LatticeBankAccountTransactions have the payment voucher. The Vendor check issued business event fires for each check, if you have activated it.
  • Payment: Success true, Status 3, the journal number in DocumentId, and a Payment transaction in LatticeVendorTransactions. Resending the same request returns "Duplicate request detected" with the same journal number and posts nothing.
  • Settlement: DocumentId is the payment voucher, LatticeVendorSettlements has the invoice/payment pair, and the invoice's RemainingTransactionCurrencyAmount in LatticeVendorTransactions is 0 with a ClosedDate once fully settled.
  • In D365: Accounts payable > Vendors > All vendors, select US-101, then Transactions.
  • Prepayment: Success true and InformationalMessages naming the order; once the order is invoiced, the prepayment's SettledTransactionCurrencyAmount in LatticeVendorTransactions is no longer 0.
  • Reversal: Success true, DocumentId the payment voucher, and LatticeVendorTransactions has a transaction on the returned PostedDocumentId with the opposite amount. A check reversal left for review returns the check reversal journal instead: the check is Pending cancellation on the bank account's Checks until the journal is posted, then Canceled.
  • The Vendor payment journal posted business event fires after a vendor payment journal posts (from the API or from the journal page), once per vendor posting, if you have activated it.

When it goes wrong​

ErrorCode arrives as an integer (1 ValidationFailed, 2 DocumentNotFound, 3 InvalidStatus, 4 BusinessRule, 11 ConfigurationMissing).

  • Proposal finds nothing (Success true, no DocumentId): no open, approved invoice is due in the window with that method of payment. Check that the invoices are approved (not still in the invoice pool) and not already in another unposted proposal journal.
  • Proposal, ValidationFailed: JournalNameId missing, unknown or not a vendor payment journal name, or FromDueDate later than ToDueDate. DocumentNotFound: unknown MethodOfPayment. A failure after the journal was created deletes the empty journal again.
  • ConfigurationMissing (proposal or payment): the journal name has no voucher number sequence.
  • Generate payments, ValidationFailed: no JournalNum; a journal that is not a vendor payment journal; an unknown method of payment or bank account; an inactive bank account; a line that pays from no bank account; a method of payment that exports a payment file or uses an Electronic reporting format ("Only check payments can be generated through the service; generate payment files in the vendor payment journal."); a journal without lines or without a line to generate. DocumentNotFound: an unknown journal. InvalidStatus: the journal is posted, blocked by the system or in use by another user, needs workflow approval and is not approved, or "The payments of journal ... are already generated". ConfigurationMissing: the method of payment has no export format, the bank account has no check layout, or a fixed check numbering has no created checks left. BusinessRule: the standard generation skipped a line (its messages are in Errors) or the posting failed; nothing is generated then either.
  • Payment, ValidationFailed: journal name missing, unknown or of the wrong type; no lines or an empty line; AccountNum is not a vendor; BankAccountId is not a bank account; a line carries InvoiceId (identify the payment by its voucher when you settle it); with GeneratePayments, a check line without BankAccountId, a line whose method of payment exports a payment file, or GeneratePayments on a customer payment journal. ConfigurationMissing with GeneratePayments: as for Generate payments above. Nothing was created.
  • Payment, BusinessRule: D365 refused to post the journal, for example a check line sent without GeneratePayments ("Check number must be filled in"). The messages are in Errors. The service deletes the journal it created, so fix the cause and send the payment again.
  • Settle, ValidationFailed: AccountNum missing or not a vendor, no PaymentVoucher / PaymentInvoiceId / PaymentTransRecId, no lines, or a line without InvoiceId or InvoiceTransRecId.
  • Settle, DocumentNotFound: no payment transaction of this vendor with that voucher or RecId, or no invoice transaction of this vendor with that number or RecId. A RecId of another vendor's transaction, or of a payment used as an invoice, is refused the same way.
  • Settle, BusinessRule: the payment or the invoice has no open balance left ("has no open balance to settle"), or nothing was settled. A payment posted as a credit to the vendor is a refund, not a payment, and cannot be settled against an invoice.
  • Prepayment, ValidationFailed: a sales order on a vendor line ("SalesOrderId applies to customer payments only."), an unknown purchase order, or an order of another vendor. InvalidStatus: the order is invoiced or canceled. Nothing was created.
  • Reversal, ValidationFailed: no AccountNum or Voucher, an unknown vendor or reason code, a reversal date before the payment date, a reason the parameters require, a ReversalJournalNameId that is unknown or not a check reversal journal name, or several check reversal journal names and none requested. DocumentNotFound: "Vendor payment voucher ... was not found on account US-101." InvalidStatus: the payment is already reversed, or its check is already reversed, pending reversal (a check reversal journal exists for it) or not in status Payment. BusinessRule: "Vendor payment ... was posted to bank account ... and was not made by check. A vendor payment posted to a bank account can only be reversed by a check reversal."; a settled payment not posted to a bank account ("Unsettle it before you reverse it."). ConfigurationMissing: a reversal number sequence is not set up; the parameters require a check reversal journal but no check reversal journal name exists, or it has no voucher number sequence. A refused reversal reverses nothing.
  • Preview and retries: PreviewMode: true is refused with PreviewUnsupported. Resending a request with the same RequestId returns the recorded result: a payment journal is never posted twice, a settlement never applied twice and a payment never reversed twice. A request that stopped between creating and posting its payment journal is resolved by the idempotency reconciliation sweep (see Idempotency reconcile runbook).

Reference​