Resources/The Agent API Atlas/CRM/Microsoft Dynamics 365

Everything an AI agent can do with the Microsoft Dynamics 365 API.

A reference guide for building AI agents: every method, how to authenticate, and the permissions each one needs.

Endpoints20
API versionv9.2
Last updated23 June 2026
Orientation

How the Microsoft Dynamics 365 API works.

The Microsoft Dynamics 365 API is how an app or AI agent works with the Dataverse data behind the business apps: reading and updating accounts and contacts, qualifying a lead, creating an opportunity, or resolving a customer case. Access is granted through a Microsoft Entra ID sign-in, and what the agent can reach is governed by Dataverse security roles and table and column privileges rather than per-endpoint scopes. A record change can be detected through change tracking or pushed to an external system by a registered plug-in or flow.

20Endpoints
5Capability groups
8Read
12Write
5Permissions
Authentication
Dynamics 365 authenticates every call with OAuth 2.0 through Microsoft Entra ID, the same identity that signs people into Microsoft 365. An interactive app requests the org-URL plus /user_impersonation scope, while a background app uses the org-URL plus /.default scope and signs in as an application user with a client secret or certificate. There is no separate API key.
Permissions
Dynamics 365 has no granular per-endpoint OAuth scopes. The real boundary is the Dataverse security roles assigned to the user or application user the agent runs as, which grant create, read, write, delete, append, assign, and share privileges per table, plus column-level security on sensitive fields. A request the role doesn't allow returns 403 Forbidden.
Versioning
The Dataverse Web API carries its version in the path, currently v9.2, and a version's behavior stays stable so code keeps working. The v9.0, v9.1, and v9.2 releases share identical behavior with no breaking changes; Microsoft mints a new version number only for a change that isn't backward compatible.
Data model
Dynamics 365 is an OData version 4.0 service over Dataverse tables. Each table is an entity set addressed by its EntitySetName (accounts, contacts, leads, opportunities, incidents), records are created with POST, updated with PATCH, and removed with DELETE, and queries use OData options like $select and $filter. Named functions (called with GET) and actions (called with POST) expose built-in business messages, and FetchXML offers an XML query alternative.
Connect & authenticate

Connection & authentication methods.

How an app or AI agent connects to Dynamics 365 determines what it can reach. There is the Dataverse Web API for making calls, a hosted server that exposes Dynamics 365 tools to agents, and change notifications for learning about activity, and each route runs as a Dataverse user whose security roles set what data the agent can touch.

Ways to connect

Dataverse Web API

The Dataverse Web API is an OData version 4.0 REST service at [Organization URI]/api/data/v9.2/. It accepts JSON, returns JSON, and uses GET to retrieve and call functions, POST to create and call actions, PATCH to update and upsert, and DELETE to remove. Records live in entity sets named by EntitySetName (accounts, contacts, leads, opportunities, incidents), and queries use OData options such as $select, $filter, $orderby, $top, and $expand.

Best forConnecting an app or AI agent to Dynamics 365 data.
Governed byThe Dataverse security roles of the user the call runs as.
Docs ↗

MCP server

Dataverse provides a first-party Model Context Protocol server that exposes its tables and records to AI agents, generally available at [Organization URI]/api/mcp (preview at /api/mcp_preview). A non-Microsoft client such as Claude connects through the @microsoft/dataverse npm local proxy or directly to the remote endpoint with a registered Microsoft Entra app granted the Dynamics CRM mcp.tools permission. It exposes tools for listing tables, describing a table's schema, querying records, and creating or updating rows, and must be enabled per environment with its allowed clients.

Best forConnecting an AI agent to Dynamics 365 through MCP.
Governed byDataverse security roles and row-level security of the signed-in user.
Docs ↗

Change tracking & plug-ins

Dataverse does not offer a generic outbound webhook on every table. To learn about changes, an agent adds the Prefer: odata.track-changes header to a query and receives a delta token to fetch only what changed since last time. To push a change to an external endpoint as it happens, register a plug-in or webhook step on a table's create, update, or delete message, or build a Power Automate flow on that event.

