Skip to main content

Scenario: Post a budget register entry or a budget transfer

Goal. Your planning or budgeting system sends approved budget amounts to Dynamics 365: an original budget for a set of main accounts in a budget model, and later transfers that move budget from one account to another. Each request becomes a budget register entry that Lattice posts exactly as the Post button on the budget register entry page does, so budget balances and budget control are updated and the entry ends Completed.

When to use. Loading the original budget of a fiscal year, and moving budget between accounts during the year. Ledger postings go through General and intercompany journals. For the retry-safe calling pattern, see Post a journal exactly-once.

Prerequisites​

  1. Security. Both operations need privilege MGCLatticeGeneralLedgerServiceMaintain, in duty MGCLatticeIntegrationServiceOperationsDuty (integration operator and administrator roles). Without it D365 refuses the call with HTTP 403.
  2. Budgeting setup in the legal entity (pages under Budgeting > Setup > Basic budgeting):
    • A budget model for the period that is not stopped. USMF has fiscal-year models FY2016 to FY2031; use the model of the fiscal year your dates fall in (FY2026 for 2026).
    • Budget codes: postBudgetEntry needs a code of type Original budget (USMF Original budget), postBudgetTransfer a code of type Transfer (USMF ExpTransfer or RevTransfer). You send the code's name.
    • The budget register entry number sequence, set on Budgeting parameters.
    • Main accounts that resolve to budget accounts in the legal entity's chart of accounts. USMF: 601500 Travel Expense and 601600 Promotional Expense.
    • If you use budget control, the post runs it; if you use Budget transfer rules, a transfer must be allowed by them.
  3. Lattice. A valid license (a never-registered environment is allowed through; an adverse verdict refuses posting with LicenseNotValid). The budget request takes D365 ids only: there are no external-key members and no Lattice parameters to set.

Steps​

Both operations are on MGCLatticeBudgetRegisterService in service group MGCLatticeGeneralLedgerServiceGroup, and share one request contract. Send a POST with an OAuth 2.0 bearer token and the request in _request (see Trial and day-one guide).

  1. Post the original budget. Send postBudgetEntry with an Original budget code, the model, and one line per account:

    POST https://<your-environment>/api/services/MGCLatticeGeneralLedgerServiceGroup/MGCLatticeBudgetRegisterService/postBudgetEntry
    {
    "_request": {
    "RequestId": "8b7a6c5d-4e3f-4a2b-9c1d-0e9f8a7b6c5d",
    "LegalEntityId": "USMF",
    "UserContext": "planning",
    "BudgetTransactionCode": "Original budget",
    "BudgetModelId": "FY2026",
    "PostingDate": "2026-10-15",
    "Lines": [
    { "MainAccountId": "601500", "Amount": 25000.00, "CurrencyCode": "USD", "Comment": "Operating budget" },
    { "MainAccountId": "601600", "Amount": 15000.00, "CurrencyCode": "USD", "Comment": "Capital budget" }
    ]
    }
    }

    Every line needs MainAccountId and a non-zero Amount. Enter amounts as you would on the budget register entry page; for a revenue account the entry stores the amount with the opposite sign, as the page does. CurrencyCode defaults to the accounting currency, a line's TransDate to PostingDate, and PostingDate to today in the calling user's time zone.

    The service resolves every account first, creates the entry as a draft, then runs the standard budget register post: it updates the budget balances, runs budget control, posts to the general ledger when the budget code requires it, and completes the entry. It answers success only when the entry is Completed: Success true, Status 3, DocumentId = PostedDocumentId = the budget register entry number, and the message "Budget register entry ... was posted with 2 line(s)."

  2. Transfer budget between accounts. Send postBudgetTransfer with a Transfer code. The lines must net to zero: a negative line on the account you take budget from, a positive line on the account you give it to.

    POST https://<your-environment>/api/services/MGCLatticeGeneralLedgerServiceGroup/MGCLatticeBudgetRegisterService/postBudgetTransfer
    {
    "_request": {
    "RequestId": "2c3d4e5f-6a7b-4c8d-9e0f-1a2b3c4d5e6f",
    "LegalEntityId": "USMF",
    "UserContext": "planning",
    "BudgetTransactionCode": "ExpTransfer",
    "BudgetModelId": "FY2026",
    "PostingDate": "2026-10-15",
    "Lines": [
    { "MainAccountId": "601500", "Amount": -5000.00, "CurrencyCode": "USD", "Comment": "Transfer from operating" },
    { "MainAccountId": "601600", "Amount": 5000.00, "CurrencyCode": "USD", "Comment": "Transfer to capital" }
    ]
    }
    }

    A transfer whose lines do not net to zero is refused before anything is created. The answer has the same shape as in step 1.

  3. Read the entry back (optional). Read the standard BudgetRegisterEntryHeaders entity with the entry number from step 1 or 2:

    GET https://<your-environment>/data/BudgetRegisterEntryHeaders?$filter=EntryNumber eq '<entry number>' and dataAreaId eq 'usmf'&cross-company=true

    Status is Completed.

