Skip to main content

Scenario: Register vendor invoices and approve them later

Goal. Record a vendor invoice the day it arrives, before anyone has decided which expense account it belongs to, and approve it to that account later. The invoice register journal books the liability to the vendor against a pending-invoice account and parks the invoice, unapproved, in the vendor invoice pool. The invoice approval journal takes it out of the pool, moves the amount to the expense account and approves it. Only an approved invoice is picked up for payment.

When to use. Invoices without a purchase order that go through a two-step register-then-approve process, for example when an invoice-capture system registers everything on receipt and a cost-center owner codes it later. If no approval step is needed, post the invoice outright instead (step 1 with a vendor invoice journal name, see the note there). Invoices for purchase orders are posted with Receive and invoice a purchase order.

Prerequisites​

  1. Security. The D365 user your Entra application is mapped to needs MGCLatticeAccountsPayableServiceMaintain for postInvoiceRegisterJournal and postInvoiceApprovalJournal (duty MGCLatticeIntegrationServiceOperationsDuty, in the integration operator and administrator roles). Reading LatticeVendorTransactions comes with MGCLatticeDataEntitiesView, which all three integration roles hold.
  2. Journal names, each with a voucher number sequence (General ledger > Journal setup > Journal names): an invoice register name (USMF APInvReg) and an invoice approval name (USMF APInvApp).
  3. A pending-invoice account. The invoice register posts to the purchasing offset account of the posting profile on the Accounts payable parameters, for the vendor (Accounts payable > Setup > Vendor posting profiles).
  4. An approver. Every register line needs an "Approved by" worker: ApproverPersonnelNumber (USMF 000020), or else the worker linked to the calling user.
  5. An expense account that allows manual entry for the approval (USMF 601500). The Accounts payable control account (200110) is refused.
  6. Lattice. A valid license for the posting operations. To send your own vendor keys, set up an external system and send line VendAccountExternalKey with ExternalSystem (see Cross-reference keys).

Steps​

Both operations belong to MGCLatticeVendorInvoiceJournalService in MGCLatticeAccountsPayableServiceGroup: POST https://<your-environment>/api/services/MGCLatticeAccountsPayableServiceGroup/MGCLatticeVendorInvoiceJournalService/<operation>, OAuth 2.0 client-credentials token, request in _request. Each call creates a journal and posts it; DocumentId and PostedDocumentId are that journal's number.

  1. Register the invoice with postInvoiceRegisterJournal and the invoice register journal name. Leave OffsetMainAccountId out: the line then gets the pending-invoice account, as it does in the journal page. An invoice is a credit to the vendor.

    { "_request": { "RequestId": "3b8e1f60-9d24-4c7a-a5e3-1f0c6d92b748", "LegalEntityId": "USMF",
    "UserContext": "Admin", "JournalNameId": "APInvReg", "Description": "Invoices received 2026-10-07",
    "PostingDate": "2026-10-07",
    "Lines": [ { "VendAccount": "US-101", "InvoiceId": "VINV-2026-0412", "AmountCurCredit": 1000.00,
    "CurrencyCode": "USD", "InvoiceDate": "2026-10-07", "ApproverPersonnelNumber": "000020" } ] } }

    Posting credits the vendor, debits the pending-invoice account, and parks the invoice in the vendor invoice pool. The message says so: "... its invoices are in the vendor invoice pool, awaiting postInvoiceApprovalJournal." Each line also takes the vendor's defaults (terms of payment and the due date they give, method of payment, cash discount, bank account, and the vendor's currency when CurrencyCode is empty).

    To post an invoice outright, with no approval step, send the same request with a vendor invoice journal name (USMF APInvoice) and the expense account in OffsetMainAccountId. The invoice is posted approved and is immediately open for payment; there is nothing to approve.

  2. The invoice waits in the pool. It is a posted, open vendor transaction, but unapproved: payment proposals do not pick it up. In D365 an approver sees it with Find vouchers in an invoice approval journal.

  3. Approve it with postInvoiceApprovalJournal and the approval journal name. Each line names a registered invoice (VendAccount + InvoiceId) and the expense account to approve it to (OffsetMainAccountId, required). The amount is optional; when you send it, it must equal the registered amount. ApproverPersonnelNumber overrides the approver from the register.

    { "_request": { "RequestId": "d61a0c93-4e7f-4b25-8c1d-a0f5e2b39c67", "LegalEntityId": "USMF",
    "UserContext": "Admin", "JournalNameId": "APInvApp", "Description": "Approval of 2026-10-07 invoices",
    "Lines": [ { "VendAccount": "US-101", "InvoiceId": "VINV-2026-0412", "AmountCurCredit": 1000.00,
    "CurrencyCode": "USD", "OffsetMainAccountId": "601500" } ] } }

    The service does what Find vouchers and posting do in the approval journal page: it fetches the invoice from the pool into a new approval journal, adds the expense line for the registered amount, posts, and checks that every invoice is now approved. Posting reverses the pending-invoice entry and books the amount to the expense account. The approval posts on the register's date (or the first open date when that period is closed). The message ends with the number of registered invoices approved.

  4. Pay it. The approved invoice is open for payment proposals and payments: Pay vendors.