Best forKeeping an app or AI agent in sync with Dynamics 365 changes.
Governed byThe security role on the query, and the plug-in or flow registration.
Docs ↗
Authentication

Microsoft Entra ID (delegated)

An interactive app signs a person in with OAuth 2.0 through Microsoft Entra ID and acts on their behalf, requesting the environment-URL plus /user_impersonation scope. The app needs the Access Dynamics 365 as organization users delegated permission. Every call runs with that person's Dataverse security roles, so the agent can reach only what they can.

TokenOAuth 2.0 Bearer access token (Microsoft Entra ID)
Best forApps that act as the signed-in user.
Docs ↗

Application user (server-to-server)

A background or scheduled app authenticates with no interactive user, requesting the environment-URL plus /.default scope and proving itself with a client secret or X.509 certificate. It is bound to a dedicated Dataverse application user that is assigned a custom security role, which defines exactly what the app can do. This route does not consume a paid user license.

TokenOAuth 2.0 Bearer access token (client secret or certificate)
Best forUnattended integrations and scheduled jobs.
Docs ↗
Endpoint reference

Every Microsoft Dynamics 365 API method.

Filter by method, access, or permission, or search any path. Select a row for version detail, rate limits, the related webhook event, and the source.

MethodEndpointWhat it doesAccessPermissionVersion

Records (CRUD)

Create, retrieve, update, upsert, and delete records in any Dataverse table.7

Needs the Create privilege on the target table. Add Prefer: return=representation to get the created record back as 201.

Acts ontable record
Permission (capability)Create privilege
VersionAvailable since the API’s base version
Webhook eventNone
Rate limitStandard limits apply

Needs the Create privilege on each table involved. The whole insert succeeds or fails together.

Acts ontable record
Permission (capability)Create privilege
VersionAvailable since the API’s base version
Webhook eventNone
Rate limitStandard limits apply

Read-only. Can also be retrieved by an alternate key. Always use $select for performance.

Acts ontable record
Permission (capability)Read privilege
VersionAvailable since the API’s base version
Webhook eventNone
Rate limitStandard limits apply

Needs the Write privilege on the table. Include only changed columns to avoid triggering unintended business logic.

Acts ontable record
Permission (capability)Write privilege
VersionAvailable since the API’s base version
Webhook eventNone
Rate limitStandard limits apply

Needs Create and Write privileges, since either may occur. Use If-Match or If-None-Match to prevent one of the two outcomes.

Acts ontable record
Permission (capability)Write privilege
VersionAvailable since the API’s base version
Webhook eventNone
Rate limitStandard limits apply

Needs the Delete privilege. Returns 204 if deleted, 404 if the record was not found.

Acts ontable record
Permission (capability)Delete privilege
VersionAvailable since the API’s base version
Webhook eventNone
Rate limitStandard limits apply

Needs the Write privilege. PUT is for individual properties, not whole records.

Acts ontable column value
Permission (capability)Write privilege
VersionAvailable since the API’s base version
Webhook eventNone
Rate limitStandard limits apply

Query

Read collections with OData query options or with FetchXML.3

Read-only. Returns up to 5,000 rows per page; the $skip and $search options are not supported.

Acts ontable collection
Permission (capability)Read privilege
VersionAvailable since the API’s base version
Webhook eventNone
Rate limitStandard limits apply

Read-only. FetchXML supports joins and aggregations OData cannot; send long queries inside a $batch to beat the URL length limit.

Acts ontable collection
Permission (capability)Read privilege
VersionAvailable since the API’s base version
Webhook eventNone
Rate limitStandard limits apply

Read-only. The standard count is capped at 5,000; request the totalrecordcountlimitexceeded annotation to know if more match.

Acts ontable collection
Permission (capability)Read privilege
VersionAvailable since the API’s base version
Webhook eventNone
Rate limitStandard limits apply

Associate & disassociate

Relate and unrelate records across table relationships.3

