Skip to main content

Scenario: Connect an integration client

Goal. Give an external system (middleware, an e-commerce platform, a CRM, a script) its own identity in Dynamics 365 Finance and Operations, with exactly the Lattice rights it needs, and prove that the identity can authenticate, reach the Lattice services and read back what it did. When you finish, the client has made a read-only status query and a non-committing preview, and you can see the preview in the service call log (status queries are not written to it).

When to use. Once per integration client and per environment, before the first business call. Every other scenario assumes it is done: Post a journal exactly-once, Workflow approvals, Use external keys, Batch and async operations, Track documents and read data and React to business events.

Prerequisites​

  1. Lattice is installed in the environment and the database is synchronized. The Lattice package carries the Siren licensing module, so there is nothing separate to install for it.
  2. License. There is no license key to enter and no activation page. A newly installed environment reports Unlicensed, and that status is allowed through, so every operation works. Lattice refuses a call with ErrorCode LicenseNotValid only after the licensing service has returned an adverse verdict (Expired, Invalid or OfflineExceeded). Previews, status queries, data entities and business events are never refused for licensing. See Deployment.
  3. An administrator who can create an app registration in Microsoft Entra ID, register it in Finance and Operations and assign security roles.
  4. A dedicated, non-interactive Finance and Operations user for the client, one per integration. All role, duty and privilege checks run against this user.
  5. Know which calls the client makes, so you can pick the role (step 3 below). The roles and duties are in Security model.

Steps​

  1. Create the app registration in Microsoft Entra ID. Register a single-tenant application, create a client secret (or upload a certificate), and record the Application (client) ID, the Directory (tenant) ID and the secret. Store the secret in a vault.

  2. Register the app in Finance and Operations. Go to System administration > Setup > Microsoft Entra ID applications, click New, enter the application (client) ID in Client Id, a Name (for example Webshop integration - UAT) and, in User ID, the dedicated user from the prerequisites. Save. Every call the client makes now runs as that user.

  3. Assign the Lattice role the calls need. Go to System administration > Security > Assign users to roles, select the role, click Manually assign / exclude users, select the user and click Assign to role. Choose the narrowest role that covers the client:

    The client will...RoleMain duties
    Post and act on documents, query status, read monitoring and Lattice entitiesMGCLatticeIntegrationOperatorRoleMGCLatticeIntegrationServiceOperationsDuty, MGCLatticeIntegrationServiceQueryDuty, MGCLatticeIntegrationServiceMonitoringDuty, MGCLatticeDataEntitiesView, MGCLatticeDataEntitiesMaintain, MGCLatticeCrossReferenceViewDuty
    Only read: status queries, price inquiry, monitoring, Lattice entitiesMGCLatticeIntegrationReadOnlyRoleMGCLatticeIntegrationServiceQueryDuty, MGCLatticeIntegrationServiceMonitoringDuty, MGCLatticeDataEntitiesView, MGCLatticeCrossReferenceViewDuty
    Also cancel free text invoices, post intercompany journals or fixed asset disposals, activate new standard costs, or write external systems and cross-reference keysMGCLatticeIntegrationAdministratorRoleadds MGCLatticeIntegrationServiceHighRiskDuty and MGCLatticeCrossReferenceMaintainDuty

    For least privilege, build a custom role from the module privileges instead (for example MGCLatticeAccountsReceivableServiceMaintain plus MGCLatticeIntegrationServiceMonitoringDuty). Never assign MGCLatticeDatabaseAccessAdministrator to an integration identity.

  4. Set the user's default company to the legal entity the client works in (System administration > Users > Users, the Company field of the user). Service calls name their legal entity in LegalEntityId, but an OData read without cross-company=true runs in the default company.

  5. Confirm the service groups are deployed. The service groups deploy with the package and need no setup. To check them, open https://<your-environment>/api/services in a browser signed in to the environment and confirm that the 13 Lattice groups are listed: MGCLatticeAccountsPayableServiceGroup, MGCLatticeAccountsReceivableServiceGroup, MGCLatticeAsyncOperationServiceGroup, MGCLatticeDataManagementServiceGroup, MGCLatticeDbAccessServiceGroup, MGCLatticeExpenseServiceGroup, MGCLatticeFixedAssetsServiceGroup, MGCLatticeGeneralLedgerServiceGroup, MGCLatticeInventoryServiceGroup, MGCLatticeJournalPostingServiceGroup, MGCLatticeProductionServiceGroup, MGCLatticeProjectServiceGroup and MGCLatticeWorkflowServiceGroup. If one is missing, the package did not finish deploying (Deployment, section 16).

  6. Get a token with the OAuth 2.0 client-credentials flow. The scope is your environment URL followed by /.default:

    POST https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token
    Content-Type: application/x-www-form-urlencoded

    grant_type=client_credentials&client_id=<client-id>&client_secret=<client-secret>&scope=https://<your-environment>/.default

    The response carries access_token. Send it as Authorization: Bearer <access_token> on every call and request a new one shortly before it expires.

  7. Make a first, read-only call: a status query. getSalesOrderStatus on MGCLatticeSalesOrderPostingService (service group MGCLatticeAccountsReceivableServiceGroup) reads a sales order and changes nothing. Every Lattice call is POST https://<your-environment>/api/services/<ServiceGroup>/<ServiceClass>/<operation> with the request in _request:

    POST /api/services/MGCLatticeAccountsReceivableServiceGroup/MGCLatticeSalesOrderPostingService/getSalesOrderStatus

    {
    "_request": {
    "RequestId": "3b7c0d52-8e41-4f0a-9c55-1d2e6f7a8b90",
    "LegalEntityId": "USMF",
    "UserContext": "webshop",
    "DocumentId": "000698"
    }
    }

    RequestId is required on every call, queries included. It returns Success true, DocumentType SalesOrder, the order's status in DocumentStatus (for example Backorder or Invoiced) and named StatusDetails such as CustAccount.

  8. Make a first write-path call: a preview. A preview validates a posting and commits nothing. Send postGeneralJournal (MGCLatticeGeneralLedgerServiceGroup / MGCLatticeGeneralLedgerJournalService) with PreviewMode true, today's date and an open period:

    {
    "_request": {
    "RequestId": "9b2d5f60-1c34-4e8a-b7d2-0a4c6e8f1b23",
    "LegalEntityId": "USMF",
    "UserContext": "webshop",
    "JournalNameId": "GenJrn",
    "Description": "Connection check",
    "PostingDate": "2026-10-07",
    "PreviewMode": true,
    "Lines": [
    { "MainAccountId": "110110", "OffsetMainAccountId": "110180", "AmountCurDebit": 10.00,
    "AmountCurCredit": 0, "CurrencyCode": "USD", "TransDate": "2026-10-07" }
    ]
    }
    }

    It returns Success true, PreviewApplied true and PreviewFidelity 1 (validation only). No journal is created, no number is used and the RequestId stays free for a real post. Only five operations have a real preview (sales confirm, packing slip and invoice, general journal, intercompany journal); see Posting preview.

  9. If the client will send its own keys, register its external system once. Either go to System administration > Setup > Lattice > External systems and add a row, or let the client create it through the shared LatticeExternalSystems entity (this needs the administrator role or MGCLatticeExternalSystemMaintain):

    { "ExternalSystemId": "SHOP", "Description": "Webshop", "Active": "Yes" }

    Send it as POST https://<your-environment>/data/LatticeExternalSystems. The rest of the setup is in Use external keys.