To post many entries in one call, use postBudgetEntryBatch / postBudgetTransferBatch with a _requests list. Both operations can also be queued with submitAsyncOperation (see API reference, sections 19 and 21).

How to verify​

  • The response: Success true, Status 3, DocumentId = PostedDocumentId = the entry number, and a message that reports the number of lines you sent.
  • BudgetRegisterEntryHeaders returns the entry with Status Completed.
  • In D365: Budgeting > Budget register entries lists the entry as Completed, with your lines and comments, the budget code and the budget model.
  • The call: System administration > Inquiries > Lattice > Services > Service call log, or LatticeServiceCallLogs filtered on your RequestId.

When it goes wrong​

A failed request answers Success false, Status 4, an ErrorCode, and the message "Budget register posting failed due to validation or business rule constraints."; the reasons are in Errors.

ErrorCodeTypical cause (text in Errors)What to do
ValidationFailed (1)No RequestId, unknown LegalEntityId; "BudgetTransactionCode is required." / "BudgetModelId is required."; "At least one budget line is required."; "Budget line N has no MainAccountId."; "Line N: Amount is required." (zero amount); "The budget transfer lines total ...; the lines of a budget transfer must net to zero."; "Budget transaction code ... is a ... code; this operation needs a ... code."; "The budget register entry could not be created with budget model ..."; "Budget line N could not be created..."Correct the request and resend.
DocumentNotFound (2)"Budget transaction code ... was not found."Send the code's name exactly as on the Budget codes page.
DimensionValueNotFound (5)"Main account '...' on budget line N could not be resolved to a budget account."Use a main account that exists and is valid for budgeting in the legal entity.
ConfigurationMissing (11)"The budget register entry number sequence is not set up..."; "Legal entity ... has no ledger with a chart of accounts."Fix the setup in D365, then resend.
BusinessRule (4)The standard post refused the entry (budget control, budget transfer rules, ledger posting); or "Budget register entry ... was not posted: its status is ... after the standard post."Read Errors, fix the cause, resend.
PreviewUnsupported (10)PreviewMode true: budget operations have no previewSend without PreviewMode. Nothing was run.
TransientRuntime (6)Infrastructure fault; IsRetryable trueResend with the same RequestId.
DuplicateRequest (7)The RequestId was first used for the other budget operation (an entry's RequestId cannot be reused for a transfer)Send a new RequestId.

A failed post leaves nothing behind. When the standard post fails, the service deletes the draft entry it created (the number is released) and returns the post's errors. The failed request also releases its RequestId, so after you fix the cause you can resend the same request: it creates a new entry.

A stranded request. If the server stops between creating the draft and finishing the post, the request's claim stays in progress and a resend answers RequestNeedsReview after an hour. The reconciliation sweep cannot check budget entries, so it parks such a claim for manual review. Open System administration > Inquiries > Lattice > Services > Service idempotency log, find the claim by RequestId (its Document ID is the entry number), and check the entry on Budget register entries: if it is Completed, use Mark succeeded; if it is still a draft, delete the draft and use Release claim, then resend. Both actions need the integration administrator role. See Idempotency reconcile runbook.

Queued requests. A request submitted with submitAsyncOperation that fails while it runs lands in the dead-letter queue; recover it from the Integration operations console as described in Integration operations.

Reference​