Scenario: Move, adjust, count and transfer stock with inventory journals
Goal. Record stock changes that happen outside D365 (a warehouse system, a shop floor terminal, a handheld count) as posted inventory journals in D365. One call creates one journal of the right type, posts it, and returns its journal number, so stock in D365 matches what your system reports.
When to use. Use a movement journal to receive or issue stock against a ledger account (samples, scrap, consumption that is not on an order), an adjustment journal to write stock up or down, a counting journal to record the result of a physical count, and a transfer journal to move stock between warehouses or locations at once. Use a BOM journal to report a kit or simple assembly as finished and consume its components without a production order, a tag counting journal when a count is recorded on numbered count tags, and an ownership change journal when consignment stock that a vendor owns becomes your own. If the move needs a shipment, an in-transit period and a receipt, use Transfer order ship and receive instead. To consume components on a production order, post its picking list (Production order lifecycle). To load thousands of lines at once (a store feed, a count upload) as ready-to-post journals, use Import journals in bulk instead.
Prerequisites
- Security. The caller needs the privilege
MGCLatticeInventoryServiceMaintain, which is in the dutyMGCLatticeIntegrationServiceOperationsDutyof the rolesMGCLatticeIntegrationAdministratorRoleandMGCLatticeIntegrationOperatorRole. Reading the posted journal back through the standard D365 entities needs the standard D365 read access to them. See Security model. - Journal names. One inventory journal name per journal type in the legal entity (Inventory
management > Setup > Journal names > Inventory). The service checks the name's Journal
type against the operation. USMF ships
IMov(Movement),IAdj(Inventory adjustment),ICnt(Counting),ITrf(Transfer),IBOM(BOM),ITag(Tag counting) andIOwner(Inventory ownership change). The journal number sequence and the journal name's voucher series must be set up. The BOM, tag counting and ownership change operations use the journal name of the inventory parameters (Inventory management > Setup > Inventory and warehouse management parameters, Journals tab) when the request names none: USMF setsIBOMandITagthere but no ownership change journal, so sendIOwner. Posting a tag counting journal also needs the parameters' counting journal name (USMFICnt). - Items and inventory dimensions. The lines name released products (USMF
D0001, Mid-range speaker) and an inventory dimension id (InventDimId) for the site and warehouse. In USMF000031is site 1, warehouse 11 and000072is site 1, warehouse 13, but the ids differ between environments: look them up once with the standardInventDimCDREntitiesentity, for exampleGET https://<your-environment>/data/InventDimCDREntities?cross-company=true&$filter=SysDataAreaId eq 'usmf' and InventSiteId eq '1' and InventLocationId eq '11' and wMSLocationId eq ''. - Offset account for movement journals. A movement line posts against a counter-account: send
OffsetMainAccountId(a main account that allows manual entry, for example USMF601500), unless the item's inventory posting profile supplies the "Inventory expenditure, profit/loss" accounts. - BOM journals. A BOM item with an active, approved BOM version at the site for the posting date (USMF
D0007, Speaker Pro Kit, at site1: componentsM0023andM0024, consumed from warehouse11), and the components on hand unless they allow negative physical inventory. - Ownership change journals. The consignment configuration key (
ConsignmentInbound) turned on, an item that tracks the owner inventory dimension, and stock a vendor owns (USMFM9211CI, owned by vendorUS-104at site5, warehouse51). The vendor needs an inventory owner (in USMF the owner id is the vendor account). - Lattice. A valid Lattice license. To send your own item keys instead of item numbers, register an external system and the keys (Use your own keys).
Steps
All seven operations belong to MGCLatticeInventoryJournalPostingService in the service group
MGCLatticeInventoryServiceGroup:
POST https://<your-environment>/api/services/MGCLatticeInventoryServiceGroup/MGCLatticeInventoryJournalPostingService/<operation>
with an OAuth 2.0 bearer token (Trial and day-one guide). The movement, adjustment, counting and
transfer journals take the same request; the BOM, tag counting and ownership change journals have their own
lines, which can name the site and warehouse (SiteId, WarehouseId) instead of, or on top of, an
InventDimId. Send a new RequestId for every new journal and the same RequestId when you resend the same
journal.
-
Post a movement journal (
postMovementJournal).Qtyis positive to receive, negative to issue.{"_request": {"RequestId": "6f1c2a4e-8b3d-4e5f-9a71-2c4d6e8f0a13","LegalEntityId": "USMF","UserContext": "Admin","JournalNameId": "IMov","Description": "Samples received from supplier","PostingDate": "2026-10-07","Lines": [{ "ItemId": "D0001", "InventDimId": "000031", "Qty": 10, "CostPrice": 25.00, "CostAmount": 250.00, "OffsetMainAccountId": "601500" }]}}The response carries the journal number in
DocumentIdand again inPostedDocumentId. -
Post an adjustment journal (
postAdjustmentJournal). Same request withJournalNameIdIAdj; a positiveQtywrites stock up, a negative one writes it down. The accounts come from the item's posting profile, so no offset account is needed.{"_request": {"RequestId": "0b9e7d2c-1f4a-4c6b-8e3d-5a7f9b1c3e25","LegalEntityId": "USMF","JournalNameId": "IAdj","Description": "Stock found during cycle check","PostingDate": "2026-10-07","Lines": [ { "ItemId": "D0001", "InventDimId": "000031", "Qty": 5, "CostPrice": 25.00, "CostAmount": 125.00 } ]}} -
Post a counting journal (
postCountingJournal).JournalNameIdICnt. HereQtyis the counted quantity, not a change: each line reads the on-hand quantity at the posting date and posts the difference, as the counting journal page does. Counting 100 where D365 shows 120 posts -20.{"_request": {"RequestId": "c3d5e7f9-2a4b-4c6d-8e0f-1a3b5c7d9e41","LegalEntityId": "USMF","JournalNameId": "ICnt","Description": "Cycle count aisle 11","PostingDate": "2026-10-07","Lines": [ { "ItemId": "D0001", "InventDimId": "000031", "Qty": 100, "CostPrice": 25.00 } ]}} -
Post a transfer journal (
postTransferJournal).JournalNameIdITrf. Each line movesQtyfromInventDimIdtoToInventDimId; send the destination on every line.{"_request": {"RequestId": "9a8b7c6d-5e4f-4a3b-9c2d-1e0f2a3b4c57","LegalEntityId": "USMF","JournalNameId": "ITrf","Description": "Move to finished goods","PostingDate": "2026-10-07","Lines": [ { "ItemId": "D0001", "InventDimId": "000031", "ToInventDimId": "000072", "Qty": 10, "CostPrice": 25.00, "CostAmount": 250.00 } ]}} -
Post a BOM journal (
postBOMJournal). Each line reports a BOM item as finished, as Report as finished on the BOM journal page does: D365 creates the receipt line and the component issue lines (phantom components exploded, cost taken from the components) from the item's BOM, then the journal is posted.Qtyis the finished quantity (greater than 0); the site and warehouse are where the item is received, and also select the BOM version.{"_request": {"RequestId": "1f3e5a7c-9b2d-4e6f-8a0c-2d4f6b8a0c19","LegalEntityId": "USMF","JournalNameId": "IBOM","Description": "Kits assembled at the packing bench","PostingDate": "2026-10-08","Lines": [ { "ItemId": "D0007", "Qty": 1, "SiteId": "1", "WarehouseId": "11" } ]}}BOMIdnames a BOM of the item to use; without it the item's active BOM version for the date, quantity and site is used.DocumentId=PostedDocumentId= the BOM journal, and the message counts the lines:BOM journal 000519 posted: 1 item line(s) reported as finished and 2 component line(s) consumed. -
Post a tag counting journal (
postTagCountingJournal). One line per count tag. A used tag names the item and the countedQty; a voided or missing tag names neither.TagStatusis an integer:1Used (also when left out),2Voided,3Missing.TagNumberis unique in the request and at most 10 characters.{"_request": {"RequestId": "5b7d9f1a-3c5e-4a7b-9d1f-3e5a7c9b1d28","LegalEntityId": "USMF","JournalNameId": "ITag","Description": "Wall-to-wall count warehouse 11","PostingDate": "2026-10-08","Lines": [{ "TagNumber": "T-0001", "ItemId": "D0001", "Qty": 600, "SiteId": "1", "WarehouseId": "11" },{ "TagNumber": "T-0002", "ItemId": "D0001", "Qty": 820, "SiteId": "1", "WarehouseId": "11" },{ "TagNumber": "T-0003", "TagStatus": 2 }]}}As in D365, posting the tags does not change on-hand by itself: it creates a counting journal with one line per item and counted dimensions (counted = the sum of the used tags) and leaves it open for review. Review and post that counting journal in D365, or send
"PostCountingJournal": trueto post it in the same call (both journals post in one transaction: both or neither).DocumentIdis the tag counting journal andPostedDocumentIdthe counting journal it created:Tag counting journal 000520 posted (3 tag(s)). Counting journal 000521 was created with the counted quantities and is open for review.When no tag counted an item, no counting journal is created andPostedDocumentIdis empty. -
Post an ownership change journal (
postOwnershipChangeJournal). Each line takes overQtyof the stock thatVendAccountowns (or the owner inInventDimId). The posting moves the quantity from the vendor's owner dimension to the legal entity's without a financial impact, and creates, confirms and receives a purchase order per vendor for it, which the vendor then invoices.{"_request": {"RequestId": "9d1f3b5c-7e9a-4c1d-8f3b-5a7c9e1d3f47","LegalEntityId": "USMF","JournalNameId": "IOwner","Description": "Consumed consignment stock, October","PostingDate": "2026-10-08","Lines": [ { "ItemId": "M9211CI", "Qty": 1, "VendAccount": "US-104", "SiteId": "5", "WarehouseId": "51" } ]}}DocumentIdis the ownership change journal andPostedDocumentIdthe purchase order the posting created (the first one when the lines name several vendors); the message lists every purchase order. -
Read the posted journal back with the journal number from
DocumentId(see below).
PostingDate defaults to today. To post many journals in one call, use the ...Batch operations
(postMovementJournalBatch, postBOMJournalBatch and so on) with { "_requests": [ ... ] }; see
Batch and async operations.
How to verify
- Response.
Successtrue,Status3,ErrorCode0,DocumentId= the journal number, andMessagesuch asMovement journal 000512 posted successfully.PostedDocumentIdis the journal number too, except for a tag counting journal (the counting journal it created) and an ownership change journal (the purchase order it created). Success is reported only when D365 marked the journal posted. - OData. Read the header from the standard entity of the journal type:
InventoryMovementJournalHeaders,InventoryAdjustmentJournalHeaders,InventoryCountingJournalHeadersorInventoryTransferJournalHeaders, for exampleGET https://<your-environment>/data/InventoryAdjustmentJournalHeaders?cross-company=true&$filter=dataAreaId eq 'usmf' and JournalNumber eq '000512'.IsPostedisYes. - Stock.
LatticeInventoryOnHands(filter onItemNumber,InventorySiteId,InventoryWarehouseId) shows the newPhysicalInventoryQuantity. - In D365. Inventory management > Journal entries > Items > Movement, Inventory
adjustment, Transfer, Bill of materials or Inventory ownership change, or Item counting >
Counting or Tag counting; set Show to Posted or All.
- A BOM journal has a receipt line for the BOM item and issue lines (BOM line = Yes) for its components; the item's on-hand rose and the components' fell.
- The counting journal a tag counting journal created shows Counted = the sum of the item's used tags,
open for review, or posted when you sent
PostCountingJournal. - The purchase order an ownership change journal created (vendor
US-104) is confirmed and received, and On-hand with the Owner dimension shows the quantity moved from the vendor to the legal entity.
- Call history. System administration > Inquiries > Lattice > Services > Service call log lists the call with its request id, status and posted document.
- Events. If your system subscribes to the Inventory on-hand changed business event, it receives one event per item, site and warehouse whose stock changed (React to business events).
When it goes wrong
A failed call answers Success false and Status 4. Message is a summary, for example
Movement journal posting failed due to validation or business rule constraints.; Errors carries the
specific reason, including D365's own messages.
| ErrorCode | Typical cause | What to do |
|---|---|---|
ValidationFailed | No JournalNameId (movement, adjustment, counting, transfer); Journal name X was not found.; Journal name X is not a Movement journal. (name of another type); no lines; an empty line; Item 'X' was not found.; an unknown inventory dimension, site or warehouse, or a warehouse of another site; an external key that does not resolve | Fix the request. Nothing was created. |
ValidationFailed (BOM) | Qty not greater than 0; "item X is not a stocked item that can be produced"; "BOM X is not a BOM of item Y."; "item X has no active BOM version on ... at site ... Send the BOMId to use." | Send a BOM item, a BOM of that item, or a site where it has an active BOM. |
ValidationFailed (tag counting) | "Request line N has no TagNumber."; "tag number X is longer than 10 characters." (it is never shortened); a tag number used twice; a used tag without an item; a voided or missing tag with an item or quantity; a negative Qty | Fix the tags and resend. |
ValidationFailed (ownership change) | Qty not greater than 0; "item X does not track the owner inventory dimension"; no owner ("Send the VendAccount of the vendor that owns the stock, or an InventDimId whose owner is that vendor."); a vendor without an inventory owner; VendAccount and InventDimId naming different owners; the legal entity itself as owner | Name the vendor that owns the stock. |
ConfigurationMissing | No JournalNameId and no default journal name of that type in the inventory parameters; posting tags without a default counting journal name; an ownership change with the consignment configuration key turned off | Send JournalNameId, or complete the inventory parameters or configuration. |
DimensionValueNotFound | Offset main account 'X' could not be resolved in the chart of accounts. | Send a main account of the legal entity's chart of accounts. |
BusinessRule | D365 refused to post: "Offset account is not specified" on a movement line, not enough on-hand for an issue, a transfer line without a valid destination, or Journal X was not posted.; a BOM journal whose Report as finished raised warnings ("BOM journal X was not posted: ...") or found a circular BOM; a tag the standard refuses (a stopped item); not enough vendor-owned stock, or a purchase order the ownership change could not create | Fix the data or setup named in Errors, then resend. |
PreviewUnsupported | PreviewMode true | These operations have no preview; send without it. |
TransientRuntime | A runtime fault (IsRetryable true) | Resend the same request with the same RequestId after a short wait. |
DuplicateRequest / RequestInProgress | The RequestId belongs to another operation / is still running | Use a new RequestId / retry once the first call finishes. |
LicenseNotValid | The Lattice license is not valid (IsRetryable true) | Renew the license; the request runs once it is valid. |
Retries never post twice. A request that succeeded returns its recorded result, with the same journal
number, when you resend it with the same RequestId. A request that failed after the journal was
created deletes the unposted journal again, so it leaves nothing behind and a resend with the same
RequestId creates it anew. With PostCountingJournal, a tag counting journal and the counting journal it
creates are posted together or not at all. A call cut off between creating and posting the journal is resolved by the
idempotency reconciliation sweep (Post a journal exactly once). These
operations are synchronous only: they are not available through
submitAsyncOperation, so a failure is answered to the caller and does not go to the dead-letter queue.
Failed calls show on the Failed service calls (24 h) tile of the Integration operations workspace
(Integration operations).
Reference
- API reference: section 4, Inventory Journal Posting Service; section 19, Bulk Operations
- Endpoint payload specification:
MGCLatticeInventoryJournalPostingService - Security model: Inventory Management privileges
- Data entity reference:
LatticeInventoryOnHands,LatticeInventoryTransactions - Cross-reference keys:
ItemIdExternalKey,BOMIdExternalKey,VendAccountExternalKey - Post a journal exactly once, Transfer order ship and receive, Production order lifecycle