How to verify​

  • The token request returns access_token, and both calls return HTTP 200 with Success true.
  • The preview is in the service call log: open System administration > Inquiries > Lattice > Services > Service call log, or read the monitoring entity GET https://<your-environment>/data/LatticeServiceCallLogs?$filter=RequestId eq 9b2d5f60-1c34-4e8a-b7d2-0a4c6e8f1b23. CalledBy is the dedicated user from step 2, UserContext is what the client sent, and IsPreview is Yes. Use a posting operation with PreviewMode true for this check: status queries are never written to the service call log, so the step 7 query is not there.
  • GET https://<your-environment>/data/LatticeExternalSystems lists the external system with Active Yes, when you created one.

When it goes wrong​

What you seeCause and fix
The token request fails, or HTTP 401 on the callNo valid token: wrong scope (it must be https://<your-environment>/.default), an expired secret, or an expired token. Request a new token.
HTTP 403The token is valid, but the app is not registered on Microsoft Entra ID applications or its user is disabled (step 2), or the user has no Lattice role that grants the operation (step 3).
HTTP 404 on a service URLThe service group is not deployed, or the group, service or operation name in the URL is misspelled.
ErrorCode ValidationFailed (1) with "RequestId is required." or "LegalEntityId is required."Send a GUID RequestId and a LegalEntityId on every call.
ValidationFailed with "Legal entity ... was not found or is not accessible."The LegalEntityId does not exist.
DocumentNotFound (2) on the status queryNo sales order with that number in that legal entity.
PreviewUnsupported (10)PreviewMode true on an operation without a preview. Nothing ran; send it without the flag when you mean to post.
LicenseNotValid (12), IsRetryable trueThe license was found expired, invalid or offline too long. Reads, previews and entities keep working; renew or reconnect and resend the same request unchanged (Deployment, section 16).
An OData read returns no rows for a record that existsThe read ran in the user's default company. Add cross-company=true and filter on dataAreaId; see Track documents and read data.

Error codes travel as integers in service responses; the full table is in API reference.

Reference​