How to verify​

  • Both calls: Success true, Status 3, DocumentId = PostedDocumentId = the journal number. The register message mentions the vendor invoice pool; the approval message the count of approved invoices.
  • The invoice is a vendor transaction: GET https://<your-environment>/data/LatticeVendorTransactions?$filter=VendorAccountNumber eq 'US-101' and InvoiceId eq 'VINV-2026-0412' and dataAreaId eq 'USMF'&cross-company=true returns it with its Voucher, DueDate and RemainingTransactionCurrencyAmount.
  • Approval took it out of the pool: approving the same invoice again is refused with InvalidStatus, "is already approved (approval journal ...)".
  • A payment proposal whose due-date window includes the invoice proposes it after the approval, not before.
  • In D365: Accounts payable > Vendors > All vendors, select US-101, then Transactions.
  • Every call is listed on System administration > Inquiries > Lattice > Services > Service call log.

When it goes wrong​

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

  • ValidationFailed, journal name: JournalNameId is missing, unknown, or of the wrong type. The register takes an invoice register or a vendor invoice journal name; the approval only an approval name.
  • ValidationFailed, lines: no lines, an empty line, or a register line whose VendAccount is not a vendor. On the approval: a line without VendAccount, InvoiceId or OffsetMainAccountId, or an amount that differs from the registered one ("was registered for ..., but the request line approves ...").
  • ConfigurationMissing: the journal name has no voucher number sequence; no approver could be resolved (send ApproverPersonnelNumber or link the calling user to a worker); the approver's personnel number does not exist; or the vendor has no purchasing offset account in the posting profile.
  • DimensionValueNotFound: OffsetMainAccountId is not an account of the legal entity's chart of accounts.
  • DocumentNotFound on the approval: the vendor has no invoice with that number, or nothing was ever registered in the legal entity, so it has no vendor invoice pool.
  • InvalidStatus on the approval: the invoice was posted outright and never registered ("nothing to approve"), is already approved, or is held by another approval journal that was not posted. Post or delete that journal first.
  • BusinessRule: D365 refused the posting, for example an invoice number already used for the vendor or a closed period; the D365 messages are in Errors. When an approval journal fails to post, the service hands its invoices back to the pool, so you can send the approval again. When a register journal fails to post, the service deletes the unposted journal, so fix the cause and send the register again; the invoice is not entered twice.
  • Not supported by the approval: splitting one invoice over several expense accounts, a posting date other than the register's, and sales tax recalculation. Use the approval journal page for those.
  • Preview and retries: PreviewMode: true is refused with PreviewUnsupported. Resending a request with the same RequestId returns the recorded journal and never posts twice. If a request stopped between creating and posting its journal, the claim names that journal and the idempotency reconciliation sweep resolves it (see Idempotency reconcile runbook); until then a resend answers RequestInProgress or, after an hour, RequestNeedsReview.

Reference​