Needs Append on the referencing table and AppendTo on the referenced table. Set the property to null to disassociate.

Acts onrelationship
Permission (capability)Append privilege
VersionAvailable since the API’s base version
Webhook eventNone
Rate limitStandard limits apply

Needs Append and AppendTo privileges. The related record is referenced by an absolute @odata.id URI.

Acts onrelationship
Permission (capability)Append privilege
VersionAvailable since the API’s base version
Webhook eventNone
Rate limitStandard limits apply

Needs Append and AppendTo privileges. Removes the link only, not the records themselves.

Acts onrelationship
Permission (capability)Append privilege
VersionAvailable since the API’s base version
Webhook eventNone
Rate limitStandard limits apply

Functions, actions & batch

Call built-in messages and group requests into a single batch.5

Functions are called with GET and have no side effects. WhoAmI needs no special privilege; other functions read data subject to the user's role.

Acts onfunction
Permission (capability)None required
VersionAvailable since the API’s base version
Webhook eventNone
Rate limitStandard limits apply

Bound functions require the full Microsoft.Dynamics.CRM namespace prefix and a record URI for the first parameter.

Acts onfunction
Permission (capability)Read privilege
VersionAvailable since the API’s base version
Webhook eventNone
Rate limitStandard limits apply

Actions are called with POST and can have side effects. Merge needs the privileges its underlying operation requires on the records involved.

Acts onaction
Permission (capability)Write privilege
VersionAvailable since the API’s base version
Webhook eventNone
Rate limitStandard limits apply

Bound actions require the full Microsoft.Dynamics.CRM namespace prefix and a record URI for the first parameter. Custom APIs are called the same way.

Acts onaction
Permission (capability)Write privilege
VersionAvailable since the API’s base version
Webhook eventNone
Rate limitStandard limits apply

Sent as multipart/mixed. Each request inside the batch is checked against the user's privileges individually; it does not bypass service protection limits.

Acts onbatch
Permission (capability)None required
VersionAvailable since the API’s base version
Webhook eventNone
Rate limitStandard limits apply

Metadata

Read the definitions of tables, columns, and relationships at run time.2

Read-only. Returns all matches with no paging; use $select because metadata records are large. Expand Attributes for column definitions.

Acts ontable definition
Permission (capability)Read privilege
VersionAvailable since the API’s base version
Webhook eventNone
Rate limitStandard limits apply

Read-only. Cast to OneToManyRelationshipMetadata or ManyToManyRelationshipMetadata to read type-specific properties.

Acts onrelationship definition
Permission (capability)Read privilege
VersionAvailable since the API’s base version
Webhook eventNone
Rate limitStandard limits apply
No endpoints match those filters.
Webhooks

Webhook events.

Dynamics 365 can keep an app informed of data that changed without re-reading everything. Change tracking lets an agent ask for only the records added, updated, or deleted since its last sync, and registered plug-ins or Power Automate flows can push a record change out to an external endpoint.

EventWhat it signalsTriggered by
No events match that search.
Rate limits & pagination

Rate limits, pagination & request size.

Dynamics 365 limits how fast each user can call the API through service protection limits measured over a rolling five-minute window, counting the number of requests, their combined run time, and how many run at once.

Request rate

Dynamics 365 applies service protection limits per user over a rolling five-minute (300-second) window, evaluated on three facets: the number of requests, their combined execution time, and how many run at once. The defaults per web server are 6,000 requests, 20 minutes (1,200 seconds) of combined execution time, and 52 concurrent requests; most environments have several web servers. Going over returns HTTP 429 Too Many Requests with a Retry-After header giving the seconds to wait. These limits are separate from licensing entitlement limits, and Dataverse search uses a different API with its own one-request-per-second-per-user limit.

Pagination

An OData query returns up to 5,000 standard-table rows (500 for elastic tables) per page. Set a smaller page size with the Prefer: odata.maxpagesize header, then follow the @odata.nextLink in the response to fetch the next page. Use $top to cap the total rows returned instead of paging. The $skip and $search query options are not supported.

