- 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.
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, confirmDynamics 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 roleRead 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 missingAppend 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, setcollectUserData: 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’schannel 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 todefault_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
| Item | Limit | Notes |
|---|---|---|
| Access token | Fetched per request | No token cache or refresh layer today — each handoff exchanges a fresh token. |
| OAuth scope | {environment_url}/.default | Must match the saved environment URL exactly, including protocol. |
| Dataverse API version | v9.2 | OpenCX pins this version for every call. |
| Incident body length | Dynamics-side limit | Very long transcripts can hit the Dataverse string-field cap; older Dynamics versions limit descriptions around 100k characters. |
| Retry behavior | None | A transient Azure AD or Dynamics 5xx is not retried; the handoff surfaces the error. Re-trigger the handoff to retry. |
Related Documentation
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.