Skip to main content
Before debugging, have this ready:
  • The Azure tenant ID, client ID, and environment URL you used to save the integration.
  • A specific (from Inbox) or a Dynamics incident/ticket number where the problem shows.
  • Admin access to both Azure AD and the Power Platform admin center.
Re-run Test & Save Configuration in Settings → Integrations → Dynamics 365 as your first step. It runs the token exchange plus a GET /incidents probe and tells you which half is failing.
Fastest path to root cause: open the failing OpenCX session and check meta for dynamics365_incident_id (or dynamics365_chat_id). If the field exists, the record was created in Dynamics — the problem is on the Dynamics side (assignment, routing). If not, the handoff itself failed — scope to credentials or the routing rule.

Common scenarios

Jump to the symptom that matches what you’re seeing.

”Failed to verify credentials” on save

: wrong tenant ID, expired secret, or missing admin consent. Fix: paste the Directory (tenant) ID from the Azure app’s Overview page (not the object ID, not the application ID). Regenerate the client secret in Certificates & secrets if yours is past its expiry. In API permissions, confirm Dynamics CRM → user_impersonation is added and shows Granted for <tenant>.

Test & Save fails even though the client secret is brand new

: environment URL typo or trailing slash. Fix: paste the URL exactly as shown in your browser up to .dynamics.com — no path, no trailing slash. Example: https://yourorg.crm.dynamics.com.

Token exchange succeeds, but the Dynamics probe returns 401

: not created, or not mapped to the Azure AD app. Fix: open the Power Platform admin center → your env → Settings → Users + permissions → Application users. Confirm there’s a row for your Azure AD app (search by client ID). If not, create one. See Azure & Dynamics Setup, step 4.

Token exchange succeeds, Dynamics probe returns 403

: Application User has no security role, or the role lacks Incident privileges. Fix: on the Application User row, click Manage roles and attach one. System Administrator is the simplest path; for a least-privilege setup give the role Read on Contact/Queue/SystemUser and Create + Write + Append To on Incident and Annotation. If you’re using chat channels, add Create on Omnichannel Live Work Item.

Incident created but unassigned

: default_owner_id and default_queue_id both blank, and no Dynamics routing rule catches the record. Fix: set default_queue_id in the OpenCX Dynamics settings to the queue your reps work from, and optionally default_owner_id for a specific systemuser. Either one (or a Dynamics routing rule) has to claim the incident for it to show up in a rep’s view.

Incident created but not in the expected queue

: security role missing Append To on the queue. Fix: in Dynamics, open the security role attached to the Application User and tick Append To on Queue at the required scope (usually Business Unit).

Contact not linked on the incident

: session has no email. Fix: gate handoff behind user-data collection so the AI captures an email before routing to Dynamics (in the widget, set collectUserData: true — see widget configuration). For SMS/phone, collect the email in-flow. Reps can also link a contact manually after the fact from the incident view.

Chat handoff from web/SMS/WhatsApp lands as an incident instead of Omnichannel

: the session’s channel value isn’t one of web, sms, or whatsapp. Fix: check the OpenCX session’s channel field. If you’re dispatching through a custom integration, confirm it sets one of the three supported strings; anything else routes to the incident path by design.

Omnichannel live work item never appears in the Agent app

: Omnichannel isn’t provisioned, or the workstream bound to default_queue_id has no routing rules. Fix: confirm Omnichannel for Customer Service is enabled on your environment from the Power Platform admin center. Open Omnichannel admin center → Workstreams, pick the workstream matching your default_queue_id, and verify it has Routing rules that route live work items to queues.

ticket_properties values rejected on incident create

: option-set labels used instead of codes, or lookup written as a raw GUID. Fix: use integer codes for option sets (e.g. "prioritycode": 1 for High, not "High"). For lookup fields, use the @odata.bind form, e.g. "[email protected]": "/accounts(<guid>)". Check the Dataverse schema for the entity if unsure.

POST /incidents returns 403 at handoff time

: role lacks Create privilege on Incident. Fix: in Dynamics, open the security role → Service tab → find Case (the Incident entity) → ensure Create is set at the right scope (usually Business Unit or Organization).

Rep replies in Dynamics don’t appear in the OpenCX session

: back-sync isn’t implemented. Fix: none — the current integration is one-way. Reps continue the conversation inside Dynamics; OpenCX marks the session as handed off once the record is created. If you need a two-way audit trail, link to the OpenCX session from the incident description.

Limits & timing

ItemLimitNotes
Access tokenFetched per requestNo token cache or refresh layer today — each handoff exchanges a fresh token.
OAuth scope{environment_url}/.defaultMust match the saved environment URL exactly, including protocol.
Dataverse API versionv9.2OpenCX pins this version for every call.
Incident body lengthDynamics-side limitVery long transcripts can hit the Dataverse string-field cap; older Dynamics versions limit descriptions around 100k characters.
Retry behaviorNoneA transient Azure AD or Dynamics 5xx is not retried; the handoff surfaces the error. Re-trigger the handoff to retry.

Azure & Dynamics Setup

Re-check app registration, Application User, and security role.

Connect to OpenCX

Re-run Test & Save after fixing credentials or roles.

Conversations in Dynamics 365

Routing rules, defaults, observability.

Human Handoff

When OpenCX decides to hand off in the first place.