Request size

A single request URL is capped at 32 KB (32,768 characters), which a long FetchXML or OData query can hit; sending the request inside a $batch raises the limit to 64 KB. Any individual OData segment is capped at 260 characters. A single $batch request can contain up to 1,000 operations.

Errors

Status codes & error handling.

The status codes an agent should handle, and what to do about each.

StatusCodeMeaningWhat to do
400BadRequestThe request is invalid, for example a malformed query, a bad argument, or a URL or OData segment that exceeds the length limit.Read the error message, fix the request, and resend. Use parameter aliases for long or special-character function parameters.
401UnauthorizedNo valid authentication was provided, or the token is expired or tampered with (for example ExpiredAuthTicket or RequestIsNotAuthenticated).Acquire a fresh Microsoft Entra ID access token and send it as a Bearer token. Tokens expire after about 60 minutes.
403Forbidden (PrivilegeDenied)The user is authenticated but their security role lacks the privilege the request needs, for example a missing Create, Read, or column-level permission.Grant the required privilege on the user's or application user's security role, then retry.
404Not FoundThe requested record or resource does not exist, or is not visible to this user.Verify the record ID and entity set name, and confirm the record exists in this environment.
412Precondition FailedA concurrency or duplicate conflict, such as ConcurrencyVersionMismatch when an ETag no longer matches, or DuplicateRecord.Re-retrieve the record to get the current ETag, reconcile, and retry. Resolve duplicates as appropriate.
429Too Many RequestsA service protection limit was hit: too many requests, too much combined execution time, or too many concurrent requests in the five-minute window.Wait the number of seconds in the Retry-After header, then retry. Smooth the request rate and reduce concurrency.
500Server ErrorAn error occurred on the Dataverse side. It can also appear as 501 Not Implemented or 503 Service Unavailable.Retry with backoff. If it persists, contact Microsoft support.
Versioning & freshness

Version history.

Dynamics 365 pins a version number in the path of every Dataverse Web API call, and a version stays stable so code written against it keeps working as new capabilities arrive under newer versions.

Version history

What changed, and when

Latest versionv9.2
v9.2Current version
Current Dataverse Web API version

v9.2 is the current Web API version, pinned in the path of every call (for example /api/data/v9.2/). The v9.0, v9.1, and v9.2 releases share identical behavior with no breaking changes between them; new capabilities are added without removing existing ones. Microsoft mints a new version number only when it must make a change that is not backward compatible.

What changed
  • OData version 4.0 service over Dataverse tables, with create, retrieve, update, delete, upsert, associate, functions, actions, batch, metadata, and FetchXML support.
  • v9.0, v9.1, and v9.2 are behaviorally identical with no breaking changes.
  • Guidance: pin the version that was current when the code was written and move up deliberately.
v9.0
Version-specific behavior introduced

Starting with the v9.0 release, the Web API supports version-specific differences in the same environment, so the version in the service URL became meaningful. New access-sharing operations such as GrantAccess, ModifyAccess, and RetrieveSharedPrincipalsAndAccess were added. FetchXML query responses stopped encoding special characters (for example '.' no longer becomes 'x002e'), and a table and a same-named column are no longer disambiguated by appending '1'.

What changed
  • Version-specific differences supported within one environment (a change from the v8.x model).
  • GrantAccess, ModifyAccess, and RetrieveSharedPrincipalsAndAccess operations added.
  • FetchXML response special-character encoding and same-name table/column suffixing removed.
v8.2
Earlier additive v8.x line

In the v8.x line, every minor version behaved identically because all changes were additive, so the version referenced in the URL did not matter. After an upgrade to v8.2, the v8.0 and v8.1 services were all the same. This line predates the version-specific behavior introduced at v9.0.

What changed
  • v8.0, v8.1, and v8.2 services were identical, with only additive changes.
  • Carried-forward limitations from v8.x are noted in the current versions documentation.

Pin v9.2 in the path and move up deliberately after checking documented differences.

Dataverse Web API versions ↗
Questions

