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
- Security. Both operations need privilege
MGCLatticeGeneralLedgerServiceMaintain, in dutyMGCLatticeIntegrationServiceOperationsDuty(integration operator and administrator roles). Without it D365 refuses the call with HTTP 403. - 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
FY2016toFY2031; use the model of the fiscal year your dates fall in (FY2026for 2026). - Budget codes:
postBudgetEntryneeds a code of type Original budget (USMFOriginal budget),postBudgetTransfera code of type Transfer (USMFExpTransferorRevTransfer). 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:
601500Travel Expense and601600Promotional Expense. - If you use budget control, the post runs it; if you use Budget transfer rules, a transfer must be allowed by them.
- A budget model for the period that is not stopped. USMF has fiscal-year models
- 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).
-
Post the original budget. Send
postBudgetEntrywith 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
MainAccountIdand a non-zeroAmount. 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.CurrencyCodedefaults to the accounting currency, a line'sTransDatetoPostingDate, andPostingDateto 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:
Successtrue,Status3,DocumentId=PostedDocumentId= the budget register entry number, and the message "Budget register entry ... was posted with 2 line(s)." -
Transfer budget between accounts. Send
postBudgetTransferwith 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.
-
Read the entry back (optional). Read the standard
BudgetRegisterEntryHeadersentity 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=trueStatusisCompleted.
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:
Successtrue,Status3,DocumentId=PostedDocumentId= the entry number, and a message that reports the number of lines you sent. BudgetRegisterEntryHeadersreturns the entry withStatusCompleted.- 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
LatticeServiceCallLogsfiltered on yourRequestId.
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.
| ErrorCode | Typical 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 preview | Send without PreviewMode. Nothing was run. |
TransientRuntime (6) | Infrastructure fault; IsRetryable true | Resend 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
- API reference: section 11 (Budget Register Service), 19 and 21 (bulk and async), 23 (reconciliation sweep)
- Endpoint payload specification:
MGCLatticeBudgetRegisterService - Security model
- Post a journal exactly-once
- Idempotency reconcile runbook
- Integration operations