Microsoft Dynamics 365 API, answered.

How does an AI agent authenticate to Dynamics 365?+
Every Dataverse Web API call uses OAuth 2.0 with Microsoft Entra ID as the identity provider, acquired through the Microsoft Authentication Library (MSAL). An app that acts as a signed-in person requests the environment-URL plus /user_impersonation scope; a background or server-to-server app requests the environment-URL plus /.default scope and runs as an application user backed by a client secret or X.509 certificate. The access token is sent as a Bearer token and is short-lived, expiring by default after about 60 minutes.
What permissions does a call need?+
Dynamics 365 does not expose granular per-endpoint OAuth scopes. Access is governed by Dataverse security roles assigned to the user or application user the call runs as. A role grants privileges such as Create, Read, Write, Delete, Append, Assign, and Share for each table, at a depth (own, business unit, or organization), and column-level security can further restrict individual fields. If the role lacks the privilege a request needs, the call returns 403 Forbidden with an error like PrivilegeDenied.
What is Dataverse and how does it relate to Dynamics 365?+
Dataverse is the data platform underneath Dynamics 365 and the wider Power Platform. The Dynamics 365 apps for Sales, Customer Service, and Field Service store their records, like accounts, contacts, leads, opportunities, and cases (the incident table), as Dataverse tables. The Dataverse Web API is the single REST interface an app or agent uses to work with all of that data.
How does an agent learn about data that changed?+
The Dataverse Web API supports change tracking. By including the Prefer: odata.track-changes header on a query, an agent receives a delta token it can present on its next call to get only the records created, updated, or deleted since last time. Dataverse does not offer a generic outbound webhook on every table, so pushing a change to an external endpoint means registering a plug-in or webhook step, or building a Power Automate flow on the table's create, update, or delete event.
What is the difference between a function and an action?+
Functions and actions are named operations beyond simple table reads and writes. A function has no side effects and is called with a GET request, like the unbound WhoAmI function that returns the calling user's IDs. An action can change data and is called with a POST request, like Merge to combine duplicate records or WinOpportunity to close a deal as won. Both can be unbound (called on their own) or bound to a specific table or table collection, in which case the full Microsoft.Dynamics.CRM namespace prefix is required.
How are errors returned?+
Errors come back as JSON in the shape { "error": { "code": "...", "message": "..." } }, where code is a Dataverse hex error code (for example 0x80040265) that is unrelated to the HTTP status. Common statuses are 400 for an invalid request, 401 for a bad or expired token, 403 when the security role lacks the needed privilege, 404 when a record is not found, 412 for a concurrency or duplicate conflict, and 429 when service protection limits are hit. Adding Prefer: odata.include-annotations="*" returns extra detail and a help link with the error.
Does Dynamics 365 have an official MCP server?+
Yes. Dataverse provides a first-party Model Context Protocol server that exposes its data to AI agents, generally available at the /api/mcp path on the environment URL (with a preview endpoint at /api/mcp_preview). A non-Microsoft client such as Claude can connect through the @microsoft/dataverse npm local proxy or directly to the remote endpoint using a registered Microsoft Entra app. The server respects Dataverse security roles and row-level security, so an agent reaches only the tables and records its role permits.
Related

More crm API guides for agents

What is Bollard AI?

Control what every AI agent can do in Dynamics 365.

Bollard AI sits between a team's AI agents and Dynamics 365. Grant each agent exactly the access it needs, read or write, table by table, and every call is checked and logged.

  • Set read, write, or full access per agent, never a shared Dynamics 365 application user.
  • Denied by default, so an agent reaches only the tables and records that have been explicitly allowed.
  • Every call recorded in plain English: who, what, where, and the decision.
Microsoft Dynamics 365
Sales Ops Agent
View accounts and contacts ResourceOffReadFull use
Create and update leads ResourceOffReadFull use
Opportunities ResourceOffReadFull use
Delete records ActionOffReadFull use
Per-agent access, set in Bollard AI, not in Dynamics 365