Dynamics API
Dynamics API
NOTE
Unsure about entity vs. table? See Developers: Understand terminology in Microsoft Dataverse.
Dataverse has tables that are used to model and manage business data. You can use the stock provided tables or
define your own custom tables to store data.
Web API
The Web API is an OData v4 RESTful endpoint. Use this for any programming language that supports HTTP
requests and authentication using OAuth 2.0.
More information: Use the Dataverse Web API
Organization service
Use the .NET Framework SDK assemblies for projects that involve writing plug-ins or workflow extensions.
More information: Use the Dataverse Organization service
NOTE
Use the Xrm.Tooling assemblies if you are creating a Windows client application. More information: Build Windows client
applications using the XRM tools
Use the Microsoft Dataverse Web API
5/21/2021 • 2 minutes to read • Edit Online
NOTE
Unsure about entity vs. table? See Developers: Understand terminology in Microsoft Dataverse.
The Web API is one of two web services you can use to work with data, and table and column definitions in
Dataverse. The other is the Organization Service.
The Dataverse Web API provides a development experience that can be used across a wide variety of
programming languages, platforms, and devices. The Web API implements the OData (Open Data Protocol),
version 4.0, an OASIS standard for building and consuming RESTful APIs over rich data sources. You can learn
more about this protocol at https://www.odata.org/. Details about this standard are available at
https://www.oasis-open.org/standards#odatav4.0.
Because the Web API is built on open standards, we don't provide assemblies for a specific developer
experience. You can compose HTTP requests for specific operations or use third-party libraries to generate
classes for whatever language or platform you want. You can find a list of libraries that support OData version
4.0 at https://www.odata.org/libraries/.
Getting started
Now that you have read an overview of the Web API, proceed to the Get started with Dataverse Web API topic
to learn how to write your first C# program in Visual Studio that uses the Web API.
If you are a JavaScript developer and want to use the Web API in model-driven apps, navigate to Client-side
JavaScript using Web API in model-driven apps.
Related Sections
Work with data using code
OData - the best way to REST
OData Version 4.0 Part 1: Protocol Plus Errata 02
OData Version 4.0 Part 2: URL Conventions Plus Errata 02
OData Version 4.0 Part 3: Common Schema Definition Language (CSDL) Plus Errata 02
Get started with Microsoft Dataverse Web API (C#)
5/21/2021 • 2 minutes to read • Edit Online
This section shows you how to access the Dataverse Web API using the C# programming language. The first
topic, Quick Start: Web API sample (C#), provides the quickest example of how to accomplish this, as all source
code is simplified and provided in one file. The rest of this section shows you how to create and configure Visual
Studio projects that use the Dataverse Web API to perform common business data operations.
After completing this guide, you will have learned enough to explore the many other business operations that
are supported by the Dataverse Web API. You will also have a better understanding about how the Web API
Samples (C#) are structured.
In this section
Quick Start: Web API sample (Do this first)
Enhanced quick start (Do this next)
Start a Dataverse Web API project in Visual Studio
See also
Perform operations using the Web API
Web API Samples (C#)
Quick Start: Web API sample (C#)
10/1/2021 • 5 minutes to read • Edit Online
In this quick start you will create a simple console application to connect to your target Microsoft Dataverse
environment and invoke the Web API WhoAmI function. This function retrieves information about the logged on
Dataverse user. Once you understand the basic functionality described here, you can move onto other Web API
operations such as create, retrieve, update, and deletion of Dataverse table rows.
This program will authenticate and use an HttpClient to send a GET request to the WhoAmI Function the
response will be a WhoAmIResponse ComplexType. The program will then display the UserId property value
obtained from the response.
NOTE
This is a very simple example to show how to get connected with a minimum of code. The Enhanced quick start will build
upon this sample to apply better design patterns.
You can find the complete Visual Studio solution for this project in the PowerApps-Samples repo under
cds/webapi/C#/QuickStart.
Prerequisites
Visual Studio 2019
Internet connection
Valid user account for a Dataverse environment
Url to the Dataverse environment you want to connect with
Basic understanding of the Visual C# language
NOTE
To authenticate you must have an app registered in Azure Active Directory (AD). This quick start example provides an app
registration clientid value you can use for the purpose of running sample code published by Microsoft. However, for
your own custom applications you must register your apps with AD. More information: Walkthrough: Register an app with
Azure Active Directory
4. In Solution Explorer , right-click the project you created and select Manage NuGet Packages in the
context menu. We will now bring in required assemblies for our project.
5. Browse for the Microsoft.IdentityModel.Clients.ActiveDirectory NuGet package, select it, and then
choose Install .
NOTE
You will be prompted to preview and OK the assembly additions, and Accept the license agreements, for the
installed packages and their contents.
To use the Microsoft Authentication Library (MSAL) instead of Azure Active Directory Authentication
Library (ADAL), browse for and install the Microsoft.Identity.Client package instead of the
Microsoft.IdentityModel.Clients.ActiveDirectory package.
6. Browse for the Newtonsoft.Json NuGet package and install the latest version.
Edit Program.cs
Follow these next steps to add code for the main program.
1. Replace the entire contents of Program.cs with the following code. If you used a different name for your
project than WebAPIQuickStart, you will need to change to namespace name in the new code to match your
project name.
C#/ADAL
C#/MSAL
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using Newtonsoft.Json.Linq;
using System;
using System.Net.Http;
using System.Net.Http.Headers;
namespace WebAPIQuickStart
{
class Program
{
static void Main()
{
// TODO Specify the Dataverse environment URL to connect with.
string resource = "https://<env-name>.<region>.dynamics.com";
// Azure Active Directory app registration shared by all Power App samples.
// For your custom apps, you will need to register them with Azure AD yourself.
// See https://docs.microsoft.com/powerapps/developer/data-platform/walkthrough-register-app-
azure-active-directory
var clientId = "51f81489-12ee-4a9e-aaae-a2591f45987d";
var redirectUri = new Uri("app://58145B91-0C36-4500-8554-080854F2AC97");
#region Authentication
// The authentication context used to acquire the web service access token
var authContext = new AuthenticationContext(
"https://login.microsoftonline.com/common", false);
// Get the web service access token. Its lifetime is about one hour after
// which it must be refreshed. For this simple sample, no refresh is needed.
// See https://docs.microsoft.com/powerapps/developer/data-platform/authenticate-oauth
var token = authContext.AcquireTokenAsync(
resource, clientId, redirectUri,
new PlatformParameters(
PromptBehavior.SelectAccount // Prompt the user for a logon account.
),
UserIdentifier.AnyUser
).Result;
#endregion Authentication
if (response.IsSuccessStatusCode)
{
// Parse the JSON formatted service response to obtain the user ID.
JObject body = JObject.Parse(
response.Content.ReadAsStringAsync().Result);
Guid userId = (Guid)body["UserId"];
2. Right below the TODO comment in the above code, replace the resource value with the actual URL of
your Dataverse test environment. To find the URL value for your test environment, follow these steps:
a. Navigate your browser to Power Apps.
b. Select the environments icon (to the right of the search field), and choose a test environment.
c. Select the settings icon and choose Developer resources .
d. Copy the Web API endpoint URL from "https:" through ".com" leaving off the /api/data/v9.x.
e. Replace the resource string value in the program code with that endpoint URL value. For example:
string resource = "https://contoso.api.crm.dynamics.com";
2. With the console window active, press any key to terminate the program.
Congratulations!
You have successfully connected to the Web API.
The quick start sample shows a simple approach to create a Visual Studio project without any exception
handling or method to refresh the access token.
This is enough to verify you can connect, but it doesn't necessarily represent a good pattern for building an app.
The Enhanced quick start topic shows how to implement exception handling methods, basic authentication
method using connection string, a re-usable method to refresh the access token, and introduces how to build re-
usable methods to perform data operations.
Next steps
Learn how to structure your code for a better design.
Enhanced quick start
Quickstart: Blazor Server Web API sample (C#)
7/19/2021 • 5 minutes to read • Edit Online
In this quickstart, you'll create a Blazor Server application to connect to your Microsoft Dataverse environment
using the Web API.
You'll authenticate and use HttpClient to send a GET request containing the WhoAmI function. The response will
be a WhoAmIResponse complex type. After call completion, the UserId property value is displayed.
NOTE
This is a very simple example to show how to get connected with a minimum of code. The Enhanced quickstart will build
upon this sample to apply better design patterns.
Prerequisites
Visual Studio 2019 (version 16.6.2 or later recommended)
Familiarity with the Microsoft Azure portal
Internet connection
Valid user account for a Dataverse instance
Administrator access to grant application registrations
URL to the Dataverse environment you want to connect with
Basic understanding of the Visual C# language
NOTE
To authenticate you must have an app registered in Azure Active Directory. The registration will happen automatically as
part of the template creation, but will require additional updates in the Azure portal.
3. Choose the appropriate dropdown and then replace CRM520451 in the example with your environment's
name.
4. Select Create .
5. In your Blazor Server app, open appsettings.json and add an entry for "ClientSecret". The Active
Directory settings should look like this:
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "{org}.onmicrosoft.com",
"TenantId": "{tenantId}",
"ClientId": "{clientId}",
"ClientSecret": "{secret}",
"CallbackPath": "/signin-oidc"
}
}
2. Open the App.razor file and add the following statements to the top of the file. Change the namespace to
match the name of your application.
@using BlazorWebAPIExample.Data
@inject TokenProvider Service
3. Add a @code block to accept a parameter and move the token into the service.
[Parameter]
public TokenProvider InitialState { get; set; }
4. Open the Pages/_Host.cshtml file and add the following using statements after the namespace
declaration.
@using BlazorCommonDataService.Data
@using Microsoft.AspNetCore.Authentication
5. After the <body> tag, add the following code and update the app component to acquire and pass the
token.
@{
var token = new TokenProvider
{
AccessToken = await HttpContext.GetTokenAsync("access_token")
};
}
<app>
<component type="typeof(App)" param-InitialState="token" render-mode="ServerPrerendered" />
</app>
6. Obtain the environment name for the Dataverse management API. If you're not sure what the name is,
open the Power Platform admin center, navigate to Environments then choose Open environment .
You will see a URL like this: https://{org}.crm.dynamics.com where {org} is the environment name.
7. Add an entry named CDSAPI to the appsettings.json file with the environment URL as the value. Append
/api/data/v9.0/ to the end of the URL so it looks like this:
{ "CDSAPI": "https://{org}.crm.dynamics.com/api/data/v9.0/" }
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
9. In the Startup.cs class, add registrations to retrieve the authentication token and configure a client
ready to use the token. Place this code between services.AddAuthentication and
services.AddControllersWithViews .
services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme,
opt =>
{
var resourceUri = new Uri(Configuration["CDSAPI"]);
var resource = $"{resourceUri.Scheme}://{resourceUri.Host}/";
opt.ResponseType = "code";
opt.SaveTokens = true;
opt.Scope.Add("user_impersonation");
opt.Scope.Add(new Uri(Configuration["CDSAPI"]).Host);
opt.Resource = resource;
});
services.AddScoped<TokenProvider>();
services.AddHttpClient("CDS", client =>
{
client.BaseAddress = new Uri(Configuration["CDSAPI"]);
});
The first registration allows requesting the token with the proper scope. The second registers the service that
tracks the token, and the third creates a client with the base API address pre-configured.
@using System.Text.Json
@using BlazorWebAPIExample.Data;
@using System.Net.Http.Headers;
@inject IHttpClientFactory Factory
@inject TokenProvider TokenProvider
@if (Loading)
{
<div class="alert alert-warning">Loading...</div>
}
@if (Error)
{
<div class="alert alert-danger">@ErrorMessage</div>
}
@if (!Loading && !Error)
{
<div class="alert alert-info">You did it! Your user id is: @UserId</div>
}
Next steps
Learn how to structure your code for a better design.
Enhanced quickstart
See Also
Tutorial: Create an ASP.NET Core Blazor WebAssembly App using Dataverse
Enhanced quick start
10/1/2021 • 9 minutes to read • Edit Online
This topic demonstrates how to re-factor the code in the Quick start topic by adding re-usable HttpClient and
error handling methods. Complete the steps in the Quick start topic to create a new Visual Studio project before
you begin this topic, or simply download the MSAL version of the complete Visual Studio project.
If you get stuck following this enhanced quick start, you can download the completed solution.
Edit the <configuration> element to add a the connectionStrings node as shown below:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.2" />
</startup>
<connectionStrings>
<!--Online using Microsoft 365-->
<add name="Connect"
connectionString="Url=https://yourorg.api.crm.dynamics.com;[email protected];Passwor
d=mypassword;" />
</connectionStrings>
</configuration>
This creates a connection string that can be referenced by name, in this case Connect , so that you can define
more than one connection if you wish.
Edit the connection string Url , Username and Password values in the connectionString parameter to match
what you need to connect to your Microsoft Dataverse test environment.
Add using directive to Program.cs
At the top of your Program.cs file, add this using directive:
using System.Configuration;
NOTE
The name of the class will determine how you will reference these helper properties and methods within your main
program. The remaining instructions will assume you named the class SampleHelpers .
using System;
using System.Linq;
using System.Net.Http;
namespace EnhancedQuickStart
{
/// <summary>
/// Shared code for common operations used by many Power Apps samples.
/// </summary>
class SampleHelpers
{
//These sample application registration values are available for all online instances.
//You can use these while running sample code, but you should get your own for your own apps
public static string clientId = "51f81489-12ee-4a9e-aaae-a2591f45987d";
public static string redirectUrl = "app://58145B91-0C36-4500-8554-080854F2AC97";
/// <summary>
/// Method used to get a value from the connection string
/// </summary>
/// <param name="connectionString"></param>
/// <param name="parameter"></param>
/// <returns>The value from the connection string that matches the parameter key value</returns>
public static string GetParameterValueFromConnectionString(string connectionString, string parameter)
{
try
{
return connectionString.Split(';').Where(s =>
s.Trim().StartsWith(parameter)).FirstOrDefault().Split('=')[1];
}
catch (Exception)
{
return string.Empty;
}
}
/// <summary>
/// Returns an HttpClient configured with an OAuthMessageHandler
/// </summary>
/// <param name="connectionString">The connection string to use.</param>
/// <param name="clientId">The client id to use when authenticating.</param>
/// <param name="redirectUrl">The redirect Url to use when authenticating</param>
/// <param name="version">The version of Web API to use. Defaults to version 9.2 </param>
/// <returns>An HttpClient you can use to perform authenticated operations with the Web API</returns>
public static HttpClient GetHttpClient(string connectionString, string clientId, string redirectUrl,
string version = "v9.2")
{
string url = GetParameterValueFromConnectionString(connectionString, "Url");
string username = GetParameterValueFromConnectionString(connectionString, "Username");
string password = GetParameterValueFromConnectionString(connectionString, "Password");
try
{
HttpMessageHandler messageHandler = new OAuthMessageHandler(url, clientId, redirectUrl,
username, password,
new HttpClientHandler());
return httpClient;
}
catch (Exception)
{
throw;
}
}
4. Add an OAuthMessageHandler class in its own class file using the code provided below.
This class ensures that the access token is refreshed each time an operation is performed. Each access token will
expire after about an hour. This class implements a DelegatingHandler that will work with the Microsoft
Authentication Library (MSAL) authentication context to call the correct AcquireToken variation every time an
operation is performed so you don't need to explicitly manage token expiration.
using Microsoft.Identity.Client;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security;
using System.Threading.Tasks;
namespace EnhancedQuickStart
{
/// <summary>
/// Custom HTTP message handler that uses OAuth authentication through
/// Microsoft Authentication Library (MSAL).
/// </summary>
class OAuthMessageHandler : DelegatingHandler
{
private AuthenticationHeaderValue authHeader;
AuthenticationResult authBuilderResult;
if (username != string.Empty && password != string.Empty)
{
//Make silent Microsoft.Identity.Client (MSAL) OAuth Token Request
var securePassword = new SecureString();
foreach (char ch in password) securePassword.AppendChar(ch);
authBuilderResult = authBuilder.AcquireTokenByUsernamePassword(scopes, username,
securePassword)
.ExecuteAsync().Result;
}
else
{
//Popup authentication dialog box to get token
authBuilderResult = authBuilder.AcquireTokenInteractive(scopes)
.ExecuteAsync().Result;
}
//Note that an Azure AD access token has finite lifetime, default expiration is 60 minutes.
authHeader = new AuthenticationHeaderValue("Bearer", authBuilderResult.AccessToken);
}
Update Program.cs
Now that you have made the changes to Enable passing credentials in a connection string and Add helper code,
you can update the Main method in the Program.cs file to only contain the following:
using Newtonsoft.Json.Linq;
using System;
using System.Configuration;
using System.Net.Http;
namespace EnhancedQuickStart
{
class Program
{
static void Main(string[] args)
{
try
{
//Get configuration data from App.config connectionStrings
string connectionString =
ConfigurationManager.ConnectionStrings["Connect"].ConnectionString;
if (response.IsSuccessStatusCode)
{
//Get the response content and parse it.
JObject body = JObject.Parse(response.Content.ReadAsStringAsync().Result);
Guid userId = (Guid)body["UserId"];
Console.WriteLine("Your UserId is {0}", userId);
}
else
{
Console.WriteLine("The request failed with a status of '{0}'",
response.ReasonPhrase);
}
Console.WriteLine("Press any key to exit.");
Console.ReadLine();
}
}
catch (Exception ex)
{
SampleHelpers.DisplayException(ex);
Console.WriteLine("Press any key to exit.");
Console.ReadLine();
}
}
}
}
This is less code and you have added error handling and the means to refresh the access token with every use of
the HttpClient .
Press F5 to run the program. Just like the Quick start sample, the output should look like this:
if (response.IsSuccessStatusCode)
{
//Get the response content and parse it.
JObject body = JObject.Parse(response.Content.ReadAsStringAsync().Result);
Guid userId = (Guid)body["UserId"];
Console.WriteLine("Your UserId is {0}", userId);
}
else
{
Console.WriteLine("The request failed with a status of '{0}'",
response.ReasonPhrase);
}
to this:
Before you begin, it would be a good idea to go out to the Web API Reference and review these topics:
WhoAmI Function
WhoAmIResponse ComplexType
Notice how the WhoAmI Function returns a WhoAmIResponse ComplexType and the WhoAmIResponse
ComplexType contains three GUID properties: BusinessUnitId , UserId , and OrganizationId ;
The code we will add is simply to model these into a re-usable method that accepts an HttpClient as a
parameter.
NOTE
Exactly how you do this is a matter of personal preference. This design is provided because of it's relative simplicity.
To this:
partial class Program
To this:
partial class Program
In this way the Program class in ProgramMethods.cs file is just an extension of the original Program class
in the Program.cs file.
3. Add the following using directives to the top of the ProgramMethods.cs file.
using Newtonsoft.Json.Linq;
using System;
using System.Net.Http;
4. Add the following method to the Program class in the ProgramMethods.cs file.
5. Add the following class outside of the Program class but within the namespace of the
ProgramMethods.cs file.
if (response.IsSuccessStatusCode)
{
//Get the response content and parse it.
JObject body = JObject.Parse(response.Content.ReadAsStringAsync().Result);
Guid userId = (Guid)body["UserId"];
Console.WriteLine("Your UserId is {0}", userId);
}
else
{
Console.WriteLine("The request failed with a status of '{0}'",
response.ReasonPhrase);
}
With this:
7. Press F5 to run the sample and you should get the same results as before.
Troubleshooting
If you have any trouble running this sample, you can download all the Power Apps samples from the GitHub
repository at https://github.com/Microsoft/PowerApps-Samples.
IMPORTANT
All the samples on the GitHub repo are configured to use a common App.config that is located at PowerApps-
Samples:cds\App.config. When you set your connection string you must edit this file. When you do, you can run all the
samples without setting your credentials again.
Next steps
Use the following resources to learn more:
Perform operations using the Web API
This topic demonstrates how to create a new project in Visual Studio 2017 (or later) that builds a console
application that uses the Dataverse Web API. It illustrates the common references and project resources that
most applications, including the SDK C# samples, use to implement Web API-based solutions.
Prerequisites
The following prerequisites are required to build the console application described in this section.
Visual Studio 2017 installed on your development computer. Any edition, including Visual Studio Express,
should be sufficient to work with the Dataverse Web API.
A NuGet client must be installed: either the command-line utility or the Visual Studio extension. For more
information, see Installing NuGet.
Create a project
The following procedure demonstrates how to create a console application project in C# that uses the Microsoft
.NET Framework.
New Project
1. In Visual Studio, click New Project . The New Project dialog is displayed.
2. In the left navigation pane under Templates , select Visual C# .
3. Above the list of available templates, select .NET Framework 4.6.2 .
4. From the list of templates, select Console App(.NET Framework) . (Alternately choose the project type
suited to your solution.) All of the Web API C# samples are console applications.
5. In the text boxes near the bottom of the form, supply the project name and location, and then select OK.
(For this topic, the solution name "StartWebAPI-CS" was used.) The initial solution files will be generated
and the solution loaded into Visual Studio.
6. Under the Project menu, open the project's properties form and verify the target framework is set to
.NET Framework 4.6.2 .
Install and verify the required assembly references
1. After the project opens, click on Tools in the control bar on the top of your project. Select NuGet Package
Manager > Package Manager Console and install the following NuGet packages.
install-package Newtonsoft.Json
install-package System.Net.Http
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
3. If you added routinely used assemblies or references in the previous sections, you may also want to add
corresponding using statements for these resources.
4. Save the file.
<connectionStrings>
<clear />
<!-- When providing a password, make sure to set the app.config file's security so that only you
can read it. -->
<add name="default" connectionString="Url=https://myserver/myorg/; Username=name;
Password=password; Domain=domain" />
<add name="CrmOnline" connectionString="Url=https://mydomain.crm.dynamics.com/;
[email protected]; Password=password" />
</connectionStrings>
<appSettings>
<!--For information on how to register an app and obtain the ClientId and RedirectUrl
values see https://msdn.microsoft.com/dynamics/crm/mt149065 -->
<!--Active Directory application registration. -->
<!--These are dummy values and should be replaced with your actual app registration values.-->
<add key="ClientId" value="e5cf0024-a66a-4f16-85ce-99ba97a24bb2" />
<add key="RedirectUrl" value="https://localhost/SdkSample" />
<!-- Use an alternate configuration file for connection string and setting values. This optional
setting
enables use of an app.config file shared among multiple applications. If the specified file does
not exist, this setting is ignored.-->
<add key="AlternateConfig" value="C:\Temp\crmsample.exe.config"/>
</appSettings>
2. When developing or deploying a solution, the actual connection and application registration values must
be substituted for the example placeholder values.
Next steps
At this point the solution can be built without errors. If you edit the application configuration file to supply
values for your Dynamics 365 Server, the program should also successfully connect to that server. The solution
represents a skeletal frame that is ready to accept custom code, including calls to the Dataverse Web API.
TIP
Before you leave this topic, consider saving your project as a project template. You can then reuse that template for future
learning projects and save yourself some time and effort in setting up new projects. To do this, while your project is open
in Microsoft Visual Studio, in the File menu select Expor t template . Follow the Export Template Wizard instructions to
create the template.
See also
Get Started with the Web API (C#)
Use the Web API Helper Library (C#)
Perform operations using the Web API
Authenticate to Microsoft Dataverse with the Web
API
4/30/2021 • 2 minutes to read • Edit Online
An Azure Active Directory (AAD) group team, similar to an owner team, can own records and can have security
roles assigned to the team. To read more about AAD group teams see Manage group teams.
The following sections describe how to work with AAD group teams using the Web API.
{
"azureactivedirectoryobjectid":"<group object ID>",
"membershiptype":0
}
Where:
Membership type is defined in the team property membershiptype
Name of the team is the name of the AAD group
Team type is based on the AAD group type - for example "Security" or "Microsoft 365"
{
"@odata.id":"[Organization URI]/api/data/v9.0/roles(<role ID>)"
}
{
"@odata.id":"[Organization URI]/api/data/v9.0/roles(<role ID>)"
}
{
"[email protected]": "[Organization URI]/api/data/v9.0/systemusers(azureactivedirectoryobjectid=<user
object ID>)"
}
Response
{
"@odata.context": "https://contoso.crm2.dynamics.com/api/data/v9.0/$metadata#roles",
"value": [
{
"@odata.etag": "W/\"1649865\"",
"name": "System Administrator",
"roleid": "ae0daa93-e566-eb11-bb2b-000d3ac4c3f6",
"_parentrootroleid_value": "ae0daa93-e566-eb11-bb2b-000d3ac4c3f6",
"t_x002e_azureactivedirectoryobjectid": "e1341054-98ed-489b-a522-15e9e277b737",
"t_x002e_membershiptype": 0,
"t_x002e_teamid": "26e477f8-3f6a-eb11-bb2b-000d3af6caae",
"t_x002e_name": "testgroup"
}
]
}
Members of an AAD group can check their security privileges without being a user of Dataverse using the
following command.
Request
GET [Organization URI]/api/data/v9.0/RetrieveAadUserPrivileges(DirectoryObjectId=<user object ID>)
Response
{
"@odata.context":
"https://contoso.crm2.dynamics.com/api/data/v9.0/$metadata#Microsoft.Dynamics.CRM.RetrieveAadUserPrivilegesR
esponse",
"RolePrivileges": [
{
"Depth": "Global",
"PrivilegeId": "0a620778-3e9f-46ec-9766-000624db57ba",
"BusinessUnitId": "aa0daa93-e566-eb11-bb2b-000d3ac4c3f6",
"PrivilegeName": "prvDeleteHierarchyRule"
},
…
]
}
See also
Manage app and resource access using Azure Active Directory groups
Web API types and operations
4/30/2021 • 16 minutes to read • Edit Online
In order to use the Web API you need to find information about what is available for you to use. The service
describes itself via service and metadata documents that you can access. This topic will introduce important
concepts and describe how you can find the information you need using documentation generated from the
service and metadata documents as well as the documentation of the system entity types, functions, and actions.
Terminology
The Web API is implemented using the OData v4 standard which uses a specific set of terms you need to be
familiar with. Entity Data Model (EDM) is the abstract data model that is used to describe the data exposed by an
OData service. The following table is a selected list of terms defined in OData Version 4.0 Part 1: Protocol Plus
Errata 02 which you should understand.
T ERM DEF IN IT IO N
Entity types Named structured types with a key. They define the named
properties and relationships of an entity. Entity types may
derive by single inheritance from other entity types.
Enumeration types or Enum types Named primitive types whose values are named constants
with underlying integer values.
Functions Operations that do not have side effects and may support
further composition, for example, with additional filter
operations, functions or an action
Service documents
There are two service documents you can reference to learn more about the Web API.
Service document
The following query, typed into the address field of your browser, returns the service document, a complete list
of all the entity sets that are available for your organization. Note that [organization URI] represents the URL for
your organization.
[Organization URI]/api/data/v9.0
The entity sets are returned in the form of a JSON array. Each item in the array has three properties as listed in
this table.
name This is the name of the entity set. This data is from the
EntityMetadata EntityType/ EntitySetName property for
the entity.
kind For the web API only Entity sets are listed.
This information can be retrieved using a GET request and may be useful to get a list of all entity sets available
using the service.
This document can be used as a data source to generate classes that will provide strongly typed objects for the
service. But if you are not using generated classes, you may want to review documentation generated from this
information instead. The Web API Reference uses information primarily from this document taken from a base
environment with some common additional solutions installed.
You can learn more about this document in OData Version 4.0 Part 3: Common Schema Definition Language
(CSDL) Plus Errata 02.
TIP
Before your read the rest of this topic, download the $metadata for your organization and take a look at how the types,
functions, and actions described are included in the $metadata and the supporting documentation.
You can view or download the document using your browser by navigating to the URL.
Metadata annotations
The metadata document includes several types of Annotation elements which provide additional information
about metadata elements and the capabilities of the service. Starting with v9.x, these annotations are not
included with the default metadata document unless explicitly requested. These annotations increase the size of
the metadata document and are not always necessary.
To include annotations you have two options when requesting the metadata document:
Append the ?annotations=true query string parameter to the URL.
Add the Prefer: odata.include-annotations="*" header to the request.
Each Annotation element includes a Term attribute that describes the type of annotation. The definitions of all
the possible annotations used by OData v4 can be found in OData Vocabularies Version 4.0. The following table
provides some examples used by the Web API.
Org.OData.Core.V1.DereferenceableIDs Whether Entity-ids are URLs that locate the identified entity.
Org.OData.Capabilities.V1.CrossJoinSupported Whether cross joins for the entity sets in this container are
supported.
Entity types
The Web API EntityType Reference lists each of the system entity types exposed through the web API which store
business data. An entity type is a named structured type with a key. It defines the named properties and
relationships of an entity. Entity types may derive by single inheritance from other entity types. Web API
Metadata EntityType Reference lists each of the entity types used to manage system metadata. Both are entity
types but the way you work with them is different. See Use the Web API with Microsoft Dataverse metadata for
information about using model entities. Each entity type is included within an EntityType element in the
$metadata . The following is the definition of the account EntityType/ from the $metadata with properties and
navigation properties removed.
Each EntityType reference page in the Web API documentation uses information from the $metadata to show
the following information when available.
IN F O RM AT IO N DESC RIP T IO N
Base Type This is the entity type that the entity type inherits from.
Display Name This information is not in the $metadata , it’s retrieved from
the EntityMetadata EntityType/ DisplayName property.
Primar y Key The property value that contains the unique identifier to
refer to an instance of an entity.
Primar y Attribute Many entities require that a primary attribute value be set,
so this is included for convenience.
Operations bound to the entity type When an operation is bound to a specific entity type, it’s
listed for convenience.
Operations that use the entity type This list shows any operations that may use the entity type.
This is derived by collecting references to all operations that
refer to the current type in the parameters or as a return
value.
Entity types that inherit from the entity type This list includes any entity types that inherit directly from
the entity type. See Type inheritance for more information.
Alternate keys
Although Dataverse allows for creating alternate keys, only the primary key will be found in the default entities.
None of the system entities have alternate keys defined. If you define alternate keys for an entity, they will be
included in the $metadata EntityType element as an Annotation like the following:
<Annotation Term="OData.Community.Keys.V1.AlternateKeys">
<Collection>
<Record Type="OData.Community.Keys.V1.AlternateKey">
<PropertyValue Property="Key">
<Collection>
<Record Type="OData.Community.Keys.V1.PropertyRef">
<PropertyValue Property="Alias" String="key name" />
<PropertyValue Property="Name" PropertyPath="key name" />
</Record>
</Collection>
</PropertyValue>
</Record>
</Collection>
</Annotation>
Information about alternate keys can also be retrieved from the metadata using the EntityMetadata EntityType/
Keys collection-valued navigation property using the Web API or the EntityMetadata. Keys property using the
organization service.
Type inheritance
Inheritance allows for sharing of common properties and categorizing entity types into groups. All entity types
in the web API inherit from two of the following entity types. All business entity types inherit from the
crmbaseentity EntityType/ and all model entities inherit from the crmmodelbaseentity EntityType/.
B A SE EN T IT Y DESC RIP T IO N
crmbaseentity EntityType/ All business entities inherit from this entity. It has no
properties. It only serves as an abstract base entity.
activitypointer EntityType/ All activity entities inherit from this entity. It defines the
common set of properties and navigation properties for
activity entities.
MetadataBase EntityType/) All metadata entities inherit from this entity. It provides the
MetadataId and HasChanged properties for all metadata
entities.
AttributeMetadata EntityType/ All model entities that represent different types of attributes
inherit from this entity.
EnumAttributeMetadata EntityType/ Those model entities that represent attributes that include a
set of options inherit from this entity.
OptionSetMetadataBase EntityType/ This model entity type provides a common set of properties
used by the BooleanOptionSetMetadata EntityType/ and
OptionSetMetadata EntityType/ model entity types that
inherit from it.
B A SE EN T IT Y DESC RIP T IO N
RelationshipMetadataBase EntityType/ This entity type provides a common set of properties used
by the ManyToManyRelationshipMetadata EntityType/ and
OneToManyRelationshipMetadata EntityType/ model entity
types that inherit from it.
Properties
Each entity type may have declared properties that correspond to attributes. In the Web API EntityType
Reference and Web API Metadata EntityType Reference content, properties that are inherited from a base entity
type are combined within the list of declared properties for each entity type. The inheritance is called out in the
description for each property.
In the $metadata EntityType elements each property is included in a Property element with a Name attribute
value that corresponds to the properties you will set in code. The Type attribute value specifies the data type of
the property. Properties for business entity types generally use OData primitive types.
The following is an example of the account EntityType/ name property defined in the $metadata .
The description of the property is available in an Annotation element with the Term attribute property of
Org.OData.Core.V1.Description. This description is taken from the AttributeMetadata EntityType/ Description
property value. Not all properties have a description.
Each property may be computed. This means that the value may be set by the system. This is specified in an
Annotation element with the Term attribute value of Org.OData.Core.V1.Computed.
Each property may also have limitations on whether it may be updated. This is defined in an Annotation
element with a Term attribute value Org.OData.Core.V1.Permissions. The only option set for this is
Org.OData.Core.V1.PermissionType/Read, which indicates that the property is read only.
Primitive types
OData supports a wide range of data types but Dataverse doesn’t use all of them. The following table describes
how Dataverse Organization service types are mapped to OData primitive types.
Lookup properties
For most single-valued navigation properties you will find a computed, read-only property that uses the
following naming convention: _<name>_value where the <name> matches the name of the single-valued
navigation property. The exception to this pattern is when a lookup attribute of the entity can accept multiple
types of entity references. A common example is how the incident entity customerid attribute may be set to a
reference that is either a contact or account entity. In the incident EntityType/ Single-valued navigation
properties you will find customerid_account and customerid_contact as separate single-valued navigation
properties to reflect the customer associated with an opportunity. If you set one of these single-valued
navigation properties, the other will be set to null because they are both bound to the customerid attribute. In
the incident EntityType/ Properties you’ll find a _customerid_value lookup property that contains the same
value that is set for whichever of the single-valued navigation properties contain a value.
Generally, you should avoid using lookup properties and use the corresponding single-valued navigation
properties instead. These properties have been included because they may be useful for certain integration
scenarios. These properties are read-only and computed because they will simply reflect the changes applied
using the corresponding single-valued navigation property.
When you include lookup properties in a query, you can request annotations to be included that provide
additional information about the data that is set for those underlying attributes which aren’t represented by a
single-valued navigation property. More information:Retrieve data about lookup properties
Navigation properties
In OData, navigation properties allow you to access data related to the current entity. When you retrieve an
entity you can choose to expand navigation properties to include the related data. There are two types of
navigation properties: single-valued and collection-valued.
Every navigation property that represents a single-valued navigation property will have a corresponding
collection-valued navigation property indicated by the Partner attribute value. Each single-valued navigation
property also has a ReferentialConstraint element with Property attribute value that represents the computed
read-only lookup property that can be used to retrieve corresponding GUID value of the related entity. More
information:Lookup properties
When the collection-valued navigation property represents a many-to-many relationship, the name of the
navigation property and the name of the partner will be the same. The following represents the account
EntityType/ accountleads_association collection-valued navigation property which represents a many-to-many
relationship:
The difference between one-to-many and many-to-many relationships is not important when using the Web
API. The way you associate entities is the same regardless of the type of relationship. Although many-to-many
relationships still use intersect entities behind the scenes, only a few special system intersect entities are
included within the Web API EntityType Reference. For example, campaignactivityitem EntityType/ is technically
an intersect entity, but it is included because it has more properties than an ordinary intersect entity.
An ordinary intersect entity has only the four basic properties required to maintain the many-to-many
relationship. When you create a custom many-to-many relationship between entities, an ordinary intersect
entity will be created to support the relationship. Because you should use navigation properties to perform
operations involving many-to-many relationships, ordinary intersect entities are not documented but are still
available using the Web API. These intersect entity types are accessible using an entity set name that uses the
following naming convention: <intersect entity logical name>+’collection’. For example, you can retrieve
information from the contactleads intersect entity type using
[Organization URI]/api/data/v9.0/contactleadscollection . You should only use these ordinary intersect entities
in situations where you wish to apply change tracking.
Actions
Actions are operations that allow side effects, such as data modification, and cannot be further composed in
order to avoid non-deterministic behavior.
The Web API Action Reference topic lists each of the available system actions. More information:Use Web API
actions.
Functions
Functions are operations that do not have side effects and may support further composition, for example, with
additional filter operations, functions or an action
There are two types of functions defined in the Web API:
The Web API Function Reference lists each of the available system functions.
The Web API Query Function Reference topic lists functions which are intended to be used as criteria in a
query.
More information:Use Web API functions
Complex types
Complex types are keyless named structured types consisting of a set of properties. Complex types are
commonly used as property values in model entities, or as parameters or return values for operations.
Web API ComplexType Reference lists all the system complex types. The following is the WhoAmIResponse
ComplexType" / from the $metadata.
<ComplexType Name="WhoAmIResponse">
<Property Name="BusinessUnitId" Type="Edm.Guid" Nullable="false" />
<Property Name="UserId" Type="Edm.Guid" Nullable="false" />
<Property Name="OrganizationId" Type="Edm.Guid" Nullable="false" />
</ComplexType>
Enumeration types
Enumeration types or EnumTypes are named primitive types whose values are named constants with underlying
integer values.
Web API EnumType Reference lists all the system enumeration types. Enumeration types are named primitive
types whose values are named constants with underlying integer values. The following is the AccessRights
EnumType" / from the $metadata.
<EnumType Name="AccessRights">
<Member Name="None" Value="0" />
<Member Name="ReadAccess" Value="1" />
<Member Name="WriteAccess" Value="2" />
<Member Name="AppendAccess" Value="4" />
<Member Name="AppendToAccess" Value="16" />
<Member Name="CreateAccess" Value="32" />
<Member Name="DeleteAccess" Value="65536" />
<Member Name="ShareAccess" Value="262144" />
<Member Name="AssignAccess" Value="524288" />
</EnumType>
See also
Use the Dataverse Web API
Authenticate to Dataverse with the Web API
Perform operations using the Web API
Perform operations using the Web API
4/30/2021 • 2 minutes to read • Edit Online
The Web API provides a RESTful web service interface that you can use to interact with data in Microsoft
Dataverse using a wide variety of programming languages and libraries.
NOTE
Information under this section is also applicable to the Dynamics 365 Customer Engagement (on-premises) users.
In this section
Compose Http requests and handle errors
Query data using the Web API
Retrieve and execute predefined queries
Create a table using the Web API
Retrieve a table using the Web API
Update and delete tables using the Web API
Associate and disassociate tables using the Web API
Use Web API functions
Use Web API actions
Execute batch operations using the Web API
Impersonate another user using the Web API
Perform conditional operations using the Web API
Manage duplicate detection during Create and Update operations
See also
Use the Dataverse Web API
Authenticate to Dataverse with the Web API
Web API types, functions and actions
Compose HTTP requests and handle errors
9/26/2021 • 11 minutes to read • Edit Online
NOTE
Unsure about entity vs. table? See Developers: Understand terminology in Microsoft Dataverse.
You interact with the Web API by composing and sending HTTP requests. You need to know how to set the
appropriate HTTP headers and handle any errors included in the response.
PA RT DESC RIP T IO N
Protocol https://
Environment Name The unique name that applies to your environment. If your
company name is Contoso, then it may be contoso .
Resource The name of the entity (table), function, or action you want
to use.
The URL you will use will be composed with these parts: Protocol + Environment Name + Region + Base URL +
Web API path + Version + Resource.
Version compatibility
This release introduces capabilities which are not available in previous versions. Subsequent minor versions
may provide additional capabilities which will not be back ported to earlier minor versions. Your code written for
v9.0 will continue to work in future versions when you reference v9.0 in the URL you use.
As new capabilities are introduced they may conflict with earlier versions. This is necessary to allow the service
to become better. Most of the time, capabilities will remain the same between versions but you should not
assume they will.
NOTE
Unlike the v8.x minor releases, new capabilities or other changes added to future versions will not be applied to earlier
versions. You will need to pay attention to the version of the service you use and test your code if you change the version
used.
HTTP methods
HTTP requests can apply a variety of different methods. When using the web API you will only use the methods
listed in the following table.
M ET H O D USA GE
HTTP headers
Although the OData protocol allows for both JSON and ATOM format, the web API only supports JSON.
Therefore the following headers can be applied.
Every request should include the Accept header value of application/json , even when no response body is
expected. Any error returned in the response will be returned as JSON. While your code should work even if this
header isn't included, we recommend including it as a best practice
The current OData version is 4.0, but future versions may allow for new capabilities. To ensure that there is no
ambiguity about the OData version that will be applied to your code at that point in the future, you should
always include an explicit statement of the current OData version and the Maximum version to apply in your
code. Use both OData-Version and OData-MaxVersion headers set to a value of 4.0 .
Queries which expand collection-valued navigation properties may return cached data for those properties that
doesn't reflect recent changes. Include If-None-Match: null header in the request body to override browser
caching of Web API request. For more information see Hypertext Transfer Protocol (HTTP/1.1): Conditional
Requests 3.2 : If-None-Match.
All HTTP requests should include at least the following headers.
Accept: application/json
OData-MaxVersion: 4.0
OData-Version: 4.0
If-None-Match: null
Every request that includes JSON data in the request body must include a Content-Type header with a value of
application/json .
Content-Type: application/json
When you execute batch operations, you must apply a number of different headers in the request and
with each part sent in the body. More information: Execute batch operations using the Web API.
When you create a solution component and want to associate it with a solution, use the
MSCRM.SolutionUniqueName request header and set the value to the unique name of the solution.
When you want to enable duplicate detection when creating a new entity record, set the
MSCRM.SuppressDuplicateDetection request header value to false. More information: Check for Duplicate
records
When you want to by-pass custom plug-in code and the caller has the prvBypassCustomPlugins privilege,
set the MSCRM.BypassCustomPluginExecution request header to true . More information: Bypass Custom
Business Logic
403 Forbidden Expect this for the following types of Client Error
errors:
- AccessDenied
- AttributePermissionReadIsMissing
-
AttributePermissionUpdateIsMissingD
uringUpdate
- AttributePrivilegeCreateIsMissing
- CannotActOnBehalfOfAnotherUser
-
CannotAddOrActonBehalfAnotherUser
Privilege
- CrmSecurityError
- InvalidAccessRights
- PrincipalPrivilegeDenied
-
PrivilegeCreateIsDisabledForOrganizati
on
- PrivilegeDenied
- unManagedinvalidprincipal
- unManagedinvalidprivilegeedepth
401 Unauthorized Expect this for the following types of Client Error
errors:
- BadAuthTicket
- ExpiredAuthTicket
- InsufficientAuthTicket
- InvalidAuthTicket
- InvalidUserAuth
- MissingCrmAuthenticationToken
-
MissingCrmAuthenticationTokenOrgan
izationName
- RequestIsNotAuthenticated
- TamperedAuthTicket
- UnauthorizedAccess
- UnManagedInvalidSecurityPrincipal
413 Payload Too Large Expect this when the request length is Client Error
too large.
404 Not Found Expect this when the resource doesn't Client Error
exist.
405 Method Not Allowed This error occurs for incorrect method Client Error
and resource combinations. For
example, you can't use DELETE or
PATCH on a collection of entities.
- CannotDeleteDueToAssociation
- InvalidOperation
- NotSupported
C O DE DESC RIP T IO N TYPE
412 Precondition Failed Expect this for the following types of Client Error
errors:
- ConcurrencyVersionMismatch
- DuplicateRecord
429 Too Many Requests Expect this when API limits are Client Error
exceeded. More information: Service
Protection API Limits
501 Not Implemented Expect this when some requested Server Error
operation isn't implemented.
503 Service Unavailable Expect this when the web API service Server Error
isn't available.
{
"error":{
"code": "<This code is not related to the http status code and is frequently empty>",
"message": "<A message describing the error>"
}
}
IMPORTANT
The structure of the error messages is changing. This change is expected to be deployed to different regions over a period
starting in August through October 2020.
Before this change, the errors returned were in this format:
{
"error":{
"code": "<This code is not related to the http status code and is frequently empty>",
"message": "<A message describing the error>",
"innererror": {
"message": "<A message describing the error, this is frequently the same as the outer message>",
"type": "Microsoft.Crm.CrmHttpException",
"stacktrace": "<Details from the server about where the error occurred>"
}
}
}
We are removing the innererror property of the error message. You should remove any code that expects to parse this
property.
The OData Error Response guidance states "The innererror name/value pair SHOULD only be used in development
environments in order to guard against potential security concerns around information disclosure.". To align with this
guidance we are removing this property.
If you find that an application you use has a dependency on this property after this change is deployed, you can contact
support and request that the change be temporarily removed for your environment. This will provide time for the
application developer to make appropriate changes to remove this dependency.
namespace MyNamespace
{
public class MyClass : IPlugin
{
public void Execute(IServiceProvider serviceProvider)
{
try
{
throw new InvalidPluginExecutionException(OperationStatus.Canceled, 12345, "Example Error
Message.");
}
catch (InvalidPluginExecutionException ex)
{
tracingService.Trace("StackTrace:");
tracingService.Trace(ex.StackTrace);
throw ex;
}
}
}
}
When this plug-in is registered on the create of an account entity, and the request to create an account includes
the odata.include-annotations="*" preference, the request and response will look like the following:
Request
Response
@Microsoft.PowerApps.CDS.ErrorDetails.OperationStatus 1
The value of the OperationStatus set by the
InvalidPluginExecutionException(OperationStatus, Int32,
String) constructor.
@Microsoft.PowerApps.CDS.ErrorDetails.SubErrorCode 12345
The value of the SubErrorCode set by the
InvalidPluginExecutionException(OperationStatus, Int32,
String) constructor.
@Microsoft.PowerApps.CDS.HelpLink http://go.microsoft.com/fwlink/?
A URL that contains information about the error which may LinkID=398563&error=Microsoft.Crm.CrmException%3a80040265&client=platform
re-direct you to guidance about how to address the error.
NOTE
The @Microsoft.PowerApps.CDS.HelpLink is not guaranteed to provide guidance for every error. Guidance may be
provided proactively but most commonly it will be provided reactively based on how frequently the link is used. Please
use the link. If it doesn't provide guidance, your use of the link helps us track that people need more guidance about the
error. We can then prioritize including guidance to the errors that people need most. The resources that the link may
direct you to may be documentation, links to community resources, or external sites.
If you do not want to receive all annotations in the response, you can specify which specific annotations you
want to have returned. Rather than using Prefer: odata.include-annotations="*" , you can use the following to
receive only formatted values for operations that retrieve data and the helplink if an error occurs:
Prefer: odata.include-
annotations="OData.Community.Display.V1.FormattedValue,Microsoft.PowerApps.CDS.HelpLink"
.
Will result in the following value within the SharedVariables collection when sent using a webhook.
{
"key": "tag",
"value": "This is a value passed."
}
This can also be done using the Organization Service: Add a Shared Variable from the Organization Service
See also
Perform operations using the Web API
Query data using the Web API
Create a table (entity) using the Web API
Retrieve a table (entity) using the Web API
Update and delete tables (entities) using the Web API
Associate and disassociate tables (entities) using the Web API
Use Web API functions
Use Web API actions
Execute batch operations using the Web API
Impersonate another user using the Web API
Perform conditional operations using the Web API
Query data using the Web API
10/7/2021 • 16 minutes to read • Edit Online
NOTE
Unsure about entity vs. table? See Developers: Understand terminology in Microsoft Dataverse.
If you want to retrieve data for an entity set, use a GET request. When retrieving data, you can apply query
options to set criteria for the entity (table) data you want and the entity properties (columns) that should be
returned.
Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
{
"@odata.context":"[Organization URI]/api/data/v9.1/$metadata#accounts(name)",
"value":[
{
"@odata.etag":"W/\"501097\"",
"name":"Fourth Coffee (sample)",
"accountid":"89390c24-9c72-e511-80d4-00155d2a68d1"
},
{
"@odata.etag":"W/\"501098\"",
"name":"Litware, Inc. (sample)",
"accountid":"8b390c24-9c72-e511-80d4-00155d2a68d1"
},
{
"@odata.etag":"W/\"501099\"",
"name":"Adventure Works (sample)",
"accountid":"8d390c24-9c72-e511-80d4-00155d2a68d1"
}
]
}
Limits on number of table rows (entities) returned
Unless you specify a smaller page size, a maximum of 5000 rows will be returned for each request. If there are
more rows that match the query filter criteria, a @odata.nextLink property will be returned with the results. Use
the value of the @odata.nextLink property with a new GET request to return the next page of rows.
NOTE
Queries on entity (table) definitions aren’t limited or paged. More information:Query table definitions using the Web API
NOTE
Limiting results using $top will prevent odata.maxpagesize preference from being applied. You can use
odata.maxpagesize preference or $top , but not both at the same time. For more information about
odata.maxpagesize , see Specify the number of rows to return in a page.
You should also not use $top with $count .
NOTE
You can’t use an odata.maxpagesize preference value greater than 5000.
The following example queries the accounts entity set and returns the name property for the first three
accounts.
Request
Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
Content-Length: 402
Preference-Applied: odata.maxpagesize=3
{
"@odata.context":"[Organization URI]/api/data/v9.1/$metadata#accounts(name)",
"value":[
{
"@odata.etag":"W/\"437194\"",
"name":"Fourth Coffee (sample)",
"accountid":"7d51925c-cde2-e411-80db-00155d2a68cb"
},
{
"@odata.etag":"W/\"437195\"",
"name":"Litware, Inc. (sample)",
"accountid":"7f51925c-cde2-e411-80db-00155d2a68cb"
},
{
"@odata.etag":"W/\"468026\"",
"name":"Adventure Works (sample)",
"accountid":"8151925c-cde2-e411-80db-00155d2a68cb"
}
],
"@odata.nextLink":"[Organization URI]/api/data/v9.1/accounts?
$select=name&$skiptoken=%3Ccookie%20pagenumber=%222%22%20pagingcookie=%22%253ccookie%2520page%253d%25221%252
2%253e%253caccountid%2520last%253d%2522%257b8151925C-CDE2-E411-80DB-
00155D2A68CB%257d%2522%2520first%253d%2522%257b7D51925C-CDE2-E411-80DB-
00155D2A68CB%257d%2522%2520%252f%253e%253c%252fcookie%253e%22%20/%3E"
}
Use the value of the @odata.nextLink property to request the next set of records. Don’t change or append any
additional system query options to the value. For every subsequent request for additional pages, you should use
the same odata.maxpagesize preference value used in the original request. Also, cache the results returned or
the value of the @odata.nextLink property so that previously retrieved pages can be returned to.
NOTE
The value of the @odata.nextLink property is URI encoded. If you URI encode the value before you send it, the XML
cookie information in the URL will cause an error.
IMPORTANT
This is a performance best practice. If properties aren’t specified using $select , all properties will be returned.
When you request certain types of properties you can expect additional read-only properties to be returned
automatically.
If you request a money value, the _transactioncurrencyid_value lookup property will be returned. This property
contains only the GUID value of the transaction currency so you could use this value to retrieve information
about the currency using the transactioncurrency EntityType /. Alternatively, by requesting annotations you can
also get additional data in the same request. More information: Retrieve data about lookup properties
If you request a property that is part of a composite attribute for an address, you will get the composite
property as well. For example, if your query requests the address1_line1 property for a contact, the
address1_composite property will be returned as well.
Filter results
Use the $filter system query option to set criteria for which rows will be returned.
Comparison Operators
Logical Operators
or Logical or $filter=contains(name,'(sample)')
or contains(name,'test')
Grouping Operators
NOTE
This is a sub-set of the 11.2.5.1.1 Built-in Filter Operations. Arithmetic operators and the comparison has operator are not
supported in the Web API.
All filter conditions for string values are case insensitive.
F UN C T IO N EXA M P L E
contains $filter=contains(name,'(sample)')
endswith $filter=endswith(name,'Inc.')
startswith $filter=startswith(name,'a')
NOTE
This is a sub-set of the 11.2.5.1.2 Built-in Query Functions. Date , Math , Type , Geo and other string functions aren’t
supported in the web API.
any operator
The any operator returns true if the Boolean expression applied is true for any member of the collection,
otherwise it returns false . The any operator without an argument returns true if the collection is not empty.
Example
The example given below shows how you can retrieve all account entity records that have at least one email
with "sometext" in the subject.
all operator
The all operator returns true if the Boolean expression applied is true for all members of the collection,
otherwise it returns false .
Example
The example given below shows how you can retrieve all account entity records that have all associated tasks
closed.
The example given below shows how you can retrieve all account entity records that have at least one email
with "sometext" in the subject and whose statecode is active.
The example given below shows how you can also create a nested query using any and all operators.
Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
{
"@odata.context":"[Organization URI]/api/data/v9.1/$metadata#accounts(name)",
"value":[
{
"@odata.etag":"W/\"513479\"",
"name":"Adventure Works (sample)",
"accountid":"3adbf27c-8efb-e511-80d2-00155db07c77"
},
{
"@odata.etag":"W/\"514057\"",
"name":"Blue Yonder Airlines (sample)",
"accountid":"3edbf27c-8efb-e511-80d2-00155db07c77"
}
]
}
Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
{
"@odata.context":"[Organization URI]/api/data/v9.1/$metadata#accounts(name)",
"value":[
{
"@odata.etag":"W/\"514058\"",
"name":"Sample Child Account 1",
"accountid":"915e89f5-29fc-e511-80d2-00155db07c77"
},
{
"@odata.etag":"W/\"514061\"",
"name":"Sample Child Account 2",
"accountid":"03312500-2afc-e511-80d2-00155db07c77"
}
]
}
NOTE
It is possible to use $filter within $expand to filter results for related records in a Retrieve operation. You can use a
semi-colon separated list of system query options enclosed in parentheses after the name of the collection-valued
navigation property. The query options that are supported within $expand are $select , $filter , $top and
$orderby . More information: Options to apply to expanded tables.
The two options for filtering results based on values of collection-valued navigation properties are:
1. Construct a quer y using Lambda operators
Lambda operators allow you to apply filter on values of collection properties for a link-entity. The below
example retrieves the records of systemuser entity type that are linked with team and teammembership entity
types, that means it retrieves systemuser records who are also administrators of a team whose name is
"CITTEST".
Order results
Specify the order in which items are returned using the $orderby system query option. Use the asc or desc
suffix to specify ascending or descending order respectively. The default is ascending if the suffix isn’t applied.
The following example shows retrieving the name and revenue properties of accounts ordered by ascending
revenue and by descending name.
USE C A SE EXA M P L E
The aggregate functions are limited to a collection of 50,000 records. Further information around using
aggregate functionality with Dataverse can be found here: Use FetchXML to construct a query.
Additional details on OData data aggregation can be found here: OData extension for data aggregation version
4.0. Note that Dataverse supports only a sub-set of these aggregate methods.
You can also use parameter aliases when using functions. More information: Use Web API functions
NOTE
The count value does not represent the total number of rows in the system. It is limited by the maximum number of rows
that can be returned. More information: Limits on number of rows returned
If you want to retrieve the total number of rows for a table beyond 5000, use the RetrieveTotalRecordCount Function /.
The response @odata.count property will contain the number of rows that match the filter criteria irrespective of
an odata.maxpagesize preference limitation.
NOTE
You should not use $top with $count .
The following example shows that there are ten accounts that match the criteria where the name contains
“sample”, but only the first three accounts are returned.
Request
Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
Preference-Applied: odata.maxpagesize=3
{
"@odata.context":"[Organization URI]/api/data/v9.1/$metadata#accounts(name)",
"@odata.count":10,
"value":[
{
"@odata.etag":"W/\"502482\"",
"name":"Fourth Coffee (sample)",
"accountid":"655eaf89-f083-e511-80d3-00155d2a68d3"
},
{
"@odata.etag":"W/\"502483\"",
"name":"Litware, Inc. (sample)",
"accountid":"675eaf89-f083-e511-80d3-00155d2a68d3"
},
{
"@odata.etag":"W/\"502484\"",
"name":"Adventure Works (sample)",
"accountid":"695eaf89-f083-e511-80d3-00155d2a68d3"
}
],
"@odata.nextLink":"[Organization URI]/api/data/v9.1/accounts?
$select=name&$filter=contains(name,'sample')&$skiptoken=%3Ccookie%20pagenumber=%222%22%20pagingcookie=%22%25
3ccookie%2520page%253d%25221%2522%253e%253caccountid%2520last%253d%2522%257b695EAF89-F083-E511-80D3-
00155D2A68D3%257d%2522%2520first%253d%2522%257b655EAF89-F083-E511-80D3-
00155D2A68D3%257d%2522%2520%252f%253e%253c%252fcookie%253e%22%20istracking=%22False%22%20/%3E"
}
If you don’t want to return any data except for the count, you can apply $count to any collection to get just the
value.
Request
Response
HTTP/1.1 200 OK
Content-Type: text/plain
OData-Version: 4.0
10
<propertyname>@OData.Community.Display.V1.FormattedValue
The following example queries the accounts entity set and returns the first record, including properties that
support formatted values.
Request
Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
Preference-Applied: odata.include-annotations="OData.Community.Display.V1.FormattedValue"
{
"@odata.context":"[Organization
URI]/api/data/v9.1/$metadata#accounts(name,donotpostalmail,accountratingcode,numberofemployees,revenue)",
"value":[
{
"@odata.etag":"W/\"502170\"",
"name":"Fourth Coffee (sample)",
"[email protected]":"Allow",
"donotpostalmail":false,
"[email protected]":"Default Value",
"accountratingcode":1,
"[email protected]":"9,500",
"numberofemployees":9500,
"[email protected]":"$100,000.00",
"revenue":100000,
"accountid":"89390c24-9c72-e511-80d4-00155d2a68d1",
"transactioncurrencyid_value":"50b6dd7b-f16d-e511-80d0-00155db07cb1"
}
]
}
These properties also can include formatted values as described in Include formatted values. Just like formatted
values, you can return the other annotations using the odata.include-annotations preference set to the specific
type of annotation you want, or you can set the value to "*" and return all three. The following sample shows
the request and response to retrieve information about the incident entity _customerid_value lookup property
with annotations included.
Request
Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
Preference-Applied: odata.include-annotations="*"
{
"@odata.context":"[Organization
URI]/api/data/v9.1/$metadata#incidents(title,_customerid_value,customerid_contact(fullname))/$entity",
"@odata.etag":"W/\"504696\"",
"_customerid_value@Microsoft.Dynamics.CRM.associatednavigationproperty":"customerid_contact",
"[email protected]":"contact",
"[email protected]":"Susanna Stubberod (sample)",
"_customerid_value":"7ddd0b31-ed8b-e511-80d2-00155d2a68d4",
"incidentid":"39dd0b31-ed8b-e511-80d2-00155d2a68d4",
"customerid_contact":{
"@odata.etag":"W/\"503587\"",
"fullname":"Susanna Stubberod (sample)",
"contactid":"7ddd0b31-ed8b-e511-80d2-00155d2a68d4"
}
}
The following example retrieves the tasks assigned to the top 5 account records.
Request
Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
{
"@odata.context":"[Organization
URI]/api/data/v9.1/$metadata#accounts(name,Account_Tasks,Account_Tasks(subject,scheduledstart))",
"value":[
{
"@odata.etag":"W/\"513475\"",
"name":"Fourth Coffee (sample)",
"accountid":"36dbf27c-8efb-e511-80d2-00155db07c77",
"Account_Tasks":[
],
"[email protected]":"[Organization URI]/api/data/v9.1/accounts(36dbf27c-8efb-e511-80d2-
00155db07c77)/Account_Tasks?$select=subject,scheduledstart"
},
{
"@odata.etag":"W/\"513477\"",
"name":"Litware, Inc. (sample)",
"accountid":"38dbf27c-8efb-e511-80d2-00155db07c77",
"Account_Tasks":[
],
"[email protected]":"[Organization URI]/api/data/v9.1/accounts(38dbf27c-8efb-e511-80d2-
00155db07c77)/Account_Tasks?$select=subject,scheduledstart"
},
{
"@odata.etag":"W/\"514074\"",
"name":"Adventure Works (sample)",
"accountid":"3adbf27c-8efb-e511-80d2-00155db07c77",
"Account_Tasks":[
],
"[email protected]":"[Organization URI]/api/data/v9.1/accounts(3adbf27c-8efb-e511-80d2-
00155db07c77)/Account_Tasks?$select=subject,scheduledstart"
},
{
"@odata.etag":"W/\"513481\"",
"name":"Fabrikam, Inc. (sample)",
"accountid":"3cdbf27c-8efb-e511-80d2-00155db07c77",
"Account_Tasks":[
],
"[email protected]":"[Organization URI]/api/data/v9.1/accounts(3cdbf27c-8efb-e511-80d2-
00155db07c77)/Account_Tasks?$select=subject,scheduledstart"
},
{
"@odata.etag":"W/\"514057\"",
"name":"Blue Yonder Airlines (sample)",
"accountid":"3edbf27c-8efb-e511-80d2-00155db07c77",
"Account_Tasks":[
],
"[email protected]":"[Organization URI]/api/data/v9.1/accounts(3edbf27c-8efb-e511-80d2-
00155db07c77)/Account_Tasks?$select=subject,scheduledstart"
}
]
}
Retrieve related rows (records) by expanding both single -valued and collection-valued navigation properties
The following example demonstrates how you can expand related rows (records) for entity sets using both
single and collection-valued navigation properties. As explained earlier, expanding on collection-valued
navigation properties to retrieve related entities for entity sets returns an @odata.nextLink property for the
related entities. You should use the value of the @odata.nextLink property with a new GET request to return the
required data.
In this example, we are retrieving the contact and tasks assigned to the top 3 accounts.
Request
Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
{
"@odata.context":"[Organization
URI]/api/data/v9.1/$metadata#accounts(name,primarycontactid,Account_Tasks,primarycontactid(contactid,fullnam
e),Account_Tasks(subject,scheduledstart))",
"value":[
{
"@odata.etag":"W/\"550614\"",
"name":"Fourth Coffee (sample)",
"accountid":"5b9648c3-68f7-e511-80d3-00155db53318",
"primarycontactid":{
"contactid":"c19648c3-68f7-e511-80d3-00155db53318",
"fullname":"Yvonne McKay (sample)"
},
"Account_Tasks":[
],
"[email protected]":"[Organization URI]/api/data/v9.1/accounts(5b9648c3-68f7-e511-80d3-
00155db53318)/Account_Tasks?$select=subject,scheduledstart"
},
{
"@odata.etag":"W/\"550615\"",
"name":"Litware, Inc. (sample)",
"accountid":"5d9648c3-68f7-e511-80d3-00155db53318",
"primarycontactid":{
"contactid":"c39648c3-68f7-e511-80d3-00155db53318",
"fullname":"Susanna Stubberod (sample)"
},
"Account_Tasks":[
],
"[email protected]":"[Organization URI]/api/data/v9.1/accounts(5d9648c3-68f7-e511-80d3-
00155db53318)/Account_Tasks?$select=subject,scheduledstart"
},
{
"@odata.etag":"W/\"550616\"",
"name":"Adventure Works (sample)",
"accountid":"5f9648c3-68f7-e511-80d3-00155db53318",
"primarycontactid":{
"contactid":"c59648c3-68f7-e511-80d3-00155db53318",
"fullname":"Nancy Anderson (sample)"
},
"Account_Tasks":[
],
"[email protected]":"[Organization URI]/api/data/v9.1/accounts(5f9648c3-68f7-e511-80d3-
00155db53318)/Account_Tasks?$select=subject,scheduledstart"
}
]
}
https://<environment-root>/contacts?$select=firstname&$filter=firstname eq lastname
NOTE
Unsure about entity vs. table? See Developers: Understand terminology in Microsoft Dataverse.
Use the $expand system query option in the navigation properties to control what data from related entities is
returned. There are two types of navigation properties:
Single-valued navigation properties correspond to Lookup attributes that support many-to-one
relationships and allow setting a reference to another entity.
Collection-valued navigation properties correspond to one-to-many or many-to-many relationships.
If you include only the name of the navigation property, you’ll receive all the properties for related records. You
can limit the properties returned for related records using the $select system query option in parentheses
after the navigation property name. Use this for both single-valued and collection-valued navigation properties.
NOTE
You are limited to no more than 10 $expand options in a query. This is to protect performance. Each $expand
options creates a join that can impact performance.
To retrieve related entities for an entity instance, see Retrieve related tables for a table by expanding navigation
properties.
Queries which expand collection-valued navigation properties may return cached data for those properties that
doesn’t reflect recent changes. It is recommended to use If-None-Match header with value null to override
browser caching. See HTTP Headers for more details.
Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
{
"@odata.context":"[Organization
URI]/api/data/v9.1/$metadata#accounts(name,primarycontactid,primarycontactid(contactid,fullname))",
"value":[
{
"@odata.etag":"W/\"513475\"",
"name":"Fourth Coffee (sample)",
"accountid":"36dbf27c-8efb-e511-80d2-00155db07c77",
"primarycontactid":{
"contactid":"9cdbf27c-8efb-e511-80d2-00155db07c77",
"fullname":"Yvonne McKay (sample)"
}
},
{
"@odata.etag":"W/\"513477\"",
"name":"Litware, Inc. (sample)",
"accountid":"38dbf27c-8efb-e511-80d2-00155db07c77",
"primarycontactid":{
"contactid":"9edbf27c-8efb-e511-80d2-00155db07c77",
"fullname":"Susanna Stubberod (sample)"
}
}
]
}
Instead of returning the related entities for entity sets, you can also return references (links) to the related
entities by expanding the single-valued navigation property with the $ref option. The following example
returns links to the contact records for all the accounts.
Request
Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
{
"@odata.context":"[Organization URI]/api/data/v9.1/$metadata#accounts(name,primarycontactid)",
"value":[
{
"@odata.etag":"W/\"513475\"",
"name":"Fourth Coffee (sample)",
"_primarycontactid_value":"9cdbf27c-8efb-e511-80d2-00155db07c77",
"accountid":"36dbf27c-8efb-e511-80d2-00155db07c77",
"primarycontactid":{
"@odata.id":"[Organization URI]/api/data/v9.1/contacts(9cdbf27c-8efb-e511-80d2-00155db07c77)"
}
},
{
"@odata.etag":"W/\"513477\"",
"name":"Litware, Inc. (sample)",
"_primarycontactid_value":"9edbf27c-8efb-e511-80d2-00155db07c77",
"accountid":"38dbf27c-8efb-e511-80d2-00155db07c77",
"primarycontactid":{
"@odata.id":"[Organization URI]/api/data/v9.1/contacts(9edbf27c-8efb-e511-80d2-00155db07c77)"
}
}
]
}
NOTE
There is no limit on the depth of nested $expand options, but the combined limit of 10 total $expand options in a
query still applies.
The following query returns task records and expands the related contact , the account related to the
contact , and finally to the systemuser who created the account record.
Request
Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
{
"@odata.context": "[Organization
URI]/api/data/v9.1/$metadata#tasks(subject,regardingobjectid_contact_task(fullname,parentcustomerid_account(
name,createdby(fullname))))",
"value":
[
{
"@odata.etag": "W/\"28876997\"",
"subject": "Task 1 for Susanna Stubberod",
"activityid": "834814f9-b0b8-ea11-a812-000d3a122b89",
"regardingobjectid_contact_task": {
"fullname": "Susanna Stubberod (sample)",
"contactid": "824814f9-b0b8-ea11-a812-000d3a122b89",
"parentcustomerid_account": {
"name": "Contoso, Ltd. (sample)",
"accountid": "7a4814f9-b0b8-ea11-a812-000d3a122b89",
"createdby": {
"fullname": "Nancy Anderson",
"systemuserid": "4026be43-6b69-e111-8f65-78e7d1620f5e",
"ownerid": "4026be43-6b69-e111-8f65-78e7d1620f5e"
}
}
}
},
{
"@odata.etag": "W/\"28877001\"",
"subject": "Task 2 for Susanna Stubberod",
"activityid": "844814f9-b0b8-ea11-a812-000d3a122b89",
"regardingobjectid_contact_task": {
"fullname": "Susanna Stubberod (sample)",
"contactid": "824814f9-b0b8-ea11-a812-000d3a122b89",
"parentcustomerid_account": {
"name": "Contoso, Ltd. (sample)",
"accountid": "7a4814f9-b0b8-ea11-a812-000d3a122b89",
"createdby": {
"fullname": "Nancy Anderson",
"systemuserid": "4026be43-6b69-e111-8f65-78e7d1620f5e",
"ownerid": "4026be43-6b69-e111-8f65-78e7d1620f5e"
}
}
}
}
]
}
Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
{
"@odata.context": "[Organization
URI]/api/data/v9.1/$metadata#accounts(name,Account_Tasks(subject,scheduledstart))",
"value": [
{
"@odata.etag": "W/\"37867294\"",
"name": "Contoso, Ltd. (sample)",
"accountid": "7a4814f9-b0b8-ea11-a812-000d3a122b89",
"Account_Tasks": [
{
"@odata.etag": "W/\"28876919\"",
"subject": "Task 1 for Contoso, Ltd.",
"scheduledstart": null,
"_regardingobjectid_value": "7a4814f9-b0b8-ea11-a812-000d3a122b89",
"activityid": "7b4814f9-b0b8-ea11-a812-000d3a122b89"
},
{
"@odata.etag": "W/\"28876923\"",
"subject": "Task 2 for Contoso, Ltd.",
"scheduledstart": null,
"_regardingobjectid_value": "7a4814f9-b0b8-ea11-a812-000d3a122b89",
"activityid": "7c4814f9-b0b8-ea11-a812-000d3a122b89"
},
{
"@odata.etag": "W/\"28876927\"",
"subject": "Task 3 for Contoso, Ltd.",
"scheduledstart": null,
"_regardingobjectid_value": "7a4814f9-b0b8-ea11-a812-000d3a122b89",
"activityid": "7d4814f9-b0b8-ea11-a812-000d3a122b89"
}
],
"[email protected]": "[Organization URI]/api/data/v9.1/accounts(7a4814f9-b0b8-ea11-
a812-000d3a122b89)/Account_Tasks?$select=subject,scheduledstart"
},
{
"@odata.etag": "W/\"37526208\"",
"name": "Fourth Coffee",
"accountid": "ccd685f9-cddd-ea11-a813-000d3a122b89",
"Account_Tasks": [],
"[email protected]": "[Organization URI]/api/data/v9.1/accounts(ccd685f9-cddd-ea11-
a813-000d3a122b89)/Account_Tasks?$select=subject,scheduledstart"
}
]
}
Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
{
"@odata.context": "[Organization
URI]/api/data/v9.1/$metadata#accounts(name,primarycontactid(contactid,fullname),Account_Tasks(subject,schedu
ledstart))",
"value": [
{
"@odata.etag": "W/\"37867294\"",
"name": "Contoso, Ltd. (sample)",
"accountid": "7a4814f9-b0b8-ea11-a812-000d3a122b89",
"primarycontactid": {
"contactid": "7e4814f9-b0b8-ea11-a812-000d3a122b89",
"fullname": "Yvonne McKay (sample)"
},
"Account_Tasks": [
{
"@odata.etag": "W/\"28876919\"",
"subject": "Task 1 for Contoso, Ltd.",
"scheduledstart": null,
"_regardingobjectid_value": "7a4814f9-b0b8-ea11-a812-000d3a122b89",
"activityid": "7b4814f9-b0b8-ea11-a812-000d3a122b89"
},
{
"@odata.etag": "W/\"28876923\"",
"subject": "Task 2 for Contoso, Ltd.",
"scheduledstart": null,
"_regardingobjectid_value": "7a4814f9-b0b8-ea11-a812-000d3a122b89",
"activityid": "7c4814f9-b0b8-ea11-a812-000d3a122b89"
},
{
"@odata.etag": "W/\"28876927\"",
"subject": "Task 3 for Contoso, Ltd.",
"scheduledstart": null,
"_regardingobjectid_value": "7a4814f9-b0b8-ea11-a812-000d3a122b89",
"activityid": "7d4814f9-b0b8-ea11-a812-000d3a122b89"
}
],
"[email protected]": "[Organization URI]/api/data/v9.1/accounts(7a4814f9-b0b8-ea11-
a812-000d3a122b89)/Account_Tasks?$select=subject,scheduledstart"
},
{
"@odata.etag": "W/\"37526208\"",
"name": "Fourth Coffee",
"accountid": "ccd685f9-cddd-ea11-a813-000d3a122b89",
"primarycontactid": {
"contactid": "384d0f84-7de6-ea11-a817-000d3a122b89",
"fullname": "Charlie Brown"
},
"Account_Tasks": [],
"[email protected]": "[Organization URI]/api/data/v9.1/accounts(ccd685f9-cddd-ea11-
a813-000d3a122b89)/Account_Tasks?$select=subject,scheduledstart"
}
]
}
See also
Search across table data using Dataverse search
Query data using Web API
Perform operations using the Web API
Compose Http requests and handle errors
Create a table using the Web API
Retrieve a table using the Web API
Update and delete tables using the Web API
Associate and disassociate tables using the Web API
Retrieve and execute predefined queries
5/21/2021 • 5 minutes to read • Edit Online
Microsoft Dataverse provides a way for administrators to create system views that are available to all users.
Individual users can save the Advanced Find queries for re-use in the application. Both of these represent
predefined queries you can retrieve and execute using the Web API. You can also compose a query using
FetchXml and use that to retrieve data.
NOTE
Unlike queries using the OData syntax, data returned from pre-defined queries or fetchXml will not return properties with
null values. When the value is null , the property will not be included in the results.
When a query is returned using OData syntax, a record will include a property with a null value like so:
{
"@odata.etag": "W/\"46849433\"",
"name": "Contoso, Ltd. (sample)",
"accountnumber": null,
"accountid": "7a4814f9-b0b8-ea11-a812-000d3a122b89"
}
When retrieved using a pre-defined query or with FetchXml, the same record will not include the accountnumber
property because it is null , like so:
{
"@odata.etag": "W/\"46849433\"",
"name": "Contoso, Ltd. (sample)",
"accountid": "7a4814f9-b0b8-ea11-a812-000d3a122b89"
}
Predefined queries
Dataverse allows you to define, save, and execute two types of queries as listed here.
Saved Quer y System-defined views for a table (entity). These views are
stored in the savedquery EntityType /. More information:
Customize table views
User Quer y Advanced Find searches saved by users for a table (entity).
These views are stored in the userquery EntityType /. More
information: UserQuery (saved view) table
Records for both of these types of entities contain the FetchXML definition for the data to return. You can query
the respective entity type to retrieve the primary key value. With the primary key value, you can execute the
query by passing the primary key value. For example, to execute the Active Accounts saved query, you must
first get the primary key using a query like this.
GET [Organization URI]/api/data/v9.0/savedqueries?$select=name,savedqueryid&$filter=name eq 'Active
Accounts'
You can then use the savedqueryid value and pass it as the value to the savedQuery parameter to the accounts
entity set.
Use the same approach to get the userqueryid and pass it as the value to the userQuery parameter to the entity
set that matches the corresponding returnedtypecode of the saved query.
<fetch mapping='logical'>
<entity name='account'>
<attribute name='accountid'/>
<attribute name='name'/>
<attribute name='accountnumber'/>
</entity>
</fetch>
%3Cfetch%20mapping%3D%27logical%27%3E%3Centity%20name%3D%27account%27%3E%3Cattribute%20name%3D%27accountid%2
7%2F%3E%3Cattribute%20name%3D%27name%27%2F%3E%3Cattribute%20name%3D%27accountnumber%27%2F%3E%3C%2Fentity%3E%
3C%2Ffetch%3E
Most programming languages include a function to URL encode a string. For example, in JavaScript you use the
encodeURI function. You should URL encode any request that you send to any RESTful web service. If you paste
a URL into the address bar of your browser it should URL encode the address automatically. The following
example shows a GET request using the FetchXML shown previously using the entity set path for accounts.
Request
Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
{
"@odata.context":"[Organization URI]/api/data/v9.0/$metadata#accounts(accountid,name)","value":[
{
"@odata.etag":"W/\"506678\"","accountid":"89390c24-9c72-e511-80d4-00155d2a68d1","name":"Fourth Coffee
(sample)", "accountnumber":"1234",
},{
"@odata.etag":"W/\"502172\"","accountid":"8b390c24-9c72-e511-80d4-00155d2a68d1","name":"Litware, Inc.
(sample)"
},{
"@odata.etag":"W/\"502174\"","accountid":"8d390c24-9c72-e511-80d4-00155d2a68d1","name":"Adventure
Works (sample)"
},{
"@odata.etag":"W/\"506705\"","accountid":"8f390c24-9c72-e511-80d4-00155d2a68d1","name":"Fabrikam, Inc.
(sample)"
},{
"@odata.etag":"W/\"506701\"","accountid":"91390c24-9c72-e511-80d4-00155d2a68d1","name":"Blue Yonder
Airlines (sample)"
},{
"@odata.etag":"W/\"502180\"","accountid":"93390c24-9c72-e511-80d4-00155d2a68d1","name":"City Power &
Light (sample)"
},{
"@odata.etag":"W/\"502182\"","accountid":"95390c24-9c72-e511-80d4-00155d2a68d1","name":"Contoso
Pharmaceuticals (sample)"
},{
"@odata.etag":"W/\"506704\"","accountid":"97390c24-9c72-e511-80d4-00155d2a68d1","name":"Alpine Ski
House (sample)"
},{
"@odata.etag":"W/\"502186\"","accountid":"99390c24-9c72-e511-80d4-00155d2a68d1","name":"A. Datum
Corporation (sample)"
},{
"@odata.etag":"W/\"502188\"","accountid":"9b390c24-9c72-e511-80d4-00155d2a68d1","name":"Coho Winery
(sample)"
},{
"@odata.etag":"W/\"504177\"","accountid":"0a3238d4-f973-e511-80d4-00155d2a68d1","name":"Litware, Inc."
}
]
}
NOTE
Properties with null values will not be included in results returned using FetchXml. In the example above, only the first
record returned has an accountnumber value.
A paging cookie must be requested as an annotation. Set the odata.include-annotations preference to use (or
include) Microsoft.Dynamics.CRM.fetchxmlpagingcookie and a @Microsoft.Dynamics.CRM.fetchxmlpagingcookie
property will be returned with the result.
NOTE
Sending a GET request within a Batch allows for URLs up to 32768 characters in length. Much more than with a normal
GET request, but it isn't unlimited.
Example
Request
Content-Type:multipart/mixed;boundary=batch_AAA123
Accept:application/json
OData-MaxVersion:4.0
OData-Version:4.0
--batch_AAA123
Content-Type: application/http
Content-Transfer-Encoding: binary
--batch_AAA123--
Response
--batchresponse_cbfd44cd-a322-484e-913b-49e18af44e34
Content-Type: application/http
Content-Transfer-Encoding: binary
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
{
"@odata.context":"[Organization
URI]/api/data/v9.0/$metadata#accounts(accountid,name,telephone1,creditonhold)",
"value":[
{
"@odata.etag":"W/\"563737\"",
"accountid":"1f55c679-485e-e811-8151-000d3aa3c22a",
"name":"Fourth Coffee (sample)",
"telephone1":"+1-425-555-0121",
"creditonhold":false
},
{
"@odata.etag":"W/\"563739\"",
"accountid":"2555c679-485e-e811-8151-000d3aa3c22a",
"name":"Litware, Inc. (sample)",
"telephone1":"+1-425-555-0120",
"creditonhold":false
}
]
}
--batchresponse_cbfd44cd-a322-484e-913b-49e18af44e34--
See also
Web API Query Data Sample (C#)
Web API Query Data Sample (Client-side JavaScript)
Perform operations using the Web API
Compose Http requests and handle errors
Query Data using the Web API
Create a table using the Web API
Retrieve a table using the Web API
Update and delete tables using the Web API
Associate and disassociate tables using the Web API
Use Web API functions
Use Web API actions
Execute batch operations using the Web API
Impersonate another user using the Web API
Perform conditional operations using the Web API
Create a table row using the Web API
7/19/2021 • 6 minutes to read • Edit Online
NOTE
Unsure about entity vs. table? See Developers: Understand terminology in Microsoft Dataverse.
Use a POST request to send data to create a table row (entity record). You can create multiple related table rows
in a single operation using deep insert. You also need to know how to set values to associate a new table row to
existing tables using the @odata.bind annotation.
NOTE
For information about how to create and update the table (entity) definitions using the Web API, see Create and update
table definitions using the Web API.
Basic Create
This example creates a new account entity record. The response OData-EntityId header contains the Uri of the
created entity.
Request
{
"name": "Sample Account",
"creditonhold": false,
"address1_latitude": 47.639583,
"description": "This is the description of the sample account",
"revenue": 5000000,
"accountcategorycode": 1
}
Response
To create a new entity record you must identify the valid property names and types. For all system entities and
attributes (table columns), you can find this information in the topic for that entity in the About the Table
Reference. For custom entities or attributes, refer to the definition of that entity in the CSDL $metadata
document. More information: Entity types
Create with data returned
You can compose your POST request so that data from the created record will be returned with a status of
201 (Created) . To get this result, you must use the return=representation preference in the request headers.
To control which properties are returned, append the $select query option to the URL to the entity set. You
may also use $expand to return related entities.
When an entity is created in this way the OData-EntityId header containing the URI to the created record is not
returned.
This example creates a new account entity and returns the requested data in the response.
Request
{
"name": "Sample Account",
"creditonhold": false,
"address1_latitude": 47.639583,
"description": "This is the description of the sample account",
"revenue": 5000000,
"accountcategorycode": 1
}
Response
{
"@odata.context": "[Organization URI]/api/data/v9.0/$metadata#accounts/$entity",
"@odata.etag": "W/\"536530\"",
"accountid": "d6f193fc-ce85-e611-80d8-00155d2a68de",
"accountcategorycode": 1,
"description": "This is the description of the sample account",
"address1_latitude": 47.63958,
"creditonhold": false,
"name": "Sample Account",
"createdon": "2016-09-28T22:57:53Z",
"revenue": 5000000.0000,
"_transactioncurrencyid_value": "048dddaa-6f7f-e611-80d3-00155db5e0b6"
}
An opportunity is created because it is defined as an object within an array that is set to the value of a
collection-valued navigation property opportunity_customer_accounts .
A task is created because it is defined an object within an array that is set to the value of a collection-
valued navigation property Opportunity_Tasks .
NOTE
When creating a new table row, it is not possible to combine the row creation with the insert of a non-primary image. For
a non-primary image to be added, the row must already exist.
Request
{
"name": "Sample Account",
"primarycontactid":
{
"firstname": "John",
"lastname": "Smith"
},
"opportunity_customer_accounts":
[
{
"name": "Opportunity associated to Sample Account",
"Opportunity_Tasks":
[
{ "subject": "Task associated to opportunity" }
]
}
]
}
Response
NOTE
This request is using the Prefer: return=representation header so it returns the values of the created record. More
information: Create with data returned
Request
{
"name": "Sample Account",
"[email protected]": "/contacts(00000000-0000-0000-0000-000000000001)",
"[email protected]": [
"/tasks(00000000-0000-0000-0000-000000000002)",
"/tasks(00000000-0000-0000-0000-000000000003)"
]
}
Response
HTTP/1.1 201 Created
OData-Version: 4.0
Preference-Applied: return=representation
{
"@odata.context": "[Organization
URI]/api/data/v9.1/$metadata#accounts(name,primarycontactid(fullname),Account_Tasks(subject))/$entity",
"@odata.etag": "W/\"36236432\"",
"name": "Sample Account",
"accountid": "00000000-0000-0000-0000-000000000004",
"primarycontactid": {
"@odata.etag": "W/\"28877094\"",
"fullname": "Yvonne McKay (sample)",
"contactid": "00000000-0000-0000-0000-000000000001"
},
"Account_Tasks": [
{
"@odata.etag": "W/\"36236437\"",
"subject": "Task 1",
"activityid": "00000000-0000-0000-0000-000000000002"
},
{
"@odata.etag": "W/\"36236440\"",
"subject": "Task 2",
"activityid": "00000000-0000-0000-0000-000000000003"
}
]
}
Response
{
"@odata.context": "[Organization URI]/api/data/v9.0/$metadata#accounts/$entity",
"@odata.type": "#Microsoft.Dynamics.CRM.account",
"[email protected]": "accounts(c65127ed-2097-e711-80eb-00155db75426)",
"[email protected]": "transactioncurrencies(732e87e1-1d96-e711-80e4-00155db75426)",
"address1_line1": "123 Maple St.",
"address1_city": "Seattle",
"address1_country": "United States of America"
}
The response received from InitializeFrom request consists of values of mapped attributes between the source
entity and target entity and the GUID of the parent record. The attribute mapping between entities that have an
entity relationship is different for different entity sets and is customizable, so the response from InitializeFrom
function request may vary for different entities and organizations. When this response is passed in the body of
create request of the new record, these attribute values are replicated in the new record. The values of custom
mapped attributes also get set in the new record during the process.
NOTE
To determine whether two entities can be mapped, use this query:
GET [Organization URI]/api/data/v9.1/entitymaps?
$select=sourceentityname,targetentityname&$orderby=sourceentityname
Other attribute values can also be set and/or modified for the new record by adding them in the JSON request
body, as shown in the example below.
{
"@odata.context": "[Organization URI]/api/data/v9.0/$metadata#accounts/$entity",
"@odata.type": "#Microsoft.Dynamics.CRM.account",
"[email protected]": "accounts(c65127ed-2097-e711-80eb-00155db75426)",
"[email protected]": "transactioncurrencies(732e87e1-1d96-e711-80e4-00155db75426)",
"name":"Contoso Ltd",
"numberofemployees":"200",
"address1_line1":"100 Maple St.",
"address1_city":"Seattle",
"address1_country":"United States of America",
"fax":"73737"
}
}
Create documents in storage partitions
If you are creating large numbers of entities that contain documents, you can create the entities in storage
partitions to speed up access to those entity records.
More information: Access documents faster using storage partitions
See also
Web API Basic Operations Sample (C#)
Web API Basic Operations Sample (Client-side JavaScript)
InitializeFrom Function
Perform operations using the Web API
Compose Http requests and handle errors
Query Data using the Web API
Retrieve a table using the Web API
Update and delete tables using the Web API
Associate and disassociate tables using the Web API
Use Web API functions
Use Web API actions
Execute batch operations using the Web API
Impersonate another user using the Web API
Perform conditional operations using the Web API
Retrieve a table row using the Web API
5/21/2021 • 9 minutes to read • Edit Online
NOTE
Unsure about entity vs. table? See Developers: Understand terminology in Microsoft Dataverse.
Use a GET request to retrieve data for a table specified as the resource with a unique identifier. When retrieving
a table row (entity record) you can also request specific properties and expand navigation properties to return
properties from related tables.
NOTE
For information about retrieving table definitions, see Query table definitions using the Web API.
To retrieve more than one entity record at a time, see Basic query example in the Query Data using the Web API
topic.
Cau t i on
The above example will return all the properties for account record, which is not a performance best practice for
retrieving data. This example was just to illustrate how you can do a basic retrieve of an entity record in
Microsoft Dataverse. Because all the properties were returned, we haven't included the response information for
the request in this example.
As a performance best practice, you must always use the $select system query option to limit the properties
returned while retrieving data. See the following section, Retrieve specific proper ties , for information about
this.
Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
{
"@odata.context": "[Organization URI]/api/data/v9.0/$metadata#accounts(name,revenue)/$entity",
"@odata.etag": "W/\"502186\"",
"name": "A. Datum Corporation (sample)",
"revenue": 10000,
"accountid": "00000000-0000-0000-0000-000000000001",
"_transactioncurrencyid_value":"b2a6b689-9a39-e611-80d2-00155db44581"
}
When you request certain types of properties you can expect additional read-only properties to be returned
automatically.
If you request a money value, the _transactioncurrencyid_value lookup property will be returned. This property
contains only the GUID value of the transaction currency so you could use this value to retrieve information
about the currency using the transactioncurrency EntityType /. Alternatively, by requesting annotations you can
also get additional data in the same request. More information:Retrieve data about lookup properties
If you request a property that is part of a composite attribute for an address, you will get the composite
property as well. For example, if your query requests the address1_line1 property for a contact, the
address1_composite property will be returned as well.
If the alternate key definition contains lookup type field (for example, the primarycontactid property for the
account entity), you can retrieve the account using the lookup property as shown here.
Any time you need to uniquely identify an entity to retrieve, update, or delete, you can use alternate keys
configured for the entity. By default, there are no alternate keys configured for entities. Alternate keys will only
be available if the organization or a solution adds them.
Retrieve documents in storage partitions
If you are retrieving entity data stored in partitions be sure to specify the partition key when retrieving that data.
More information: Access table data faster using storage partitions
Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
{
"@odata.context":"[Organization URI]/api/data/v9.0/$metadata#accounts(00000000-0000-0000-0000-
000000000001)/name",
"value":"Adventure Works (sample)"
}
Request
Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
{
"@odata.context": "[Organization URI]/api/data/v9.0/$metadata#contacts(fullname)/$entity",
"@odata.etag": "W/\"500128\"",
"fullname": "Rene Valdes (sample)",
"contactid": "ff390c24-9c72-e511-80d4-00155d2a68d1"
}
For collection-valued navigation properties you have the option to request to return only references to the
related entities or just a count of the related entities.
The following example will just return references to tasks related to a specific account by adding /$ref to the
request.
Request
Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
{
"@odata.context": "[Organization URI]/api/data/v9.0/$metadata#Collection($ref)",
"value":
[
{ "@odata.id": "[Organization URI]/api/data/v9.0/tasks(6b5941dd-d175-e511-80d4-00155d2a68d1)" },
{ "@odata.id": "[Organization URI]/api/data/v9.0/tasks(fcbb60ed-d175-e511-80d4-00155d2a68d1)" }
]
}
The following example returns the number of tasks related to a specific account using the Account_Tasks
collection-valued navigation property with /$count appended.
Request
Response
2
NOTE
The value returned includes the UTF-8 byte order mark (BOM) characters (  ) that represent that this is a UTF-8
document.
NOTE
To retrieve related entities for entity sets, see Retrieve related tables by expanding navigation properties.
Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
{
"@odata.context":"[Organization
URI]/api/data/v9.0/$metadata#accounts(name,primarycontactid,primarycontactid(contactid,fullname))/$en
tity",
"@odata.etag":"W/\"550616\"",
"name":"Adventure Works (sample)",
"accountid":"00000000-0000-0000-0000-000000000001",
"primarycontactid":
{
"@odata.etag":"W/\"550626\"",
"contactid":"c59648c3-68f7-e511-80d3-00155db53318",
"fullname":"Nancy Anderson (sample)"
}
}
Instead of returning the related entities for entity records, you can also return references (links) to the
related entities by expanding the single-valued navigation property with the $ref option. The following
example returns links to the contact record for the account entity.
Request
Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
{
"@odata.context":"[Organization
URI]/api/data/v9.0/$metadata#accounts(name,primarycontactid)/$entity",
"@odata.etag":"W/\"550616\"",
"name":"Adventure Works (sample)",
"accountid":"00000000-0000-0000-0000-000000000001",
"_primarycontactid_value":"c59648c3-68f7-e511-80d3-00155db53318",
"primarycontactid": { "@odata.id":"[Organization URI]/api/data/v9.0/contacts(c59648c3-68f7-e511-
80d3-00155db53318)" }
}
Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
{
"@odata.context": "[Organization
URI]/api/data/v9.0/$metadata#accounts(name,Account_Tasks,Account_Tasks(subject,scheduledstart))/$enti
ty",
"@odata.etag": "W/\"514069\"",
"name": "Sample Child Account 1",
"accountid": "915e89f5-29fc-e511-80d2-00155db07c77",
"Account_Tasks":
[
{
"@odata.etag": "W/\"514085\"",
"subject": "Sample Task 1",
"scheduledstart": "2016-04-11T15:00:00Z",
"activityid": "a983a612-3ffc-e511-80d2-00155db07c77"
},
{
"@odata.etag": "W/\"514082\"",
"subject": "Sample Task 2",
"scheduledstart": "2016-04-13T15:00:00Z",
"activityid": "7bcc572f-3ffc-e511-80d2-00155db07c77"
}
]
}
NOTE
If you expand on collection-valued navigation parameters to retrieve related entities for entity sets, a @odata.nextLink
property will be returned instead for the related entities. You should use the value of the @odata.nextLink property with a
new GET request to return the required data. More information:Retrieve related tables by expanding navigation properties
Retrieve related entities for an entity instance by expanding both single-valued and
collection-valued navigation proper ties : The following example demonstrates how you can expand
related entities for an entity instance using both single- and collection-values navigation properties.
Request
Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
{
"@odata.context": "[Organization
URI]/api/data/v9.0/$metadata#accounts(accountid,parentaccountid,Account_Tasks,parentaccountid(created
on,name),Account_Tasks(subject,scheduledstart))/$entity",
"@odata.etag": "W/\"514069\"",
"accountid": "915e89f5-29fc-e511-80d2-00155db07c77",
"parentaccountid":
{
"@odata.etag": "W/\"514074\"",
"createdon": "2016-04-06T00:29:04Z",
"name": "Adventure Works (sample)",
"accountid": "3adbf27c-8efb-e511-80d2-00155db07c77"
},
"Account_Tasks":
[
{
"@odata.etag": "W/\"514085\"",
"subject": "Sample Task 1",
"scheduledstart": "2016-04-11T15:00:00Z",
"activityid": "a983a612-3ffc-e511-80d2-00155db07c77"
},
{
"@odata.etag": "W/\"514082\"",
"subject": "Sample Task 2",
"scheduledstart": "2016-04-13T15:00:00Z",
"activityid": "7bcc572f-3ffc-e511-80d2-00155db07c77"
}
]
}
NOTE
You can’t use the /$ref or /$count path segments to return only the URI for the related entity or a count of the
number of related entities.
?$expand=Account_Tasks($filter=endswith(subject,'1');$select=subject)
The following example specifies that related tasks should be returned in ascending order based on the
createdon property.
?$expand=Account_Tasks($orderby=createdon asc;$select=subject,createdon)
The following example applies nested $expand options to return details about the systemuser who last
modified the account and the name of the businessunit that user belongs to.
?$select=name&$expand=modifiedby($select=fullname;$expand=businessunitid($select=name))
NOTE
Nested $expand options can only be applied to single-valued navigation properties.
Each request can include a maximum of 10 $expand options. There is no limit on the depth of nested $expand
options, but the limit of 10 total $expand options applies to these as well.
This is a subset of the system query options described in the “11.2.4.2.1 Expand Options” section of OData
Version 4.0 Part 1: Protocol Plus Errata 02. The options $skip , $count , $search , and $levels aren’t
supported for the Web API.
More information about nested $expand option use: Multi-level expand of single-valued navigation properties
NOTE
Unsure about entity vs. table? See Developers: Understand terminology in Microsoft Dataverse.
Operations to modify data are a core part of the Web API. In addition to a simple update and delete, you can
perform operations on single table columns (entity attributes) and compose upsert requests that will either
update or insert data depending on whether it exists.
Basic update
Update operations use the HTTP PATCH verb. Pass a JSON object containing the properties you want to update
to the URI that represents the entity. A response with a status of 204 will be returned if the update is successful.
The If-Match: * header helps ensure you don't create a new record by accidentally performing an upsert
operation. More information: Prevent create in upsert.
IMPORTANT
When updating an entity, only include the properties you are changing in the request body. Simply updating the
properties of an entity that you previously retrieved, and including that JSON in your request, will update each property
even though the value is the same. This can cause system events that can trigger business logic that expects that the
values have changed. This can cause properties to appear to have been updated in auditing data when in fact they
haven’t actually changed.
NOTE
The definition for attributes includes a RequiredLevel property. When this is set to SystemRequired , you cannot set
these attributes to a null value. More information: Attribute requirement level
This example updates an existing account record with the accountid value of 00000000-0000-0000-0000-
000000000001.
Request
PATCH [Organization URI]/api/data/v9.0/accounts(00000000-0000-0000-0000-000000000001) HTTP/1.1
Content-Type: application/json
OData-MaxVersion: 4.0
OData-Version: 4.0
If-Match: *
{
"name": "Updated Sample Account ",
"creditonhold": true,
"address1_latitude": 47.639583,
"description": "This is the updated description of the sample account",
"revenue": 6000000,
"accountcategorycode": 2
}
Response
NOTE
See Associate and disassociate tables on update for information about associating and disassociating entities on update.
To control which properties are returned, append the $select query option to the URL to the entity set. The
$expand query option will be ignored if used.
This example updates an account entity and returns the requested data in the response.
Request
Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
Preference-Applied: return=representation
OData-Version: 4.0
{
"@odata.context": "[Organization URI]/api/data/v9.0/$metadata#accounts/$entity",
"@odata.etag": "W/\"536537\"",
"accountid": "00000000-0000-0000-0000-000000000001",
"accountcategorycode": 1,
"description": "This is the description of the sample account",
"address1_latitude": 47.63958,
"creditonhold": false,
"name": "Updated Sample Account",
"createdon": "2016-09-28T23:14:00Z",
"revenue": 5000000.0000,
"_transactioncurrencyid_value": "048dddaa-6f7f-e611-80d3-00155db5e0b6"
}
Response
Response
NOTE
This can’t be used with a single-valued navigation property to disassociate two entities. For an alternative approach, see
Remove a reference to a table.
Upsert a table
An upsert operation is exactly like an update. It uses a PATCH request and uses a URI to reference a specific
entity. The difference is that if the entity doesn’t exist it will be created. If it already exists, it will be updated.
Normally when creating a new entity you will let the system assign a unique identifier. This is a best practice. But
if you need to create a record with a specific id value, an upsert operation provides a way to do this. This can
be valuable in situation where you are synchronizing data in different systems.
Sometimes there are situations where you want to perform an upsert , but you want to prevent one of the
potential default actions: either create or update. You can accomplish this through the addition of If-Match or
If-None-Match headers. For more information, see Limit upsert operations.
Basic delete
A delete operation is very straightforward. Use the DELETE verb with the URI of the entity you want to delete.
This example message deletes an account entity with the primary key accountid value equal to 00000000-
0000-0000-0000-000000000001.
Request
Response
If the entity exists, you’ll get a normal response with status 204 to indicate the delete was successful. If the entity
isn’t found, you’ll get a response with status 404.
NOTE
Unsure about entity vs. table? See Developers: Understand terminology in Microsoft Dataverse.
There are several methods you can use to associate and disassociate tables (entities). Which method you apply
depends on whether you’re creating or updating the tables and whether you’re operating in the context of the
referenced table or the referencing table.
{
"@odata.id":"[Organization URI]/api/data/v9.0/opportunities(00000000-0000-0000-0000-000000000001)"
}
Response
{
"@odata.id":"accounts(00000000-0000-0000-0000-000000000002)"
}
Response
Request
For a single-valued navigation property, remove the $id query string parameter.
Response
Either way, a successful response has status 204.
{
"[email protected]":"accounts(00000000-0000-0000-0000-000000000002)"
}
Response
{
"[email protected]": null,
"[email protected]": null
}
{
"parentaccountid": null,
"primarycontactid": null
}
NOTE
Associating multiple tables with a table on update is a special scenario that is possible only with activityparty EntityType /.
Request
{
"value": [
{
"[email protected]":"contacts(a30d4045-fc46-e711-8115-e0071b66df51)",
"participationtypemask":3
},
{
"[email protected]":"contacts(1dcdda07-3a39-e711-8145-e0071b6a2001)",
"participationtypemask":2
}
]
}
Response
See also
Web API Basic Operations Sample (C#)
Web API Basic Operations Sample (Client-side JavaScript)
Perform operations using the Web API
Compose Http requests and handle errors
Query Data using the Web API
Create a table using the Web API
Retrieve a table using the Web API
Update and delete tables using the Web API
Use Web API functions
Use Web API actions
Execute batch operations using the Web API
Impersonate another user using the Web API
Perform conditional operations using the Web API
Merge table rows using the Web API
5/21/2021 • 2 minutes to read • Edit Online
NOTE
Unsure about entity vs. table? See Developers: Understand terminology in Microsoft Dataverse.
When you find duplicate records you can combine them into one using the Merge Action.
NOTE
Only the following entity types can be merged:
account
contact
incident
lead
Merge action
Merge is an unbound action that accepts four parameters:
{
"Target": {
"name": "Account 1",
"accountid": "bb8055c0-aea6-ea11-a812-000d3a55d474",
"@odata.type": "Microsoft.Dynamics.CRM.account"
},
"Subordinate": {
"name": "Account 2",
"accountid": "c38055c0-aea6-ea11-a812-000d3a55d474",
"@odata.type": "Microsoft.Dynamics.CRM.account"
},
"UpdateContent": {
"accountnumber": "1234",
"@odata.type": "Microsoft.Dynamics.CRM.account"
},
"PerformParentingChecks": false
}
IMPORTANT
Because the Target , Subordinate , and UpdateContent property types are not explicitly defined by the parameter,
you must include the @odata.type annotation to specify the type.
Response
See also
Merge duplicate records
MergeRequest
Use Web API functions
5/21/2021 • 6 minutes to read • Edit Online
NOTE
Unsure about entity vs. table? See Developers: Understand terminology in Microsoft Dataverse.
Functions and actions represent re-usable operations you can perform using the Web API. There are two types
of functions in the Web API:
Functions
Use a GET request with functions listed in Web API Function Reference to perform operations that have no side-
effects. These functions generally retrieve data. They return either a collection or a complex type. Each of these
functions has a corresponding message in the organization service.
Quer y Functions
Use the functions listed in Web API Query Function Reference to evaluate properties and values in the
composition of a query. Each of these functions has a corresponding ConditionOperator value.
However, there’s an open issue using DateTimeOffset values with the inline syntax, as explained in the following
article: DateTimeOffset as query parameter #204.
Therefore, the best practice is to pass the values in as parameters as shown in the following code sample. If you
use this best practice, you can avoid the open issue that applies to DateTimeOffset .
Parameter aliases also allow you to re-use parameter values to reduce the total length of the URL when the
parameter value is used multiple times.
When you pass a reference to an existing entity, use the @odata.id annotation to the Uri for the entity. For
example if you are using the RetrievePrincipalAccess Function / you can use the following Uri to specify
retrieving access to a specific contact:
The @odata.id annotation can be the full Uri, but a relative Uri works too.
Bound functions
In the CSDL metadata document, when a Function element represents a bound function, it has an IsBound
attribute with the value true . The first Parameter element defined in the function represents the entity that the
function is bound to. When the Type attribute of the parameter is a collection, the function is bound to an entity
collection. As an example, the following is the definition of the CalculateTotalTimeIncident Function / and
CalculateTotalTimeIncidentResponse ComplexType / in the CSDL.
<ComplexType Name="CalculateTotalTimeIncidentResponse">
<Property Name="TotalTime" Type="Edm.Int64" Nullable="false" />
</ComplexType>
<Function Name="CalculateTotalTimeIncident" IsBound="true">
<Parameter Name="entity" Type="mscrm.incident" Nullable="false" />
<ReturnType Type="mscrm.CalculateTotalTimeIncidentResponse" Nullable="false" />
</Function>
This bound function is equivalent to the CalculateTotalTimeIncidentRequest used by the Organization service. In
the Web API this function is bound to the incident EntityType / that represents the
CalculateTotalTimeIncidentRequest.IncidentId property. Instead of returning a
CalculateTotalTimeIncidentResponse, this function returns a CalculateTotalTimeIncidentResponse ComplexType /.
When a function returns a complex type, its definition appears directly above the definition of the function in the
CSDL.
To invoke a bound function, append the full name of the function to the URL and include any named parameters
within the parentheses following the function name. The full function name includes the namespace
Microsoft.Dynamics.CRM . Functions that aren’t bound must not use the full name.
IMPORTANT
A bound function must be invoked using a URI to set the first parameter value. You can’t set it as a named parameter
value.
The following example shows an example using the CalculateTotalTimeIncident Function /, which is bound to the
incident entity.
Request
Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
{
"@odata.context":"[Organization
URI]/api/data/v9.0/$metadata#Microsoft.Dynamics.CRM.CalculateTotalTimeIncidentResponse","TotalTime":30
}
Unbound functions
The WhoAmI Function / isn’t bound to an entity. It is defined in the CSDL without an IsBound attribute.
<ComplexType Name="WhoAmIResponse">
<Property Name="BusinessUnitId" Type="Edm.Guid" Nullable="false" />
<Property Name="UserId" Type="Edm.Guid" Nullable="false" />
<Property Name="OrganizationId" Type="Edm.Guid" Nullable="false" />
</ComplexType>
<Function Name="WhoAmI">
<ReturnType Type="mscrm.WhoAmIResponse" Nullable="false" />
</Function>
This function corresponds to the WhoAmIRequest and returns a WhoAmIResponse ComplexType / that
corresponds to the WhoAmIResponse used by the Organization service. This function doesn’t have any
parameters.
When invoking an unbound function, use just the function name as shown in the following example.
Request
Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
{
"@odata.context": "[Organization URI]/api/data/v9.0/$metadata#Microsoft.Dynamics.CRM.WhoAmIResponse",
"BusinessUnitId": "ded5a64f-f06d-e511-80d0-00155db07cb1",
"UserId": "d96e9f55-f06d-e511-80d0-00155db07cb1",
"OrganizationId": "4faf1f34-f06d-e511-80d0-00155db07cb1"
}
Composable functions
Some functions listed in Web API Function Reference will return a collection of entities. A subset of these
functions are composable, which means that you can include an additional $select or $filter system query
option to control which columns are returned in the results. These functions have an IsComposable attribute in
the CSDL. Each of these functions has a companion message in the organization service that accept either a
ColumnSet or QueryBase type parameter. The OData system query options provide the same functionality so
these functions do not have the same parameters as their companion messages in the organization service. The
following table shows a list of those composable functions in this release.
F UN C T IO N S F UN C T IO N S ( C O N T 'D) F UN C T IO N S ( C O N T 'D)
Query functions
Functions listed in Web API Query Function Reference are intended to be used to compose a query. These
functions can be used in a manner similar to the Built-in query functions, but there are some important
differences.
You must use the full name of the function and include the names of the parameters. The following example
shows how to use the LastXHours Function / to return all account entities modified in the past 12 hours.
Several query functions have a companion negated query function. For example, you can use the
NotEqualUserId Function /. The following query will return the expected results:
Other query functions can be negated in different ways. For example, rather than trying to negate the Last7Days
Function / like this (which will fail with the same error as mentioned above):
See also
Web API Functions and Actions Sample (C#)
Web API Functions and Actions Sample (Client-side JavaScript)
Perform operations using the Web API
Compose Http requests and handle errors
Query Data using the Web API
Create a table using the Web API
Retrieve a table using the Web API
Update and delete tables using the Web API
Associate and disassociate tables using the Web API
Use Web API actions
Execute batch operations using the Web API
Impersonate another user using the Web API
Perform conditional operations using the Web API
Use Web API actions
5/21/2021 • 7 minutes to read • Edit Online
NOTE
Unsure about entity vs. table? See Developers: Understand terminology in Microsoft Dataverse.
Actions and functions represent re-usable operations you can perform using the Web API. Use a POST request
with actions listed in Web API Action Reference to perform operations that have side effects. You can also define
custom actions and they’ll be available for you to use.
Unbound actions
Actions are defined in CSDL metadata document. As an example, the following is the definition of the
WinOpportunity Action represented in the metadata document.
<Action Name="WinOpportunity">
<Parameter Name="OpportunityClose" Type="mscrm.opportunityclose" Nullable="false" />
<Parameter Name="Status" Type="Edm.Int32" Nullable="false" />
</Action>
The WinOpportunity Action corresponds to the WinOpportunityRequest using the organization service. Use this
action to set the state of an opportunity to Won and create an opportunityclose EntityType to record the event.
This action doesn’t include a return value. If it succeeds, the operation is complete.
The OpportunityClose parameter requires a JSON representation of the opportunityclose entity to create in the
operation. This entity must be related to the opportunity issuing the opportunityid single-valued navigation
property. In the JSON this is set using the @odata.bind annotation as explained in Associate tables on create.
The Status parameter must be set to the status to for the opportunity when it is closed. You can find the default
value for this in the opportunity EntityType statuscode property. The Won option has a value of 3. You may ask
yourself, why is it necessary to set this value when there is only one status reason option that represents Won ?
The reason is because you may define custom status options to represent a win, such as Big Win or Small
Win , so the value could potentially be different from 3 in that situation.
The following example is the HTTP request and response to call the WinOpportunity action for an opportunity
with an opportunityid value of b3828ac8-917a-e511-80d2-00155d2a68d2 .
Request
POST [Organization URI]/api/data/v9.0/WinOpportunity HTTP/1.1
Accept: application/json
Content-Type: application/json; charset=utf-8
OData-MaxVersion: 4.0
OData-Version: 4.0
{
"Status": 3,
"OpportunityClose": {
"subject": "Won Opportunity",
"[email protected]": "[Organization URI]/api/data/v9.0/opportunities(b3828ac8-917a-e511-80d2-
00155d2a68d2)"
}
}
Response
Bound actions
There are two ways that an action can be bound. The most common way is for the action to be bound by an
entity. Less frequently, it can also be bound to an entity collection.
In the CSDL metadata document, when an Action element represents a bound action, it has an IsBound
attribute with the value true . The first Parameter element defined within the action represents the entity that
the operation is bound to. When the Type attribute of the parameter is a collection, the operation is bound to a
collection of entities.
When invoking a bound function, you must include the full name of the function including the
Microsoft.Dynamics.CRM namespace. If you do not include the full name, you will get the following error:
Status Code:400 Request message has unresolved parameters .
<ComplexType Name="AddToQueueResponse">
<Property Name="QueueItemId" Type="Edm.Guid" Nullable="false" />
</ComplexType>
<Action Name="AddToQueue" IsBound="true">
<Parameter Name="entity" Type="mscrm.queue" Nullable="false" />
<Parameter Name="Target" Type="mscrm.crmbaseentity" Nullable="false" />
<Parameter Name="SourceQueue" Type="mscrm.queue" />
<Parameter Name="QueueItemProperties" Type="mscrm.queueitem" />
<ReturnType Type="mscrm.AddToQueueResponse" Nullable="false" />
</Action>
This entity bound action is equivalent to the AddToQueueRequest used by the organization service. In the Web
API this action is bound to the queue EntityType that represents the AddToQueueRequest.DestinationQueueId
property. This action accepts several additional parameters and returns a AddToQueueResponse ComplexType
corresponding to the AddToQueueResponse returned by the organization service. When an action returns a
complex type, the definition of the complex type will appear directly above the action in the CSDL.
An action bound to an entity must be invoked using a URI to set the first parameter value. You cannot set it as a
named parameter value.
The following example shows using the AddToQueue Action to add a letter to a queue. Because the type of the
Target parameter type is not specific ( mscrm.crmbaseentity ), you must explicitly declare type of the object
using the @odata.type property value of the full name of the entity, including the Microsoft.Dynamics.CRM
namespace. In this case, Microsoft.Dynamics.CRM.letter . More information: Specify entity parameter type
Request
{
"Target": {
"activityid": "59ae8258-4878-e511-80d4-00155d2a68d1",
"@odata.type": "Microsoft.Dynamics.CRM.letter"
}
}
Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
{
"@odata.context": "[Organization URI]/api/data/v9.0/$metadata#Microsoft.Dynamics.CRM.AddToQueueResponse",
"QueueItemId": "5aae8258-4878-e511-80d4-00155d2a68d1"
}
As an example of an action bound to an entity collection, the following is the definition of the ExportTranslation
Action represented in the CSDL:
<ComplexType Name="ExportTranslationResponse">
<Property Name="ExportTranslationFile" Type="Edm.Binary" />
</ComplexType>
<Action Name="ExportTranslation" IsBound="true">
<Parameter Name="entityset" Type="Collection(mscrm.solution)" Nullable="false" />
<Parameter Name="SolutionName" Type="Edm.String" Nullable="false" Unicode="false" />
<ReturnType Type="mscrm.ExportTranslationResponse" Nullable="false" />
</Action>
This entity collection bound action is equivalent to the ExportTranslationRequest used by the organization
service. In the Web API this action is bound to the solution EntityType. But rather than passing a value to the
request, the entity collection binding simply applies the constraint that the URI of the request must include the
path to the specified entity set.
The following example shows using the ExportTranslation Action which exports a binary file containing data
about localizable string values which can be updated to modify or add localizable values. Note how the entity
collection bound action is preceded by the entity set name for the solution entity: solutions .
Request
{
"SolutionName":"MySolution"
}
Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
{
"@odata.context": "[Organization
URI]/api/data/v9.1/$metadata#Microsoft.Dynamics.CRM.ExportTranslationResponse",
"ExportTranslationFile": "[Binary data Removed for brevity]"
}
UI L A B EL VA L UE
Entity Contact
Categor y Action
Process Arguments
NAME TYPE REQ UIRED DIREC T IO N
Steps
After you publish and activate the custom action, when you download the CSDL you will find this new action
defined.
The following HTTP request and response shows how to call the custom action and the response it returns if
successful.
Request
{
"NoteTitle": "New Note Title",
"NoteText": "This is the text of the note"
}
Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
{
"@odata.context": "[Organization URI]/api/data/v9.0/$metadata#annotations/$entity",
"annotationid": "9ad8c461-a27a-e511-80d2-00155d2a68d2"
}
{
"Members": [{
"@odata.type": "Microsoft.Dynamics.CRM.systemuser",
"ownerid": "5dbf5efc-4507-e611-80de-5065f38a7b01"
}]
}
If you do not specify the type of entity in this situation, you can get the following error:
"EdmEntityObject passed should have the key property value set." .
See also
Web API Functions and Actions Sample (C#)
Web API Functions and Actions Sample (Client-side JavaScript)
Perform operations using the Web API
Compose Http requests and handle errors
Query Data using the Web API
Create a table using the Web API
Retrieve a table using the Web API
Update and delete tables using the Web API
Associate and disassociate tables using the Web API
Use Web API functions
Execute batch operations using the Web API
Impersonate another user using the Web API
Perform conditional operations using the Web API
Execute batch operations using the Web API
9/26/2021 • 8 minutes to read • Edit Online
NOTE
Unsure about entity vs. table? See Developers: Understand terminology in Microsoft Dataverse.
You can group multiple operations into a single HTTP request using a batch operation. These operations will be
performed sequentially in the order they are specified.
NOTE
Batch requests can contain up to 1000 individual requests and cannot contain other batch requests.
URLs for GET requests sent with a batch are limited to 32768 characters.
Batch requests
Use a POST request to submit a batch operation that contains multiple requests. A batch request can include
GET requests and change sets. To use the transactional capabilities of batch requests, only operations that will
change data can be included within a change set. GET requests must not be included in the change set.
The POST request containing the batch must have a Content-Type header with a value set to multipart/mixed
with a boundary set to include the identifier of the batch using this pattern:
--batch_<unique identifier>
The unique identifier doesn't need to be a GUID, but should be unique. Each item within the batch must be
preceded by the batch identifier with a Content-Type and Content-Transfer-Encoding header like the following:
--batch_WKQS9Yui9r
Content-Type: application/http
Content-Transfer-Encoding:binary
The end of the batch must contain a termination indicator like the following:
--batch_WKQS9Yui9r--
Change sets
When multiple operations are contained in a change set, all the operations are considered atomic, which means
that if any one of the operations fail, any completed operations will be rolled back. Like a batch request, change
sets must have a Content-Type header with value set to multipart/mixed with a boundary set to include the
identifier of the change set using this pattern:
--changeset_<unique identifier>
The unique identifier doesn't need to be a GUID, but should be unique. Each item within the change set must be
preceded by the change set identifier with a Content-Type and Content-Transfer-Encoding header like the
following:
--changeset_BBB456
Content-Type: application/http
Content-Transfer-Encoding:binary
Change sets can also include a Content-ID header with a unique value. This value, when prefixed with $ ,
represents a variable that contains the Uri for any entity created in that operation. For example, when you set the
value of 1, you can refer to that entity using $1 later in your change set.
The end of the change set must contain a termination indicator like the following:
--changeset_BBB456--
Handling errors
When an error occurs for a request within a batch, the error for that request will be returned for the batch
request and additional requests will not be processed.
You can use the Prefer: odata.continue-on-error request header to specify that additional requests be
processed when errors occur. The batch request will return 200 OK and individual response errors will be
returned in the batch response body.
Example
The following example includes a batch with a unique identifier of AAA123 and a change set with a unique
identifier of BBB456 .
Within the change set, two tasks are created using POST and associated with an existing account with
accountid = 00000000-0000-0000-000000000001 .
Finally, a GET request is included outside the change set to return all six tasks associated with the account,
including the two that were created in the batch request.
Request
--batch_AAA123
Content-Type: multipart/mixed;boundary=changeset_BBB456
--changeset_BBB456
Content-Type: application/http
Content-Transfer-Encoding:binary
Content-ID: 1
{"subject":"Task 1 in batch","[email protected]":"[Organization
URI]/api/data/v9.1/accounts(00000000-0000-0000-000000000001)"}
--changeset_BBB456
Content-Type: application/http
Content-Transfer-Encoding:binary
Content-ID: 2
{"subject":"Task 2 in batch","[email protected]":"[Organization
URI]/api/data/v9.1/accounts(00000000-0000-0000-000000000001)"}
--changeset_BBB456--
--batch_AAA123
Content-Type: application/http
Content-Transfer-Encoding:binary
GET[Organization URI]/api/data/v9.1/accounts(00000000-0000-0000-000000000001)/Account_Tasks?$select=subject
HTTP/1.1
Accept: application/json
--batch_AAA123--
Response
--batchresponse_c1bd45c1-dd81-470d-b897-e965846aad2f
Content-Type: multipart/mixed; boundary=changesetresponse_ff83b4f1-ab48-430c-b81c-926a2c596abc
--changesetresponse_ff83b4f1-ab48-430c-b81c-926a2c596abc
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: 1
--changesetresponse_ff83b4f1-ab48-430c-b81c-926a2c596abc
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: 2
--changesetresponse_ff83b4f1-ab48-430c-b81c-926a2c596abc--
--batchresponse_c1bd45c1-dd81-470d-b897-e965846aad2f
Content-Type: application/http
Content-Transfer-Encoding: binary
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
{
"@odata.context":"[Organization URI]/api/data/v9.1/$metadata#tasks(subject)","value":[
{
"@odata.etag":"W/\"474122\"","subject":"Task Created with Test Account","activityid":"919c24f3-fafc-
e411-80dd-00155d2a68cb"
},{
"@odata.etag":"W/\"474125\"","subject":"Task 1","activityid":"a29c24f3-fafc-e411-80dd-00155d2a68cb"
},{
"@odata.etag":"W/\"474128\"","subject":"Task 2","activityid":"a39c24f3-fafc-e411-80dd-00155d2a68cb"
},{
"@odata.etag":"W/\"474131\"","subject":"Task 3","activityid":"a49c24f3-fafc-e411-80dd-00155d2a68cb"
},{
"@odata.etag":"W/\"474134\"","subject":"Task 1 in batch","activityid":"a59c24f3-fafc-e411-80dd-
00155d2a68cb"
},{
"@odata.etag":"W/\"474137\"","subject":"Task 2 in batch","activityid":"a69c24f3-fafc-e411-80dd-
00155d2a68cb"
}
]
}
--batchresponse_c1bd45c1-dd81-470d-b897-e965846aad2f--
Include odata.include-annotations preference header with the GET requests and set its value to "*" to specify
that all annotations related to the properties be returned.
--batch_AAA123
Content-Type: application/http
Content-Transfer-Encoding:binary
GET[Organization URI]/api/data/v9.1/accounts(00000000-0000-0000-000000000001)?
$select=name,telephone1,emailaddress1,shippingmethodcode,customersizecode,accountratingcode,followemail,dono
temail,donotphone,statuscode HTTP/1.1
Accept: application/json
Prefer: odata.include-annotations="*"
--batch_AAA123--
--batch_AAA123
Content-Type: multipart/mixed; boundary=changeset_dd81ccab-11ce-4d57-b91d-12c4e25c3cab
--changeset_dd81ccab-11ce-4d57-b91d-12c4e25c3cab
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: 1
{
"firstname":"aaa",
"lastname":"bbb"
}
--changeset_dd81ccab-11ce-4d57-b91d-12c4e25c3cab
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: 2
{"@odata.type":"Microsoft.Dynamics.CRM.contact","firstname":"Oncall Contact-1111"}
--changeset_dd81ccab-11ce-4d57-b91d-12c4e25c3cab
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: 3
{
"name":"IcM Account",
"[email protected]":"$1",
"[email protected]":"$2"
}
--changeset_dd81ccab-11ce-4d57-b91d-12c4e25c3cab--
--batch_AAA123--
Response
200 OK
--batchresponse_3cace264-86ea-40fe-83d3-954b336c0f4a
Content-Type: multipart/mixed; boundary=changesetresponse_1a5db8a1-ec98-42c4-81f6-6bc6adcfa4bc
--changesetresponse_1a5db8a1-ec98-42c4-81f6-6bc6adcfa4bc
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: 1
--changesetresponse_1a5db8a1-ec98-42c4-81f6-6bc6adcfa4bc
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: 2
--changesetresponse_1a5db8a1-ec98-42c4-81f6-6bc6adcfa4bc
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: 3
--changesetresponse_1a5db8a1-ec98-42c4-81f6-6bc6adcfa4bc--
--batchresponse_3cace264-86ea-40fe-83d3-954b336c0f4a--
--batch_AAA123
Content-Type: multipart/mixed; boundary=changeset_dd81ccab-11ce-4d57-b91d-12c4e25c3cab
--changeset_dd81ccab-11ce-4d57-b91d-12c4e25c3cab
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: 1
{
"@odata.type":"Microsoft.Dynamics.CRM.contact",
"firstname":"Contact",
"lastname":"AAAAAA"
}
--changeset_dd81ccab-11ce-4d57-b91d-12c4e25c3cab
Content-Transfer-Encoding: binary
Content-Type: application/http
Content-ID: 2
{
"value":"BBBBB"
}
--changeset_dd81ccab-11ce-4d57-b91d-12c4e25c3cab--
--batch_AAA123--
Response
200 OK
--batchresponse_2cb48f48-39a8-41ea-aa52-132fa8ab3c2d
Content-Type: multipart/mixed; boundary=changesetresponse_d7528170-3ef3-41bd-be8e-eac971a8d9d4
--changesetresponse_d7528170-3ef3-41bd-be8e-eac971a8d9d4
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: 1
--changesetresponse_d7528170-3ef3-41bd-be8e-eac971a8d9d4
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: 2
--changesetresponse_d7528170-3ef3-41bd-be8e-eac971a8d9d4--
--batchresponse_2cb48f48-39a8-41ea-aa52-132fa8ab3c2d--
--batch_AAA123
Content-Type: multipart/mixed; boundary=changeset_dd81ccab-11ce-4d57-b91d-12c4e25c3cab
--changeset_dd81ccab-11ce-4d57-b91d-12c4e25c3cab
Content-Type:application/http
Content-Transfer-Encoding:binary
Content-ID:1
{"@odata.type":"Microsoft.Dynamics.CRM.account","name":"IcM Account"}
--changeset_dd81ccab-11ce-4d57-b91d-12c4e25c3cab
Content-Type:application/http
Content-Transfer-Encoding:binary
Content-ID:2
{"@odata.type":"Microsoft.Dynamics.CRM.contact","firstname":"Oncall Contact"}
--changeset_dd81ccab-11ce-4d57-b91d-12c4e25c3cab
Content-Type:application/http
Content-Transfer-Encoding:binary
Content-ID:3
{"@odata.id":"$2"}
--changeset_dd81ccab-11ce-4d57-b91d-12c4e25c3cab--
--batch_AAA123--
Response
200 OK
--batchresponse_0740a25c-d8e1-41a5-9202-1b50a297864c
Content-Type: multipart/mixed; boundary=changesetresponse_19ca0da8-d8bb-4273-a3f7-fe0d0fadfe5f
--changesetresponse_19ca0da8-d8bb-4273-a3f7-fe0d0fadfe5f
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: 1
--changesetresponse_19ca0da8-d8bb-4273-a3f7-fe0d0fadfe5f
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: 2
--changesetresponse_19ca0da8-d8bb-4273-a3f7-fe0d0fadfe5f
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: 3
--changesetresponse_19ca0da8-d8bb-4273-a3f7-fe0d0fadfe5f--
--batchresponse_0740a25c-d8e1-41a5-9202-1b50a297864c--
--batch_AAA123
Content-Type: multipart/mixed; boundary=changeset_dd81ccab-11ce-4d57-b91d-12c4e25c3cab
--changeset_dd81ccab-11ce-4d57-b91d-12c4e25c3cab
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: 1
{"@odata.type":"Microsoft.Dynamics.CRM.account","name":"IcM Account"}
--changeset_dd81ccab-11ce-4d57-b91d-12c4e25c3cab
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: 2
{
"@odata.type":"Microsoft.Dynamics.CRM.contact",
"firstname":"Oncall Contact"
}
--changeset_dd81ccab-11ce-4d57-b91d-12c4e25c3cab
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: 3
PATCH $1 HTTP/1.1
Content-Type: application/json
{
"[email protected]":"$2"
}
--changeset_dd81ccab-11ce-4d57-b91d-12c4e25c3cab--
--batch_AAA123--
Response
200 OK
--batchresponse_9595d3ae-48f6-414f-a3aa-a3a33559859e
Content-Type: multipart/mixed; boundary=changesetresponse_0c1567a5-ad0d-48fa-b81d-e6db05cad01c
--changesetresponse_0c1567a5-ad0d-48fa-b81d-e6db05cad01c
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: 1
--changesetresponse_0c1567a5-ad0d-48fa-b81d-e6db05cad01c
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: 2
--changesetresponse_0c1567a5-ad0d-48fa-b81d-e6db05cad01c
Content-Type: application/http
Content-Transfer-Encoding: binary
Content-ID: 3
--changesetresponse_0c1567a5-ad0d-48fa-b81d-e6db05cad01c--
--batchresponse_9595d3ae-48f6-414f-a3aa-a3a33559859e--
NOTE
Referencing a Content-ID before it has been declared in the request body will return the error HTTP 400 Bad request.
The example given below shows a request body that can cause this error.
Request body
--batch_AAA123
Content-Type: multipart/mixed; boundary=changeset_BBB456
--changeset_BBB456
Content-Type: application/http
Content-Transfer-Encoding:binary
Content-ID: 2
{
"phonenumber":"911",
"[email protected]" : "$1"
}
--changeset_BBB456
Content-Type: application/http
Content-Transfer-Encoding:binary
Content-ID: 1
{
"name":"QQQQ",
"revenue": 1.50
}
--changeset_BBB456--
--batch_AAA123--
Response
See also
Perform operations using the Web API
Compose Http requests and handle errors
Query Data using the Web API
Create a table using the Web API
Retrieve a table using the Web API
Update and delete tables using the Web API
Associate and disassociate tables using the Web API
Use Web API functions
Use Web API actions
Impersonate another user using the Web API
Perform conditional operations using the Web API
Impersonate another user using the Web API
5/21/2021 • 2 minutes to read • Edit Online
NOTE
Unsure about entity vs. table? See Developers: Understand terminology in Microsoft Dataverse.
There are times when your code will need to perform operations on behalf of another user. If the system account
running your code has the necessary privileges, you can perform operations on behalf of other users.
Request
Response
HTTP/1.1 204 No Content
OData-Version: 4.0
OData-EntityId: [Organization URI]/api/data/v9.0/accounts(00000000-0000-0000-000000000003)
Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
ETag: W/"506868"
{
"@odata.context": "[Organization
URI]/api/data/v9.0/$metadata#accounts(name,createdby(fullname,azureactivedirectoryobjectid),createdonbehalfb
y(fullname,azureactivedirectoryobjectid),owninguser(fullname,azureactivedirectoryobjectid))/$entity",
"@odata.etag": "W/\"2751197\"",
"name": "Sample Account created using impersonation",
"accountid": "00000000-0000-0000-000000000003",
"createdby": {
"@odata.etag": "W/\"2632435\"",
"fullname": "Impersonated User",
"azureactivedirectoryobjectid": "e39c5d16-675b-48d1-8e67-667427e9c084",
"systemuserid": "75df116d-d9da-e711-a94b-000d3a34ed47",
"ownerid": "75df116d-d9da-e711-a94b-000d3a34ed47"
},
"createdonbehalfby": {
"@odata.etag": "W/\"2632445\"",
"fullname": "Actual User",
"azureactivedirectoryobjectid": "3d8bed3e-79a3-47c8-80cf-269869b2e9f0",
"systemuserid": "278742b0-1e61-4fb5-84ef-c7de308c19e2",
"ownerid": "278742b0-1e61-4fb5-84ef-c7de308c19e2"
},
"owninguser": {
"@odata.etag": "W/\"2632435\"",
"fullname": "Impersonated User",
"azureactivedirectoryobjectid": "e39c5d16-675b-48d1-8e67-667427e9c084",
"systemuserid": "75df116d-d9da-e711-a94b-000d3a34ed47",
"ownerid": "75df116d-d9da-e711-a94b-000d3a34ed47"
}
}
See also
Impersonate another user
Impersonate another user using the Organization service
Perform operations using the Web API
Compose Http requests and handle errors
Query Data using the Web API
Create a table using the Web API
Retrieve a table using the Web API
Update and delete tables using the Web API
Associate and disassociate tables using the Web API
Use Web API functions
Use Web API actions
Execute batch operations using the Web API
Perform conditional operations using the Web API
Perform conditional operations using the Web API
5/21/2021 • 6 minutes to read • Edit Online
NOTE
Unsure about entity vs. table? See Developers: Understand terminology in Microsoft Dataverse.
Microsoft Dataverse provides support for a set of conditional operations that rely upon the standard HTTP
resource versioning mechanism known as ETags.
ETags
The HTTP protocol defines an entity tag, or ETag for short, for identifying specific versions of a resource. ETags
are opaque identifiers whose exact values are implementation dependent. ETag values occur in two varieties:
strong and weak validation. Strong validation indicates that a unique resource, identified by a specific URI, will
be identical on the binary level if its corresponding ETag value is unchanged. Weak validation only guarantees
that the resource representation is semantically equivalent for the same ETag value.
Dataverse generates a weakly validating @odata.etag property for every entity instance, and this property is
automatically returned with each retrieved entity record. For more information, see Retrieve a table using the
Web API.
WARNING
Client code should not give any meaning to the specific value of an ETag, nor to any apparent relationship between ETags
beyond equality or inequality. For example, an ETag value for a more recent version of a resource is not guaranteed to be
greater than the ETag value for an earlier version. Also, the algorithm used to generate new ETag values may change
without notice between releases of a service.
Conditional retrievals
Etags enable you to optimize record retrievals whenever you access the same record multiple times. If you have
previously retrieved a record, you can pass the ETag value with the If-None-Match header to request data to be
retrieved only if it has changed since the last time it was retrieved. If the data has changed, the request returns
an HTTP status of 200 (OK) with the latest data in the body of the request. If the data hasn't changed, the HTTP
status code 304 (Not Modified) is returned to indicate that the entity hasn't been modified.
The following example message pair returns data for an account entity with the accountid equal to
00000000-0000-0000-0000-000000000001 when the data hasn't changed since it was last retrieved when the Etag
value was W/"468026"
Request
Response
{
"name": "Updated Sample Account ",
"creditonhold": true,
"address1_latitude": 47.639583,
"description": "This is the updated description of the sample account",
"revenue": 6000000,
"accountcategorycode": 2
}
Response
If the entity is found, you'll get a normal response with status 204 (No Content). When the entity is not found,
you'll get the following response with status 404 (Not Found).
{
"error": {
"code": "",
"message": "account With Id = 00000000-0000-0000-0000-000000000001 Does Not Exist"
}
}
{
"name": "Updated Sample Account ",
"creditonhold": true,
"address1_latitude": 47.639583,
"description": "This is the updated description of the sample account",
"revenue": 6000000,
"accountcategorycode": 2
}
Response
If the entity isn't found, you will get a normal response with status 204 (No Content). When the entity is found,
you'll get the following response with status 412 (Precondition Failed).
HTTP/1.1 412 Precondition Failed
OData-Version: 4.0
Content-Type: application/json; odata.metadata=minimal
{
"error":{
"code":"",
"message":"A record with matching key values already exists."
}
}
Response
{
"error":{
"code":"","message":"The version of the existing record doesn't match the RowVersion property provided."
}
}
Response
{
"error":{
"code":"","message":"The version of the existing record doesn't match the RowVersion property provided."
}
}
See also
Web API Conditional Operations Sample (C#)
Web API Conditional Operations Sample (Client-side JavaScript)
Perform operations using the Web API
Compose Http requests and handle errors
Query Data using the Web API
Create a table using the Web API
Retrieve a table using the Web API
Update and delete tables using the Web API
Associate and disassociate tables using the Web API
Use Web API functions
Use Web API actions
Execute batch operations using the Web API
Impersonate another user using the Web API
Detect duplicate data using the Web API
5/21/2021 • 2 minutes to read • Edit Online
NOTE
Unsure about entity vs. table? See Developers: Understand terminology in Microsoft Dataverse.
Microsoft Dataverse Web API allows you to detect duplicate table rows of an existing row in order to maintain
integrity of data. For detailed information about detecting duplicate data using code, see Detect duplicate data
using code
NOTE
Passing of the CalculateMatchCodeSynchronously optional parameter is not required. The match codes used to detect
duplicates are calculated synchronously regardless of the value passed in this parameter.
Use preference header MSCRM.SuppressDuplicateDetection and set its value to false in the Web API request.
NOTE
Make sure there are appropriate duplicate detection rules in place. Dataverse includes default duplicate detection rules for
accounts, contacts, and leads, but not for other types of records. If you want the system to detect duplicates for other
record types, you'll need to create a new rule.
- For information on how to create a duplicate detection rule using the UI, see Set up duplicate detection rules to keep
your data clean.
- For information on creating duplicate detection rules using code, see Duplicate rule tables
Example: Detect duplicates during Create operation using the Web API
The following example shows how to detect duplicates during Create and Update operations using
MSCRM.SuppressDuplicateDetection header in Web API request.
Request
POST [Organization URI]/org1/api/data/v9.0/leads HTTP/1.1
If-None-Match: null
OData-Version: 4.0
OData-MaxVersion: 4.0
Content-Type: application/json
Accept: application/json
MSCRM.SuppressDuplicateDetection: false
{
"firstname":"Monte",
"lastname":"Orton",
"emailaddress1":"[email protected]"
}
If a lead record with the same emailaddress1 attribute already exists, the following Response is returned.
Response
{
"error": {
"code": "0x80040333",
"message": "A record was not created or updated because a duplicate of the current record already
exists."
}
}
Request
{
"firstname":"Monte",
"lastname":"Orton",
"emailaddress1":"[email protected]"
}
Response
{
"error": {
"code": "0x80040333",
"message": "A record was not created or updated because a duplicate of the current record already
exists."
}
}
See Also
Detect duplicate data using the Organization service
Access entity data faster using storage partitions
5/3/2021 • 2 minutes to read • Edit Online
An optional partition key can be specified to create a logical partition for non-relational custom entity data
stored in NoSql tables of Azure heterogenous storage (Cosmos DB). Having a partition key improves application
performance for large sets of data (millions of records) by grouping data items into logical sets within a table.
For example, a table containing products can be grouped logically into product categories to improve retrieval
of all items within a product category. The partition key value can be a string or numeric type. Once specified,
the partition key value can't be changed.
When no partition key is specified, the table is the logical boundary and retrieving a single item or a set of
logically related items from a large data set will not be as performant as when using a partition key.
HTTP commands that retrieve one and all customer records from the named storage partition.
An HTTP command that updates a customer record in the named storage partition.
An HTTP command that deletes a customer record in the named storage partition.
Additional information
Here are a few more details about the partition key and partition management.
The key value must be unique in the environment.
A partition is limited to 20 GB of data, and there is no method available to check the partition's current size.
There is no defined limit to the number of partitions you can allocate in an environment.
Partition allocation is automatic. Specifying a unique partition key during a Create operation creates a
partition. When all data has been deleted from the partition, the partition is deleted.
There is no method available to rename a key.
Presently, only the Create, Update, Retrieve, and Delete entity operations support storage partitioning.
See Also
Create an entity record using the Web API
Retrieve an entity record using the Web API
Update and delete entities using the Web API
Partitioning and horizontal scaling in Azure Cosmos DB
Use the Web API with table definitions
4/30/2021 • 2 minutes to read • Edit Online
NOTE
Unsure about entity vs. table? See Developers: Understand terminology in Microsoft Dataverse.
You can perform any of the table and column definition (metadata) operations with the Web API that you can
perform using the Organization service. This section provides guidance about how to use the Web API with the
entity types included in Web API Metadata EntityType Reference.
There are four entity set paths exposed to perform operations with definition entities as described in the
following table.
Each definition entity type uses MetadataId as the unique identifier property, which it inherits from the
MetadataBase EntityType/. While all definition entities have a MetadataId , you can’t query all of them directly.
For example, you can query and perform operations on attributes (table columns) only in the context of the
EntityMetadata entity that contains them.
These definition entities have some substantial differences from the tables that store business and application
data, for example:
The properties for definition entities use many of the complex and enum types defined in Web API
ComplexType Reference and Web API EnumType Reference rather than the primitive data types used for
properties in entities that inherit from crmbaseentity EntityType/.
Definition entities follow a different naming convention and maintain the Pascal Case naming style used
in the assemblies of the Organization service.
Definition entities make more extensive use of inheritance, which requires that you may need to perform
casts to retrieve the data that you want.
In This Section
Query table definitions using the Web API
You can use the Web API to query table or column definitions using a RESTful query style.
Retrieve table definitions by name or MetadataId
Your applications can adapt to configuration changes by querying the table and column definitions. When you
know one of the key properties of a definition item, you can retrieve definitions using the Web API.
Create and update table definitions using the Web API
You can create and update tables and columns using the Web API to achieve the same results you get with the
organization service CreateEntityRequest, UpdateEntityRequest, CreateAttributeRequest, and
UpdateAttributeRequest.
Create and update table relationships using the Web API
You can check whether tables are eligible to participate in a relationship with other tables and then create or
update those relationships using the Web API.
See also
Browse the table definitions for your environment
Use the Microsoft Dataverse Web API
Query table definitions using the Web API
4/30/2021 • 7 minutes to read • Edit Online
NOTE
Unsure about entity vs. table? See Developers: Understand terminology in Microsoft Dataverse.
Because Microsoft Dataverse is a metadata-driven application, developers may need to query the system
definitions at run-time to adapt to how an organization has been configured. This capability uses a RESTful
query style.
NOTE
You can also construct a query using an object-based style using the EntityQueryExpression ComplexType/ with the
RetrieveMetadataChanges Function/. This function allows for capturing changes to table definitions between two periods
of time as well as returning a limited set of definitions described by a query you specify.
Request
Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
{
"@odata.context": "[Organization
URI]/api/data/v9.0/$metadata#EntityDefinitions(DisplayName,IsKnowledgeManagementEnabled,EntitySetName)",
"value": [
{
"DisplayName": {
"LocalizedLabels": [
{
"Label": "Account",
"LanguageCode": 1033,
"IsManaged": true,
"MetadataId": "2a4901bf-2241-db11-898a-0007e9e17ebd",
"HasChanged": null
}
],
"UserLocalizedLabel": {
"Label": "Account",
"LanguageCode": 1033,
"IsManaged": true,
"MetadataId": "2a4901bf-2241-db11-898a-0007e9e17ebd",
"HasChanged": null
}
},
"IsKnowledgeManagementEnabled": false,
"EntitySetName": "accounts",
"MetadataId": "70816501-edb9-4740-a16c-6a5efbc05d84"
}
]
}
You can use any of the EntityMetadata properties with $select system query options and you can use
$filter on any properties which use primitive or enumeration values.
There are no limits on the number of metadata entities that will be returned in a query. There is no paging. All
matching resources will be returned in the first response.
This pattern works with BooleanManagedProperty ComplexType because the primitive value to check is one
level deep. However, this does not work on properties of Label ComplexType.
But you can’t include the OptionSet or GlobalOptionSet collection-valued navigation properties that
PicklistAttributeMetadata EntityType attributes have within the $select filter of this query.
In order to retrieve the properties of a specific type of attribute you must cast the Attributes collection-valued
navigation property to the type you want. The following query will return only the PicklistAttributeMetadata
EntityType attributes and will include the LogicalName as well as expanding the OptionSet and GlobalOptionSet
collection-valued navigation properties
GET [Organization
URI]/api/data/v9.0/EntityDefinitions(LogicalName='account')/Attributes/Microsoft.Dynamics.CRM.PicklistAttrib
uteMetadata?$select=LogicalName&$expand=OptionSet,GlobalOptionSet
NOTE
Despite the fact that the OptionSet and GlobalOptionSet collection-valued navigation properties are defined within
EnumAttributeMetadata EntityType, you cannot cast the attributes to this type. This means that if you want to filter on
other types which also inherit these properties (see Entity types that inherit from EnumAttributeMetadata ), you must
perform separate queries to filter for each type.
Another example of this is accessing the Precision property available in MoneyAttributeMetadata EntityType
and DecimalAttributeMetadata EntityType attributes. To access this property you must cast the attributes
collection either as MoneyAttributeMetadata EntityType or DecimalAttributeMetadata EntityType. An example
showing casting to MoneyAttributeMetadata is shown here.
GET [Organization
URI]/api/data/v9.0/EntityDefinitions(LogicalName='account')/Attributes/Microsoft.Dynamics.CRM.MoneyAttribute
Metadata?$select=LogicalName,Precision
Retrieving attributes
When you know the MetadataId for both the EntityMetadata and the AttributeMetadata, you can retrieve an
individual attribute and access the property values using a query like the following. This query retrieves the
LogicalName property of the attribute as well as expanding the OptionSet collection-valued navigation property.
Note that you must cast the attribute as a Microsoft.Dynamics.CRM.PicklistAttributeMetadata to access the
OptionSet collection-valued navigation property.
Request
Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
{
"@odata.context": "[Organization URI]/api/data/v9.0/$metadata#EntityDefinitions(70816501-edb9-4740-a16c-
6a5efbc05d84)/Attributes/Microsoft.Dynamics.CRM.PicklistAttributeMetadata(LogicalName,OptionSet)/$entity",
"LogicalName": "preferredappointmentdaycode",
"MetadataId": "5967e7cc-afbb-4c10-bf7e-e7ef430c52be",
"[email protected]": "[Organization URI]/api/data/v9.0/$metadata#EntityDefinitions(70816501-edb9-4740-
a16c-6a5efbc05d84)/Attributes(5967e7cc-afbb-4c10-bf7e-
e7ef430c52be)/Microsoft.Dynamics.CRM.PicklistAttributeMetadata/OptionSet/$entity",
"OptionSet": {
"Options": [
{
"Value": 0,
"Label": {
"LocalizedLabels": [
{
"Label": "Sunday",
"LanguageCode": 1033,
"IsManaged": true,
"MetadataId": "21d6a218-2341-db11-898a-0007e9e17ebd",
"HasChanged": null
}
],
"UserLocalizedLabel": {
"Label": "Sunday",
"LanguageCode": 1033,
"IsManaged": true,
"MetadataId": "21d6a218-2341-db11-898a-0007e9e17ebd",
"HasChanged": null
"HasChanged": null
}
},
"Description": {
"LocalizedLabels": [],
"UserLocalizedLabel": null
},
"Color": null,
"IsManaged": true,
"MetadataId": null,
"HasChanged": null
}
Additional options removed for brevity
],
"Description": {
"LocalizedLabels": [
{
"Label": "Day of the week that the account prefers for scheduling service activities.",
"LanguageCode": 1033,
"IsManaged": true,
"MetadataId": "1b67144d-ece0-4e83-a38b-b4d48e3f35d5",
"HasChanged": null
}
],
"UserLocalizedLabel": {
"Label": "Day of the week that the account prefers for scheduling service activities.",
"LanguageCode": 1033,
"IsManaged": true,
"MetadataId": "1b67144d-ece0-4e83-a38b-b4d48e3f35d5",
"HasChanged": null
}
},
"DisplayName": {
"LocalizedLabels": [
{
"Label": "Preferred Day",
"LanguageCode": 1033,
"IsManaged": true,
"MetadataId": "ebb7e979-f9e3-40cd-a86d-50b479b1c5a4",
"HasChanged": null
}
],
"UserLocalizedLabel": {
"Label": "Preferred Day",
"LanguageCode": 1033,
"IsManaged": true,
"MetadataId": "ebb7e979-f9e3-40cd-a86d-50b479b1c5a4",
"HasChanged": null
}
},
"IsCustomOptionSet": false,
"IsGlobal": false,
"IsManaged": true,
"IsCustomizable": {
"Value": true,
"CanBeChanged": false,
"ManagedPropertyLogicalName": "iscustomizable"
},
"Name": "account_preferredappointmentdaycode",
"OptionSetType": "Picklist",
"IntroducedVersion": null,
"MetadataId": "53f9933c-18a0-40a6-b4a5-b9610a101735",
"HasChanged": null
}
}
If you don’t require any properties of the attribute and only want the values of a collection-valued navigation
property such as OptionsSet, you can include that in the URL and limit the properties with a $select system
query option for a somewhat more efficient query. In the following example only the Options property of the
OptionSet are included.
Request
Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
{
"@odata.context": "[Organization
URI]/api/data/v9.0/$metadata#EntityDefinitions('account')/Attributes(5967e7cc-afbb-4c10-bf7e-
e7ef430c52be)/Microsoft.Dynamics.CRM.PicklistAttributeMetadata/OptionSet(Options)/$entity",
"Options": [{
"Value": 0,
"Label": {
"LocalizedLabels": [{
"Label": "Sunday",
"LanguageCode": 1033,
"IsManaged": true,
"MetadataId": "21d6a218-2341-db11-898a-0007e9e17ebd",
"HasChanged": null
}],
"UserLocalizedLabel": {
"Label": "Sunday",
"LanguageCode": 1033,
"IsManaged": true,
"MetadataId": "21d6a218-2341-db11-898a-0007e9e17ebd",
"HasChanged": null
}
},
"Description": {
"LocalizedLabels": [],
"UserLocalizedLabel": null
},
"Color": null,
"IsManaged": true,
"MetadataId": null,
"HasChanged": null
}
Additional options removed for brevity
],
"MetadataId": "53f9933c-18a0-40a6-b4a5-b9610a101735"
}
The properties available when querying this entity set are limited to those in the RelationshipMetadataBase
EntityType. To access properties from the entity types that inherit from RelationshipMetadataBase you need to
include a cast in the query like the following one to return only OneToManyRelationshipMetadata EntityType.
GET [Organization
URI]/api/data/v9.0/RelationshipDefinitions/Microsoft.Dynamics.CRM.OneToManyRelationshipMetadata?
$select=SchemaName
Because the entities returned are typed as OneToManyRelationshipMetadata , you can filter on the properties such
as ReferencedEntity to construct a query to return only the one-to-many entity relationships for a specific
entity, such as the account entity as shown in the following query:
GET [Organization
URI]/api/data/v9.0/RelationshipDefinitions/Microsoft.Dynamics.CRM.OneToManyRelationshipMetadata?
$select=SchemaName&$filter=ReferencedEntity eq 'account'
That query will return essentially the same results as the following query, which is filtered because it is included
in the EntityMetadataOneToManyRelationships collection-valued navigation property of the account entity. The
difference is that for the previous query you don’t need to know the MetadataId for the account entity.
NOTE
Unsure about entity vs. table? See Developers: Understand terminology in Microsoft Dataverse.
Your applications can adapt to configuration changes by querying the table and column definitions (metadata).
When you know one of the key properties of a definition item, you can retrieve definitions using the Web API.
NOTE
Expanding both the OptionSet and GlobalOptionSet single-valued navigation properties of PicklistAttributeMetadata
EntityType allows you to get the option definition whether the attribute is configured to use global option sets or the
'local' option set within the entity. If it is a 'local' option set, the GlobalOptionSet property will be null as shown below.
If the attribute used a global option set, the GlobalOptionSet property would contain the defined options and the
OptionSet property would be null.
Request
GET [Organization
URI]/api/data/v9.0/EntityDefinitions(LogicalName='account')/Attributes(LogicalName='accountcategorycode')/Mi
crosoft.Dynamics.CRM.PicklistAttributeMetadata?
$select=LogicalName&$expand=OptionSet($select=Options),GlobalOptionSet($select=Options) HTTP/1.1
OData-MaxVersion: 4.0
OData-Version: 4.0
Accept: application/json
Content-Type: application/json; charset=utf-8
Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
{
"@odata.context": "[Organization
URI]/api/data/v9.0/$metadata#EntityDefinitions('account')/Attributes/Microsoft.Dynamics.CRM.PicklistAttribut
eMetadata(LogicalName,OptionSet,GlobalOptionSet,OptionSet(Options),GlobalOptionSet(Options))/$entity",
"LogicalName": "accountcategorycode",
"MetadataId": "118771ca-6fb9-4f60-8fd4-99b6124b63ad",
"[email protected]": "[Organization
URI]/api/data/v9.0/$metadata#EntityDefinitions('account')/Attributes(118771ca-6fb9-4f60-8fd4-
99b6124b63ad)/Microsoft.Dynamics.CRM.PicklistAttributeMetadata/OptionSet(Options)/$entity",
"OptionSet": {
"Options": [{
"Value": 1,
"Label": {
"LocalizedLabels": [{
"Label": "Preferred Customer",
"LanguageCode": 1033,
"IsManaged": true,
"MetadataId": "0bd8a218-2341-db11-898a-0007e9e17ebd",
"HasChanged": null
}],
"UserLocalizedLabel": {
"Label": "Preferred Customer",
"LanguageCode": 1033,
"IsManaged": true,
"MetadataId": "0bd8a218-2341-db11-898a-0007e9e17ebd",
"HasChanged": null
}
},
"Description": {
"LocalizedLabels": [
],
"UserLocalizedLabel": null
},
"Color": null,
"IsManaged": true,
"MetadataId": null,
"HasChanged": null
}, {
"Value": 2,
"Label": {
"LocalizedLabels": [{
"Label": "Standard",
"LanguageCode": 1033,
"IsManaged": true,
"MetadataId": "0dd8a218-2341-db11-898a-0007e9e17ebd",
"HasChanged": null
}],
"UserLocalizedLabel": {
"Label": "Standard",
"LanguageCode": 1033,
"IsManaged": true,
"MetadataId": "0dd8a218-2341-db11-898a-0007e9e17ebd",
"HasChanged": null
}
},
"Description": {
"LocalizedLabels": [
],
"UserLocalizedLabel": null
},
"Color": null,
"IsManaged": true,
"MetadataId": null,
"HasChanged": null
}],
"MetadataId": "b994cdd8-5ce9-4ab9-bdd3-8888ebdb0407"
},
"GlobalOptionSet": null
}
DEF IN IT IO N IT EM EXA M P L E
Relationship GET
/api/data/v9.0/RelationshipDefinitions(<Relationship
MetadataId>)
Request
Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
{
"@odata.context":"[Organization URI]/api/data/v9.0/$metadata#EntityDefinitions(MetadataId)","value":[
{
"MetadataId":"70816501-edb9-4740-a16c-6a5efbc05d84"
}
]
}
Request
Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
{
"@odata.context": "[Organization URI]/api/data/v9.0/$metadata#EntityDefinitions(70816501-edb9-4740-a16c-
6a5efbc05d84)/Attributes(MetadataId)","value":[
{
"@odata.type": "#Microsoft.Dynamics.CRM.PicklistAttributeMetadata",
"MetadataId": "118771ca-6fb9-4f60-8fd4-99b6124b63ad"
}
]
}
Request
Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
{
"@odata.context": "[Organization URI]/api/data/v9.0/$metadata#EntityDefinitions(70816501-edb9-4740-a16c-
6a5efbc05d84)/Attributes/Microsoft.Dynamics.CRM.PicklistAttributeMetadata(LogicalName,OptionSet,GlobalOption
Set,OptionSet(Options),GlobalOptionSet(Options))/$entity",
"LogicalName": "accountcategorycode",
"MetadataId": "118771ca-6fb9-4f60-8fd4-99b6124b63ad",
"[email protected]": "[Organization URI]/api/data/v9.0/$metadata#EntityDefinitions(70816501-edb9-
4740-a16c-6a5efbc05d84)/Attributes(118771ca-6fb9-4f60-8fd4-
99b6124b63ad)/Microsoft.Dynamics.CRM.PicklistAttributeMetadata/OptionSet(Options)/$entity",
"OptionSet": {
"Options": [{
"Value": 1,
"Label": {
"LocalizedLabels": [{
"Label": "Preferred Customer",
"LanguageCode": 1033,
"IsManaged": true,
"MetadataId": "0bd8a218-2341-db11-898a-0007e9e17ebd",
"HasChanged": null
}],
"UserLocalizedLabel": {
"Label": "Preferred Customer",
"LanguageCode": 1033,
"IsManaged": true,
"MetadataId": "0bd8a218-2341-db11-898a-0007e9e17ebd",
"HasChanged": null
}
},
"Description": {
"LocalizedLabels": [
],
"UserLocalizedLabel": null
},
"Color": null,
"IsManaged": true,
"MetadataId": null,
"HasChanged": null
}, {
"Value": 2,
"Label": {
"LocalizedLabels": [{
"Label": "Standard",
"LanguageCode": 1033,
"IsManaged": true,
"MetadataId": "0dd8a218-2341-db11-898a-0007e9e17ebd",
"HasChanged": null
}],
"UserLocalizedLabel": {
"Label": "Standard",
"LanguageCode": 1033,
"IsManaged": true,
"MetadataId": "0dd8a218-2341-db11-898a-0007e9e17ebd",
"HasChanged": null
}
},
"Description": {
"LocalizedLabels": [
],
"UserLocalizedLabel": null
},
"Color": null,
"IsManaged": true,
"MetadataId": null,
"HasChanged": null
}],
"MetadataId": "b994cdd8-5ce9-4ab9-bdd3-8888ebdb0407"
},
"GlobalOptionSet": null
}
See also
Use the Web API with table definitions
Query table definitions using the Web API
Create and update table definitions using the Web API
Create and update table relationships using the Web API
Create and update table definitions using the Web
API
4/30/2021 • 11 minutes to read • Edit Online
NOTE
Unsure about entity vs. table? See Developers: Understand terminology in Microsoft Dataverse.
You can perform all the same operations on table definitions using the Web API that you can with the
Organization service. This topic focuses on working with table definitions (metadata) using the Web API. To find
details about the table definition properties, see Customize table definitions and EntityMetadata EntityType.
TIP
Entities, attributes, and global option sets (also known as tables, columns, and choices) are all solution components. When
you create them you can associate them with a solution by using the MSCRM.SolutionUniqueName request header and
setting the value to the unique name of the solution it should be part of.
OwnershipType UserOwned Note: For the values you can set here, see
OwnershipTypes EnumType.
IsActivity false
HasActivities false
EN T IT Y M ETA DATA P RO P ERT Y VA L UE
HasNotes false
In addition to the properties listed previously, the EntityMetadataAttributes property must contain an array that
includes one StringAttributeMetadata EntityType to represent the primary name attribute for the entity. The
attribute IsPrimaryName property must be true. The following table describes the properties set in the example.
SchemaName new_AccountName
RequiredLevel None
Note: For the values you can set here, see
AttributeRequiredLevelManagedProperty ComplexType and
AttributeRequiredLevel EnumType.
MaxLength 100
FormatName Text
Note: The primary name attribute must use Text format. For
format options available for other string attributes, see
String formats.
IsPrimaryName true
NOTE
When you create or update labels using the Label ComplexType, you only need to set the LocalizedLabels property.
The UserLocalizedLabel value returned is based on the user’s language preference and is read-only.
The following example shows the creation of a custom table with the properties set. The language is English
using the locale ID (LCID) of 1033. Valid locale ID values can be found at Locale ID (LCID) Chart.
Request
{
"@odata.type": "Microsoft.Dynamics.CRM.EntityMetadata",
"Attributes": [
{
"AttributeType": "String",
"AttributeTypeName": {
"Value": "StringType"
},
"Description": {
"@odata.type": "Microsoft.Dynamics.CRM.Label",
"LocalizedLabels": [
{
{
"@odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel",
"Label": "Type the name of the bank account",
"LanguageCode": 1033
}
]
},
"DisplayName": {
"@odata.type": "Microsoft.Dynamics.CRM.Label",
"LocalizedLabels": [
{
"@odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel",
"Label": "Account Name",
"LanguageCode": 1033
}
]
},
"IsPrimaryName": true,
"RequiredLevel": {
"Value": "None",
"CanBeChanged": true,
"ManagedPropertyLogicalName": "canmodifyrequirementlevelsettings"
},
"SchemaName": "new_AccountName",
"@odata.type": "Microsoft.Dynamics.CRM.StringAttributeMetadata",
"FormatName": {
"Value": "Text"
},
"MaxLength": 100
}
],
"Description": {
"@odata.type": "Microsoft.Dynamics.CRM.Label",
"LocalizedLabels": [
{
"@odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel",
"Label": "An entity to store information about customer bank accounts",
"LanguageCode": 1033
}
]
},
"DisplayCollectionName": {
"@odata.type": "Microsoft.Dynamics.CRM.Label",
"LocalizedLabels": [
{
"@odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel",
"Label": "Bank Accounts",
"LanguageCode": 1033
}
]
},
"DisplayName": {
"@odata.type": "Microsoft.Dynamics.CRM.Label",
"LocalizedLabels": [
{
"@odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel",
"Label": "Bank Account",
"LanguageCode": 1033
}
]
},
"HasActivities": false,
"HasNotes": false,
"IsActivity": false,
"OwnershipType": "UserOwned",
"SchemaName": "new_BankAccount"
}
Response
When you update table definitions with labels, you should include a custom MSCRM.MergeLabels header to
control how any labels in the update should be handled. If a label for an item already has labels for other
languages and you update it with a label that contains only one label for a specific language, the
MSCRM.MergeLabels header controls whether to overwrite the existing labels or merge your new label with any
existing language labels. With MSCRM.MergeLabels set to true , any new labels defined will only overwrite
existing labels when the language code matches. If you want to overwrite the existing labels to include only the
labels you include, set MSCRM.MergeLabels to false .
IMPORTANT
If you don’t include a MSCRM.MergeLabels header, the default behavior is as if the value were false and any localized
labels not included in your update will be lost.
When you update a table or column definition, you must use the PublishXml Action or PublishAllXml Action
before the changes you make will be applied to the application. More information: Publish customizations
Typically, you will retrieve the JSON definition of the entity attribute and modify the properties before you send
it back. The following example contains all the definition properties of the table created in the Create table
definitions example, but with the DisplayName changed to “Bank Business Name.” It may be useful to note that
the JSON here provides the default values for properties not set in the Create table definitions example.
Request
{
"@odata.context": "[Organization URI]/api/data/v9.0/$metadata#EntityDefinitions/$entity",
"ActivityTypeMask": 0,
"AutoRouteToOwnerQueue": false,
"CanTriggerWorkflow": true,
"Description": {
"LocalizedLabels": [
{
"Label": "An entity to store information about customer bank accounts",
"LanguageCode": 1033,
"IsManaged": false,
"IsManaged": false,
"MetadataId": "edc3abd7-c5ae-4822-a3ed-51734fdd0469",
"HasChanged": null
}
]
},
"DisplayCollectionName": {
"LocalizedLabels": [
{
"Label": "Bank Accounts",
"LanguageCode": 1033,
"IsManaged": false,
"MetadataId": "7c758e0c-e9cf-4947-93b0-50ec30b20f60",
"HasChanged": null
}
]
},
"DisplayName": {
"@odata.type": "Microsoft.Dynamics.CRM.Label",
"LocalizedLabels": [
{
"@odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel",
"Label": "Bank Business Name",
"LanguageCode": 1033
}
]
},
"EntityHelpUrlEnabled": false,
"EntityHelpUrl": null,
"IsDocumentManagementEnabled": false,
"IsOneNoteIntegrationEnabled": false,
"IsInteractionCentricEnabled": false,
"IsKnowledgeManagementEnabled": false,
"AutoCreateAccessTeams": false,
"IsActivity": false,
"IsActivityParty": false,
"IsAuditEnabled": {
"Value": false,
"CanBeChanged": true,
"ManagedPropertyLogicalName": "canmodifyauditsettings"
},
"IsAvailableOffline": false,
"IsChildEntity": false,
"IsAIRUpdated": false,
"IsValidForQueue": {
"Value": false,
"CanBeChanged": true,
"ManagedPropertyLogicalName": "canmodifyqueuesettings"
},
"IsConnectionsEnabled": {
"Value": false,
"CanBeChanged": true,
"ManagedPropertyLogicalName": "canmodifyconnectionsettings"
},
"IconLargeName": null,
"IconMediumName": null,
"IconSmallName": null,
"IsCustomEntity": true,
"IsBusinessProcessEnabled": false,
"IsCustomizable": {
"Value": true,
"CanBeChanged": true,
"ManagedPropertyLogicalName": "iscustomizable"
},
"IsRenameable": {
"Value": true,
"CanBeChanged": true,
"ManagedPropertyLogicalName": "isrenameable"
},
"IsMappable": {
"IsMappable": {
"Value": true,
"CanBeChanged": false,
"ManagedPropertyLogicalName": "ismappable"
},
"IsDuplicateDetectionEnabled": {
"Value": false,
"CanBeChanged": true,
"ManagedPropertyLogicalName": "canmodifyduplicatedetectionsettings"
},
"CanCreateAttributes": {
"Value": true,
"CanBeChanged": false,
"ManagedPropertyLogicalName": "cancreateattributes"
},
"CanCreateForms": {
"Value": true,
"CanBeChanged": true,
"ManagedPropertyLogicalName": "cancreateforms"
},
"CanCreateViews": {
"Value": true,
"CanBeChanged": true,
"ManagedPropertyLogicalName": "cancreateviews"
},
"CanCreateCharts": {
"Value": true,
"CanBeChanged": true,
"ManagedPropertyLogicalName": "cancreatecharts"
},
"CanBeRelatedEntityInRelationship": {
"Value": true,
"CanBeChanged": true,
"ManagedPropertyLogicalName": "canberelatedentityinrelationship"
},
"CanBePrimaryEntityInRelationship": {
"Value": true,
"CanBeChanged": true,
"ManagedPropertyLogicalName": "canbeprimaryentityinrelationship"
},
"CanBeInManyToMany": {
"Value": true,
"CanBeChanged": true,
"ManagedPropertyLogicalName": "canbeinmanytomany"
},
"CanEnableSyncToExternalSearchIndex": {
"Value": true,
"CanBeChanged": true,
"ManagedPropertyLogicalName": "canenablesynctoexternalsearchindex"
},
"SyncToExternalSearchIndex": false,
"CanModifyAdditionalSettings": {
"Value": true,
"CanBeChanged": true,
"ManagedPropertyLogicalName": "canmodifyadditionalsettings"
},
"CanChangeHierarchicalRelationship": {
"Value": true,
"CanBeChanged": true,
"ManagedPropertyLogicalName": "canchangehierarchicalrelationship"
},
"IsOptimisticConcurrencyEnabled": true,
"ChangeTrackingEnabled": false,
"IsImportable": true,
"IsIntersect": false,
"IsMailMergeEnabled": {
"Value": true,
"CanBeChanged": true,
"ManagedPropertyLogicalName": "canmodifymailmergesettings"
},
},
"IsManaged": false,
"IsEnabledForCharts": true,
"IsEnabledForTrace": false,
"IsValidForAdvancedFind": true,
"IsVisibleInMobile": {
"Value": false,
"CanBeChanged": true,
"ManagedPropertyLogicalName": "canmodifymobilevisibility"
},
"IsVisibleInMobileClient": {
"Value": false,
"CanBeChanged": true,
"ManagedPropertyLogicalName": "canmodifymobileclientvisibility"
},
"IsReadOnlyInMobileClient": {
"Value": false,
"CanBeChanged": true,
"ManagedPropertyLogicalName": "canmodifymobileclientreadonly"
},
"IsOfflineInMobileClient": {
"Value": false,
"CanBeChanged": true,
"ManagedPropertyLogicalName": "canmodifymobileclientoffline"
},
"DaysSinceRecordLastModified": 0,
"IsReadingPaneEnabled": true,
"IsQuickCreateEnabled": false,
"LogicalName": "new_bankaccount",
"ObjectTypeCode": 10009,
"OwnershipType": "UserOwned",
"PrimaryNameAttribute": "new_accountname",
"PrimaryImageAttribute": null,
"PrimaryIdAttribute": "new_bankaccountid",
"Privileges": [
{
"CanBeBasic": true,
"CanBeDeep": true,
"CanBeGlobal": true,
"CanBeLocal": true,
"CanBeEntityReference": false,
"CanBeParentEntityReference": false,
"Name": "prvCreatenew_BankAccount",
"PrivilegeId": "d1a8de4b-27df-42e1-bc5c-b863e002b37f",
"PrivilegeType": "Create"
},
{
"CanBeBasic": true,
"CanBeDeep": true,
"CanBeGlobal": true,
"CanBeLocal": true,
"CanBeEntityReference": false,
"CanBeParentEntityReference": false,
"Name": "prvReadnew_BankAccount",
"PrivilegeId": "726043b1-de2c-487e-9d6d-5629fca2bf22",
"PrivilegeType": "Read"
},
{
"CanBeBasic": true,
"CanBeDeep": true,
"CanBeGlobal": true,
"CanBeLocal": true,
"CanBeEntityReference": false,
"CanBeParentEntityReference": false,
"Name": "prvWritenew_BankAccount",
"PrivilegeId": "fa50c539-b6c7-4eaf-bd49-fd8224bc51b6",
"PrivilegeType": "Write"
},
{
"CanBeBasic": true,
"CanBeBasic": true,
"CanBeDeep": true,
"CanBeGlobal": true,
"CanBeLocal": true,
"CanBeEntityReference": false,
"CanBeParentEntityReference": false,
"Name": "prvDeletenew_BankAccount",
"PrivilegeId": "17c1fd6e-f856-45e7-b563-796f53108b85",
"PrivilegeType": "Delete"
},
{
"CanBeBasic": true,
"CanBeDeep": true,
"CanBeGlobal": true,
"CanBeLocal": true,
"CanBeEntityReference": false,
"CanBeParentEntityReference": false,
"Name": "prvAssignnew_BankAccount",
"PrivilegeId": "133ca81d-668e-4c19-a71e-10c6dfe099cd",
"PrivilegeType": "Assign"
},
{
"CanBeBasic": true,
"CanBeDeep": true,
"CanBeGlobal": true,
"CanBeLocal": true,
"CanBeEntityReference": false,
"CanBeParentEntityReference": false,
"Name": "prvSharenew_BankAccount",
"PrivilegeId": "15f27df4-9c67-47c9-b1f1-274e1c44f24a",
"PrivilegeType": "Share"
},
{
"CanBeBasic": true,
"CanBeDeep": true,
"CanBeGlobal": true,
"CanBeLocal": true,
"CanBeEntityReference": false,
"CanBeParentEntityReference": false,
"Name": "prvAppendnew_BankAccount",
"PrivilegeId": "ac8b1920-8f93-4e9d-94e3-c680e2a2f228",
"PrivilegeType": "Append"
},
{
"CanBeBasic": true,
"CanBeDeep": true,
"CanBeGlobal": true,
"CanBeLocal": true,
"CanBeEntityReference": false,
"CanBeParentEntityReference": false,
"Name": "prvAppendTonew_BankAccount",
"PrivilegeId": "f63a5f46-3bc7-4eac-81d0-7f77f566ef46",
"PrivilegeType": "AppendTo"
}
],
"RecurrenceBaseEntityLogicalName": null,
"ReportViewName": "Filterednew_BankAccount",
"SchemaName": "new_BankAccount",
"IntroducedVersion": "1.0",
"IsStateModelAware": true,
"EnforceStateTransitions": false,
"EntityColor": null,
"LogicalCollectionName": "new_bankaccounts",
"CollectionSchemaName": "new_BankAccounts",
"EntitySetName": "new_bankaccounts",
"IsEnabledForExternalChannels": false,
"IsPrivate": false,
"MetadataId": "417129e1-207c-e511-80d2-00155d2a68d2",
"HasChanged": null
}
Response
Create columns
You can create table columns (entity attributes) at the same time you create the table definition by including the
JSON definition of the attributes in the Attributes array for the entity you post in addition to the string
attribute that serves as the primary name attribute. If you want to add attributes to an entity that is already
created, you can send a POST request including the JSON definition of them to the entity Attributes collection-
valued navigation property.
SchemaName new_BankName
RequiredLevel None
MaxLength 100
FormatName Text
The following example creates a string attribute using the properties and adds it to the entity with the
MetadataId value of 402fa40f-287c-e511-80d2-00155d2a68d2.
The URI for the attribute is returned in the response.
Request
POST [Organization URI]/api/data/v9.0/EntityDefinitions(402fa40f-287c-e511-80d2-00155d2a68d2)/Attributes
HTTP/1.1
Accept: application/json
Content-Type: application/json; charset=utf-8
OData-MaxVersion: 4.0
OData-Version: 4.0
{
"AttributeType": "String",
"AttributeTypeName": {
"Value": "StringType"
},
"Description": {
"@odata.type": "Microsoft.Dynamics.CRM.Label",
"LocalizedLabels": [
{
"@odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel",
"Label": "Type the name of the bank",
"LanguageCode": 1033
}
]
},
"DisplayName": {
"@odata.type": "Microsoft.Dynamics.CRM.Label",
"LocalizedLabels": [
{
"@odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel",
"Label": "Bank Name",
"LanguageCode": 1033
}
]
},
"RequiredLevel": {
"Value": "None",
"CanBeChanged": true,
"ManagedPropertyLogicalName": "canmodifyrequirementlevelsettings"
},
"SchemaName": "new_BankName",
"@odata.type": "Microsoft.Dynamics.CRM.StringAttributeMetadata",
"FormatName": {
"Value": "Text"
},
"MaxLength": 100
}
Response
SchemaName new_Balance
DisplayName Balance
M O N EY AT T RIB UT E P RO P ERT IES VA L UES
RequiredLevel None
PrecisionSource 2
Note: For information on the valid values for
PrecisionSource, see MoneyType. The value 2 means that the
level of decimal precision will match
TransactionCurrency.CurrencyPrecision that is associated
with the current record.
The following example creates a money attribute using the properties and adds it to the entity with the
MetadataId value of 402fa40f-287c-e511-80d2-00155d2a68d2. The URI for the attribute is returned in the
response.
Request
{
"AttributeType": "Money",
"AttributeTypeName": {
"Value": "MoneyType"
},
"Description": {
"@odata.type": "Microsoft.Dynamics.CRM.Label",
"LocalizedLabels": [
{
"@odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel",
"Label": "Enter the balance amount",
"LanguageCode": 1033
}
]
},
"DisplayName": {
"@odata.type": "Microsoft.Dynamics.CRM.Label",
"LocalizedLabels": [
{
"@odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel",
"Label": "Balance",
"LanguageCode": 1033
}
]
},
"RequiredLevel": {
"Value": "None",
"CanBeChanged": true,
"ManagedPropertyLogicalName": "canmodifyrequirementlevelsettings"
},
"SchemaName": "new_Balance",
"@odata.type": "Microsoft.Dynamics.CRM.MoneyAttributeMetadata",
"PrecisionSource": 2
}
Response
HTTP/1.1 204 No Content
OData-Version: 4.0
OData-EntityId: [Organization URI]/api/data/v9.0/EntityDefinitions(402fa40f-287c-e511-80d2-
00155d2a68d2)/Attributes(f11bef16-287c-e511-80d2-00155d2a68d2)
SchemaName new_Checkeddate
DisplayName Date
RequiredLevel None
Format DateOnly Note: For the valid options for this property, see
DateTimeFormat EnumType.
The following example creates a datetime attribute using the properties and adds it to the entity with the
MetadataId value of 402fa40f-287c-e511-80d2-00155d2a68d2. The URI for the attribute is returned in the
response.
Request
POST [Organization URI]/api/data/v9.0/EntityDefinitions(402fa40f-287c-e511-80d2-00155d2a68d2)/Attributes
HTTP/1.1
Accept: application/json
Content-Type: application/json; charset=utf-8
OData-MaxVersion: 4.0
OData-Version: 4.0
{
"AttributeType": "DateTime",
"AttributeTypeName": {
"Value": "DateTimeType"
},
"Description": {
"@odata.type": "Microsoft.Dynamics.CRM.Label",
"LocalizedLabels": [
{
"@odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel",
"Label": "The date the account balance was last confirmed",
"LanguageCode": 1033
}
]
},
"DisplayName": {
"@odata.type": "Microsoft.Dynamics.CRM.Label",
"LocalizedLabels": [
{
"@odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel",
"Label": "Date",
"LanguageCode": 1033
}
]
},
"RequiredLevel": {
"Value": "None",
"CanBeChanged": true,
"ManagedPropertyLogicalName": "canmodifyrequirementlevelsettings"
},
"SchemaName": "new_Checkeddate",
"@odata.type": "Microsoft.Dynamics.CRM.DateTimeAttributeMetadata",
"Format": "DateOnly"
}
Response
SchemaName new_CustomerId
C USTO M ER LO O K UP AT T RIB UT E P RO P ERT IES VA L UES
DisplayName Customer
The example creates a customer lookup attribute, new_CustomerId , and adds it to the custom entity:
new_bankaccount . The response is a CreateCustomerRelationshipsResponse ComplexType.
Request
{
"OneToManyRelationships": [{
"SchemaName": "new_bankaccount_customer_account",
"ReferencedEntity": "account",
"ReferencingEntity": "new_bankaccount"
}, {
"SchemaName": "new_bankaccount_customer_contact",
"ReferencedEntity": "contact",
"ReferencingEntity": "new_bankaccount"
}],
"Lookup": {
"AttributeType": "Lookup",
"AttributeTypeName": {
"Value": "LookupType"
},
"Description": {
"@odata.type": "Microsoft.Dynamics.CRM.Label",
"LocalizedLabels": [{
"@odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel",
"Label": "Sample Customer Lookup Attribute",
"LanguageCode": 1033
}],
"UserLocalizedLabel": {
"@odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel",
"Label": "Sample Customer Lookup Attribute",
"LanguageCode": 1033
}
},
"DisplayName": {
"@odata.type": "Microsoft.Dynamics.CRM.Label",
"LocalizedLabels": [{
"@odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel",
"Label": "Customer",
"LanguageCode": 1033
}],
"UserLocalizedLabel": {
"@odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel",
"Label": "Customer",
"LanguageCode": 1033
}
},
"SchemaName": "new_CustomerId",
"@odata.type": "Microsoft.Dynamics.CRM.ComplexLookupAttributeMetadata"
}
}
Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
{
"@odata.context": "[Organization
URI]/api/data/v9.0/$metadata#Microsoft.Dynamics.CRM.CreateCustomerRelationshipsResponse",
"RelationshipIds": [
"a7d261bc-3580-e611-80d7-00155d2a68de", "aed261bc-3580-e611-80d7-00155d2a68de"
],
"AttributeId": "39a5d94c-e8a2-4a41-acc0-8487242d455e"
}
Update a column
As mentioned in Update table definitions, data model entities are updated using the HTTP PUT method with the
entire JSON definition of the current item. This applies to entity attributes as well as entities. Just like with
entities, you have the option to overwrite labels using the MSCRM.MergeLabels header with the value set to
false , and you must publish customizations before they are active in the system.
See also
Use the Web API with Microsoft Dataverse metadata
Query table definitions using the Web API
Retrieve table definitions by name or MetadataId
Model table relationships using the Web API
Work with table definitions using the Organization service
Column (attribute) definitions
Create and update table relationships using the
Web API
7/19/2021 • 4 minutes to read • Edit Online
NOTE
Unsure about entity vs. table? See Developers: Understand terminology in Microsoft Dataverse.
The Web API supports working with relationship definitions (metadata). The concepts described in Table
relationship definitions also apply to the Web API.
A C T IO N DESC RIP T IO N
CanBeReferenced Action Checks whether the specified entity can be the primary
entity (one) in a one-to-many relationship.
CanBeReferencing Action Checks whether the specified entity can be the referencing
entity (many) in a one-to-many relationship.
GetValidManyToMany Function Returns the set of entities that can participate in a many-to-
many relationship.
GetValidReferencedEntities Function Returns the set of entities that are valid as the primary entity
(one) from the specified entity in a one-to-many
relationship.
GetValidReferencingEntities Function Returns the set of entities that are valid as the related entity
(many) to the specified entity in a one-to-many relationship.
{
"SchemaName": "new_contact_new_bankaccount",
"@odata.type": "Microsoft.Dynamics.CRM.OneToManyRelationshipMetadata",
"AssociatedMenuConfiguration": {
"Behavior": "UseCollectionName",
"Group": "Details",
"Label": {
"@odata.type": "Microsoft.Dynamics.CRM.Label",
"LocalizedLabels": [
{
"@odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel",
"Label": "Bank Accounts",
"LanguageCode": 1033
}
],
"UserLocalizedLabel": {
"@odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel",
"Label": "Bank Accounts",
"LanguageCode": 1033
}
},
"Order": 10000
},
"CascadeConfiguration": {
"Assign": "Cascade",
"Delete": "Cascade",
"Merge": "Cascade",
"Reparent": "Cascade",
"Share": "Cascade",
"Unshare": "Cascade"
},
"ReferencedAttribute": "contactid",
"ReferencedEntity": "contact",
"ReferencingEntity": "new_bankaccount",
"Lookup": {
"AttributeType": "Lookup",
"AttributeTypeName": {
"Value": "LookupType"
},
"Description": {
"@odata.type": "Microsoft.Dynamics.CRM.Label",
"LocalizedLabels": [
{
"@odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel",
"Label": "The owner of the account",
"LanguageCode": 1033
}
],
"UserLocalizedLabel": {
"UserLocalizedLabel": {
"@odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel",
"Label": "The owner of the account",
"LanguageCode": 1033
}
},
"DisplayName": {
"@odata.type": "Microsoft.Dynamics.CRM.Label",
"LocalizedLabels": [
{
"@odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel",
"Label": "Account Owner",
"LanguageCode": 1033
}
],
"UserLocalizedLabel": {
"@odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel",
"Label": "Account Owner",
"LanguageCode": 1033
}
},
"RequiredLevel": {
"Value": "ApplicationRequired",
"CanBeChanged": true,
"ManagedPropertyLogicalName": "canmodifyrequirementlevelsettings"
},
"SchemaName": "new_AccountOwner",
"@odata.type": "Microsoft.Dynamics.CRM.LookupAttributeMetadata"
}
}
Response
{
"SchemaName": "new_accounts_campaigns",
"@odata.type": "Microsoft.Dynamics.CRM.ManyToManyRelationshipMetadata",
"Entity1AssociatedMenuConfiguration": {
"Behavior": "UseLabel",
"Group": "Details",
"Label": {
"@odata.type": "Microsoft.Dynamics.CRM.Label",
"LocalizedLabels": [
{
"@odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel",
"Label": "Account",
"LanguageCode": 1033
}
],
"UserLocalizedLabel": {
"@odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel",
"Label": "Account",
"LanguageCode": 1033
}
},
"Order": 10000
},
"Entity1LogicalName": "account",
"Entity2AssociatedMenuConfiguration": {
"Behavior": "UseLabel",
"Group": "Details",
"Label": {
"@odata.type": "Microsoft.Dynamics.CRM.Label",
"LocalizedLabels": [
{
"@odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel",
"Label": "Campaign",
"LanguageCode": 1033
}
],
"UserLocalizedLabel": {
"@odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel",
"Label": "Campaign",
"LanguageCode": 1033
}
},
"Order": 10000
},
"Entity2LogicalName": "campaign",
"IntersectEntityName": "new_accounts_campaigns"
}
Response
Update relationships
As discussed in Update table definitions, you update relationships using the HTTP PUT method to replace the
existing definition with changes you want to apply. You can’t edit individual properties using the HTTP PATCH
method as you can with business data tables. Just like with entities and attributes, you should include a
MSCRM.MergeLabels header with the value set to true to avoid overwriting localized labels not included in your
update and you must publish customizations before they are active in the system.
Delete relationships
To delete a relationship using the Web API, use the HTTP DELETE method with the URI for the relationship.
See also
Use the Web API with table definitions
Query table definitions using the Web API
Retrieve table definitions by name or MetadataId
Model tables and columns using the Web API
Multi-table Lookups
7/19/2021 • 3 minutes to read • Edit Online
Multi-table lookup type columns allow a user to use a specific table that has multiple one-to-many (1:M)
relationships to other tables in the environment. A single lookup type column can refer to multiple other tables.
A lookup value submitted to the multi-table type column will be matched to a record in any of the related tables.
Multi-table types are currently built into Microsoft Dataverse as static types like Customer, which connects to
Account and Contact. This new feature gives users the power to define any other multi-table lookups they may
need.
NOTE
At this time users can create and modify custom multi-table lookups via the SDK or Web APIs. Interactive user interface
support will be coming in a future release.
Examples
Let's say you are hosting media for users in a library. You have many different MediaObjects, many of them have
the same name but are in different formats like “Books”, “Audio”, and “Video”. Creating a multi-table lookup
called “new_Media” that has 1:M relationships to “new_Books”, “new_Audio”, and “new_Video” will result in a
“new_Media” lookup table that provides quick identifications of records stored in specific tables.
new_Media lookup table
P RIM A RY ID P RIM A RY N A M E REL AT EDID REL AT ED N A M E
new_Books table
P RIM A RY ID P RIM A RY N A M E C A L L N UM B ER
new_Audio table
P RIM A RY ID P RIM A RY N A M E A UDIO F O RM AT
The Media look up can return records across all the tables in the polymorphic lookup.
A lookup on Media with the name Content1 would retrieve records for <books1> and <audio1>
A lookup on Media of Content3 would retrieve records for <audio2> and <video1>
Web API example
Shown below is an HTTP post for a polymorphic lookup attribute.
Accept: application/json
Content-Type: application/json; charset=utf-8
OData-MaxVersion: 4.0
OData-Version: 4.0
{
"OneToManyRelationships": [
{
"SchemaName": "new_media_new_book",
"ReferencedEntity": "new_book",
"ReferencingEntity": "new_media"
},
{
"SchemaName": "new_media_new_video",
"ReferencedEntity": "new_video",
"ReferencingEntity": "new_media"
},
{
"SchemaName": "new_media_new_audio",
"ReferencedEntity": "new_audio",
"ReferencingEntity": "new_media",
"CascadeConfiguration": {
"Assign": "NoCascade",
"Delete": "RemoveLink",
"Merge": "NoCascade",
"Reparent": "NoCascade",
"Share": "NoCascade",
"Unshare": "NoCascade"
}
}
],
"Lookup": {
"AttributeType": "Lookup",
"AttributeTypeName": {
"Value": "LookupType"
},
"Description": {
"@odata.type": "Microsoft.Dynamics.CRM.Label",
"LocalizedLabels": [
{
"@odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel",
"Label": "Media Polymorphic Lookup",
"LanguageCode": 1033
}
],
],
"UserLocalizedLabel": {
"@odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel",
"Label": " Media Polymorphic Lookup Attribute",
"LanguageCode": 1033
}
},
"DisplayName": {
"@odata.type": "Microsoft.Dynamics.CRM.Label",
"LocalizedLabels": [
{
"@odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel",
"Label": "MediaPolymorphicLookup",
"LanguageCode": 1033
}
],
"UserLocalizedLabel": {
"@odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel",
"Label": "MediaPolymorphicLookup",
"LanguageCode": 1033
}
},
"SchemaName": "new_mediaPolymporphicLookup",
"@odata.type": "Microsoft.Dynamics.CRM.ComplexLookupAttributeMetadata"
}
}
The response from the HTTP post is shown below containing the ID of the polymorphic attribute and all the
relationships created.
{
"@odata.context":
"http://<organization
URL>/api/data/v9.1/$metadata#Microsoft.Dynamics.CRM.CreatePolymorphicLookupAttributeResponse",
"RelationshipIds":[
"77d4c6e9-0397-eb11-a81c-000d3a6cfaba",
"7ed4c6e9-0397-eb11-a81c-000d3a6cfaba",
"85d4c6e9-0397-eb11-a81c-000d3a6cfaba"
],
"AttributeId":"d378dd3e-42f4-4bd7-95c7-0ee546c7de40"
O P ERAT IO N
( M ET H O D) DESC RIP T IO N URL F O RM AT
The following table lists the operations relevant for table and attribute data.
O P ERAT IO N
( M ET H O D) DESC RIP T IO N URL F O RM AT
POST [OrganizationUrl]/api/data/v9.1/new_checkouts
{
"new_name": "c1",
[email protected]: "/new_books(387a2c9b-ecc6-ea11-a81e-000d3af68bd7)"
}
{
"new_name": "c2",
[email protected]: "/new_devices(6472e7ba-ecc6-ea11-a81e-000d3af68bd7)"
}
POST [OrganizationUrl]/api/data/v9.0/CreatePolymorphicLookupAttribute
{
"OneToManyRelationships": [
{
"SchemaName": "new_checkout_poly_new_book",
"ReferencedEntity": "new_book",
"ReferencingEntity": "new_checkout"
},
{
"SchemaName": "new_checkout_poly_new_device",
"ReferencedEntity": "new_device",
"ReferencingEntity": "new_checkout"
},
{
"SchemaName": "new_checkout_poly_new_dvd",
"ReferencedEntity": "new_dvd",
"ReferencingEntity": "new_checkout",
"CascadeConfiguration": {
"Assign": "NoCascade",
"Delete": "RemoveLink",
"Merge": "NoCascade",
"Reparent": "NoCascade",
"Share": "NoCascade",
"Unshare": "NoCascade"
}
}
],
"Lookup": {
"AttributeType": "Lookup",
"AttributeTypeName": {
"Value": "LookupType"
},
"Description": {
"@odata.type": "Microsoft.Dynamics.CRM.Label",
"LocalizedLabels": [
{
"@odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel",
"Label": "Checkouted item Polymorphic Lookup Attribute",
"LanguageCode": 1033
}
],
"UserLocalizedLabel": {
"@odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel",
"Label": "Checkedout item Polymorphic Lookup Attribute",
"LanguageCode": 1033
}
},
"DisplayName": {
"@odata.type": "Microsoft.Dynamics.CRM.Label",
"LocalizedLabels": [
{
"@odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel",
"Label": "Checkedout item",
"LanguageCode": 1033
}
],
"UserLocalizedLabel": {
"@odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel",
"Label": "Checkedout item",
"LanguageCode": 1033
}
},
"SchemaName": "new_CheckedoutItem",
"@odata.type": "Microsoft.Dynamics.CRM.ComplexLookupAttributeMetadata"
}
}
Add relationship to existing polymorphic lookup (example payload)
POST [OrganizationUrl]/api/data/v9.0/RelationshipDefinitions
{
"SchemaName": "new_checkout_poly_new_researchresource",
"@odata.type": "Microsoft.Dynamics.CRM.OneToManyRelationshipMetadata",
"CascadeConfiguration": {
"Assign": "NoCascade",
"Delete": "RemoveLink",
"Merge": "NoCascade",
"Reparent": "NoCascade",
"Share": "NoCascade",
"Unshare": "NoCascade"
},
"ReferencedEntity": "new_researchresource",
"ReferencingEntity": "new_checkout",
"Lookup": {
"AttributeType": "Lookup",
"AttributeTypeName": { "Value": "LookupType" },
"Description": {
"@odata.type": "Microsoft.Dynamics.CRM.Label",
"LocalizedLabels": [
{
"@odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel",
"Label": "Checkout Polymorphic Lookup Attribute",
"LanguageCode": 1033
}
],
"UserLocalizedLabel": {
"@odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel",
"Label": "Checkout Polymorphic Lookup Attribute",
"LanguageCode": 1033
}
},
"DisplayName": {
"@odata.type": "Microsoft.Dynamics.CRM.Label",
"LocalizedLabels": [
{
"@odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel",
"Label": "Checkout item",
"LanguageCode": 1033
}
],
"UserLocalizedLabel": {
"@odata.type": "Microsoft.Dynamics.CRM.LocalizedLabel",
"Label": "Checkout item",
"LanguageCode": 1033
}
},
"SchemaName": "new_CheckedoutItem",
"@odata.type": "Microsoft.Dynamics.CRM.LookupAttributeMetadata"
}
}
See Also
Create and update entity relationships
Create and update choices (option sets) using the
Web API
4/30/2021 • 2 minutes to read • Edit Online
NOTE
Unsure about entity vs. table? See Developers: Understand terminology in Microsoft Dataverse.
Typically, you use global option sets to set table columns so that different columns can share the same set of
options, which are maintained in one location. Unlike local option sets which are defined only for a specific
column, you can reuse global option sets. You will also see them used in request parameters in a manner similar
to an enumeration.
When you define a global option set using a POST request to [Organization URI]
/api/data/v9.0/GlobalOptionSetDefinitions , we recommend that you let the system assign a value. You do this
by passing a null value when you create the new OptionMetadata instance. When you define an option, it will
contain an option value prefix specific to the context of the publisher set for the solution that the option set is
created in. This prefix helps reduce the chance of creating duplicate option sets for a managed solution, and in
any option sets that are defined in environments where your managed solution is installed. For more
information, see Merge option set options.
Messages
The following table lists the messages that you can use with global option sets.
M ESSA GE W EB A P I O P ERAT IO N
The following table lists the messages you can use with local and global option sets
M ESSA GE W EB A P I O P ERAT IO N
See also
Customize choices
Use Postman with the Web API
4/30/2021 • 2 minutes to read • Edit Online
There are a number of third-party tools that allow you to authenticate to Microsoft Dataverse instances and to
compose and send Web API requests and view responses. Postman is one of the most popular.
Use Postman to perform ad hoc queries or to verify the behavior of operations without writing a program. This
section covers information on how to configure a Postman environment that connects to your Dataverse
instance and use Postman to perform operations with the Web API.
Postman offers many other capabilities beyond those covered in this content. More information: First 5 things to
try if you're new to Postman
In this section
Set up a Postman environment
Use Postman to perform operations with the Web API
Set up a Postman environment
10/4/2021 • 3 minutes to read • Edit Online
You can use Postman to connect to your Microsoft Dataverse instance and to compose Web API requests, send
them, and view responses. Managing authentication challenges many people. This topic describes how to
configure a Postman environment to work for your Dataverse environments.
You can use a Postman environment to save a set of variables that you use to connect. These values can be
accessed within Postman by using this syntax: {{name}} . For more information with Postman variables, see
Postman Documentation > Variables.
Prerequisites
Have a Power Apps Dataverse environment that you can connect to.
Download and install the Postman desktop application.
VA RIA B L E N A M E VA L UE
clientid 51f81489-12ee-4a9e-aaae-a2591f45987d
version 9.0
webapiurl {{url}}/api/data/v{{version}}/
callback https://callbackurl
authurl https://login.microsoftonline.com/common/oauth2/authorize?
resource={{url}}
NOTE
For Dataverse search, specify a version of 1.0 and a webapiurl of {{url}}/api/search/v{{version}}/.
2. Replace the instance URL placeholder value with the URL of your Dataverse environment, and select Add
to save the environment.
3. Close the Manage environments dialog box.
Generate an access token to use with your environment
To connect using OAuth 2.0 , you must have an access token. Use the following steps to get a new access token:
1. Make sure the new environment you created is selected.
2. Select the Authorization tab.
3. Set the Type to OAuth 2.0 .
4. Verify that you have selected the environment that you created.
5. Select Get New Access Token
6. Set the following values in the dialog box. Select Implicit from the Grant Type drop-down menu. You
can set the Token Name to whatever you like, and leave other keys set to default values.
NOTE
If you are configuring environments in Postman for multiple Dataverse instances using different user credentials,
you might need to delete the cookies cached by Postman. Select the Cookies link, which can be found under the
Send button, and remove the saved cookies from the Manage Cookies dialog box.
Some of these cookies are very persistent. You can delete some of them in groups, but you might have to delete
others individually. You might need to do this twice to ensure that no cookies remain.
7. Select Request Token . When you do this, an Azure Active Directory sign-in page appears. Enter your
username and password.
8. After the token is generated, scroll to the bottom and select Use Token . This closes the Manage Access
Tokens dialog box.
9. After you have added a token, you can select which token to apply to requests. On the Available Tokens
drop-down list, select the token you have just created. The Authorization header gets added to the Web
API request.
See Test your connection for steps to verify your connection.
See also
Use Postman to perform operations
Walkthrough: Register a Dataverse app with Azure Active Directory
Use Postman to perform operations with the Web
API
7/19/2021 • 3 minutes to read • Edit Online
NOTE
Unsure about entity vs. table? See Developers: Understand terminology in Microsoft Dataverse.
Use Postman to compose and send Web API requests and view responses. This topic describes how to use
Postman to create Web API requests that perform create, retrieve, update, and delete (CRUD) operations and use
functions and actions.
IMPORTANT
You need to have an environment that was created by using the steps described in Set up a Postman environment.
The environment created by using the instructions in Set up a Postman environment creates a {{webapiurl}}
Postman variable that provides the base URL for requests. Append to this variable to define the URL for your
requests.
The HTTP methods and values you use depend on the type of operations you want to perform. The following
sections show examples of common operations.
NOTE
Web API requests should include certain HTTP headers. Every request should include the Accept header value of
application/json , even when no response body is expected. The current OData version is 4.0 , so include header
OData-Version: 4.0 . Include the OData-MaxVersion header so that there is no ambiguity about the version when
there are new releases of OData. More information: HTTP headers.
Example
GET {{webapiurl}}accounts?$select=name,accountnumber&$top=3
{
"@odata.context":
"https://yourorg.crm.dynamics.com/api/data/v9.0/$metadata#accounts(name,accountnumber,primarycontactid(fulln
ame))/$entity",
"@odata.etag": "W/\"2291742\"",
"name": "Fourth Coffee",
"accountnumber": null,
"accountid": "a2706dc8-d2f5-e711-a956-000d3a328903",
"primarycontactid": {
"@odata.etag": "W/\"1697263\"",
"fullname": "Susie Curtis",
"contactid": "a3706dc8-d2f5-e711-a956-000d3a328903"
}
}
Create a record
Use a POST request to send data to create a record. Set the URL to the entity set name--in this case, accounts --
and set the headers as shown here.
POST {{webapiurl}}accounts
Set the body of the request with information about the account to create.
When you send this request, the body will be empty, but the ID of the created account will be in the
OData-EntityId header value.
Update a record
Use the PATCH method to update a table record, as shown here.
PATCH {{webapiurl}}accounts( <accountid> )
When you send this request, the response body will be empty, but the ID of the updated account will be in the
OData-EntityId header value.
More information: Update and delete tables using the Web API.
Delete a record
Use the DELETE method to delete an existing record.
DELETE {{webapiurl}}accounts( <accountid> )
When you send this request, the account record with the given accountid gets deleted.
More information: Update and delete tables using the Web API.
Use a function
Use a GET request with the functions listed in Web API Function Reference to perform reusable operations with
the Web API. The example that follows shows how to send a Web API request that uses the RetrieveDuplicates
function / to detect and retrieve duplicates of a specified record.
H T T P M ET H O D URL
GET {{webapiurl}}RetrieveDuplicates(BusinessEntity=@p1,MatchingEntityName=@p2,PagingInfo=
@p1={'@odata.type':'Microsoft.Dynamics.CRM.account','accountid':'
<accountid>
'}&@p2='account'&@p3={'PageNumber':1,'Count':50}
Functions return either a collection or a complex type. The response from the preceding RetrieveDuplicates
function should look like this:
{
{
"@odata.context": "https://yourorgname.crm.dynamics.com/api/data/v9.0/$metadata#accounts",
"value": [
<Omitted for brevity: JSON data for any matching accounts including all properties>
]
}
}
Use an action
Use a POST request with the actions listed in Web API Action Reference to perform operations that have side
effects.
This example shows how to use BulkDetectDuplicates action.
POST {{webapiurl}}BulkDetectDuplicates
The request in the example just shown submits an asynchronous duplicate detection job that runs in the
background. The duplicates are detected according to the published duplicate rules for the table type.
BulkDetectDuplicatesResponse ComplexType is returned as a response from BulkDetectDuplicates action. The
response includes the JobId property, which contains the GUID of the asynchronous duplicate detection job
that detects and logs duplicate records.
More information: Use Web API actions.
See also
Use Postman with the Web API
Perform operations using the Web API
Client-side JavaScript using Web API in model-
driven apps
5/21/2021 • 2 minutes to read • Edit Online
In HTML web resources, form scripts, or ribbon commands in model-driven apps, you can use JavaScript to
perform operations on Microsoft Dataverse data using the Web API. Use the Xrm.WebApi client API methods to
use Web API with JavaScript and web resources.
See also
Apply business logic using client scripting
Customize commands and ribbon
Web resources
Microsoft Dataverse Web API Versions
6/15/2021 • 2 minutes to read • Edit Online
Beginning with the v9.0 release of Dynamics 365, the Web API supports version specific differences in the same
environment.
This is different from the behavior for in the v8.x releases. In the previous releases new capabilities were
available to any version of the service depending on the update applied to the environment. After an upgrade to
v8.2, the v8.0, and v8.1 services were all identical. This was possible because all the changes were additive.
Nothing was removed or introduced breaking changes. As a result, the specific version referenced in the service
URL for the v8.x wasn't actually important.
Going forward the capabilities of the service can change, including potentially breaking changes such as
removing specific operations. This will allow for improvements to be applied on an on-going basis. This topic
will record any version specific differences and any limitations where the Web API hasn't yet achieved parity
with the organization service.
NOTE
While the v9.x releases can support specific differences, there have been no breaking changes added to v9.0, v9.1, or v9.2
releases. Each of these releases are have identical Web API behaviors.
Differences in API behavior is driven more by the solutions installed in the system rather than version of the product.
However, if we need to make a fundamental change that is not backward compatible, it will be included in a new version
number.
Guidance : Use the version number that was current when your code was written. Do not automatically use a newer
version without looking for documented differences here and testing. Do not assume a newer version wll be fully
backward compatible.
NOTE
If you defined a custom action which included a complex return value and a simple return value, a corresponding Action
was not available in the Web API but was available using the 2011 SOAP endpoint. A complex return value is an
EntityReference , Entity , or EntityCollection . You can have any combination of simple return values or a single
complex return value. More information: Create your own actions.
See also
Use the Dataverse Web API
Authenticate to Dataverse with the Web API
Web API types and operations
Perform operations using the Web API
Search across table data using Dataverse search
10/4/2021 • 10 minutes to read • Edit Online
NOTE
Unsure about entity vs. table? See Developers: Understand terminology in Microsoft Dataverse.
Dataverse search delivers fast and comprehensive search results across multiple tables, in a single list, sorted by
relevance. Dataverse search must be enabled in your target environment by an administrator before you can
use the feature. More information: Using Dataverse search to search for records
To begin using Dataverse search, your application simply issues an HTTP POST request (presently Web API only)
to start a Dataverse search. When searching data, specify optional query parameters to set criteria for how the
environment data is to be searched.
There are three Dataverse search methods that can be used in the Power Apps web application UI:
Search : Provides a search results page.
Suggestions : Provides suggestions as the user enters text into a form field.
Autocomplete : Provides autocompletion of input as the user enters text into a form field.
The following sections describe how to access the above mentioned search capabilities from application code.
Search
The minimum syntax of a Dataverse search HTTP request is as shown below.
The search parameter value contains the term to be searched for and has a 100-character limit.
A successful search response returns an HTTP status of 200 and consists of:
value: a list of tables. By default, 50 results are returned. This also includes search highlights, which
indicate matches to the search parameter value contained within the crmhit tag of the response.
totalrecordcount: The total count of results (of type long). A value of −1 is returned if
returntotalrecordcount is set to false (default).
The default table list searches across all Dataverse search–configured tables and columns. The default list is
configured by your administrator when Dataverse search is enabled.
facets=[list<string>] (optional)
Facets support the ability to drill down into data results after they've been retrieved.
“facets”: ["@search.entityname,count:100",
"account.primarycontactid,count:100",
"ownerid,count:100",
"modifiedon,values:2019-04-27T00:00:00|2020-03-27T00:00:00|2020-04-20T00:00:00|2020-04-27T00:00:00",
"createdon,values:2019-04-27T00:00:00|2020-03-27T00:00:00|2020-04-20T00:00:00|2020-04-27T00:00:00"]
}
filter=[string] (optional)
Filters are applied while searching data and are specified in standard OData syntax.
Specify true to return the total record count; otherwise false . The default is false .
skip=[int] (optional)
Specifies the number of search results to retrieve. The default is 50, and the maximum value is 100.
orderby=[list<string>] (optional)
A list of comma-separated clauses where each clause consists of a column name followed by 'asc' (ascending,
which is the default) or 'desc' (descending). This list specifies how to order the results in order of precedence. By
default, results are listed in descending order of relevance score (@search.score). For results with identical
scores, the ordering will be random.
For a set of results that contain multiple table types, the list of clauses for orderby must be globally applicable
(for example, modifiedon, createdon, @search.score). Note that specifying the orderby parameter overrides the
default. For example, to get results ranked (in order of precedence) by relevance, followed by the most recently
modified records listed higher:
“orderby”: [“@search.score desc", "modifiedon desc”]
If the query request includes a filter for a specific table type, orderby can optionally specify table-specific
columns.
searchmode= any | all (optional)
Specifies whether any or all the search terms must be matched to count the document as a match. The default is
'any'.
NOTE
The searchMode parameter on a query request controls whether a term with the NOT operator is AND'ed or OR'ed with
other terms in the query (assuming there is no + or | operator on the other terms).
Using searchMode=any increases the recall of queries by including more results, and by default will be interpreted as "OR
NOT". For example, "wifi -luxury" will match documents that either contain the term "wifi" or those that don't contain the
term "luxury".
Using searchMode=all increases the precision of queries by including fewer results, and by default will be interpreted as
"AND NOT". For example, "wifi -luxury" will match documents that contain the term "wifi" and don't contain the term
"luxury".
The search type specifies the syntax of a search query. Using 'simple' selects simple query syntax and 'full'
selects Lucene query syntax. The default is 'simple'.
The simple query syntax supports the following functionality:
F UN C T IO N A L IT Y DESC RIP T IO N
Precedence operators A search term "hotel+(wifi | luxury)" will search for results
containing the term "hotel" and either "wifi" or "luxury" (or
both).
F UN C T IO N A L IT Y DESC RIP T IO N
Proximity search Returns results where terms are within x words of each other,
for more contextual results.
For example, "airport hotel"~5 returns results where
"airport" and "hotel" are within five words of each other, thus
boosting the chances of finding a hotel located close to an
airport.
Regular expression (regex) search For example, /[mh]otel/ matches "motel" or "hotel".
NOTE
Wildcards are used only for word completion in Dataverse search. As a rule, querying with a leading wildcard will take
significantly longer than not using a wildcard, so we encourage you to explore alternative ways to find what you're looking
for and only use leading wildcards sparingly, if at all.
In order to use any of the search operators as part of the search text, escape the character by prefixing it with a
single backslash (\). Special characters that require escaping include the following: + - & | ! ( ) { } [ ] ^ " ~ * ? : \ /
Example: basic search
Below is an example of a basic search request and response.
Request
“facets”: ["@search.entityname,count:100",
"account.primarycontactid,count:100",
"ownerid,count:100",
"modifiedon,values:2019-04-27T00:00:00|2020-03-27T00:00:00|2020-04-20T00:00:00|2020-04-27T00:00:00",
"createdon,values:2019-04-27T00:00:00|2020-03-27T00:00:00|2020-04-20T00:00:00|2020-04-27T00:00:00"]
}
Response
{
"value": [
{
"@search.score": 0.4547767,
"@search.highlights": {
"emailaddress1": [
"{crmhit}maria{/crmhit}@contoso.com"
],
"firstname": [
"{crmhit}Maria{/crmhit}"
],
"fullname": [
"{crmhit}Maria{/crmhit} Sullivan"
]
},
"@search.entityname": "contact",
"@search.objectid": "16ffc791-d06d-4d8c-84ad-89a8978e14f3",
"ownerid": "bb2500d1-5e6d-4953-8389-bfedf57e3857",
"ownerid": "bb2500d1-5e6d-4953-8389-bfedf57e3857",
"owneridname": "Corey Gray",
"@search.ownerid.logicalname": "systemuser",
"@search.objecttypecode": 2,
"fullname": "Maria Sullivan",
"entityimage_url": **null**,
"createdon": "10/9/2020 5:27 PM",
"modifiedon": "10/9/2020 5:27 PM",
"emailaddress1": "[email protected]",
"address1_city": **“Seattle”**,
"address1_telephone1": **“206-400-0200”**,
"parentcustomerid": **null**,
"parentcustomeridname": **null**,
"telephone1": **“206-400-0300”**
}
],
"facets": {
"account.primarycontactid": [],
"ownerid": [
{
"Type": "Value",
"Value": "31ca7d4b-701c-4ea9-8714-a89a5172106e",
"OptionalValue": "Corey Gray",
"Count": 1
}
],
"@search.entityname": [
{
"Type": "Value",
"Value": "contact",
"Count": 1
}
],
"modifiedon": [
{
"Type": "Range",
"To": "4/27/2019 12:00 AM",
"Count": 0
},
{
"Type": "Range",
"From": "4/27/2019 12:00 AM",
"To": "3/27/2020 12:00 AM",
"Count": 0
},
{
"Type": "Range",
"From": "3/27/2020 12:00 AM",
"To": "4/20/2020 12:00 AM",
"Count": 0
},
{
"Type": "Range",
"From": "4/20/2020 12:00 AM",
"To": "4/27/2020 12:00 AM",
"Count": 0
},
{
"Type": "Range",
"From": "4/27/2020 12:00 AM",
"Count": 1
}
],
"createdon": [
{
"Type": "Range",
"To": "4/27/2019 12:00 AM",
"Count": 0
},
{
{
"Type": "Range",
"From": "4/27/2019 12:00 AM",
"To": "3/27/2020 12:00 AM",
"Count": 0
},
{
"Type": "Range",
"From": "3/27/2020 12:00 AM",
"To": "4/20/2020 12:00 AM",
"Count": 0
},
{
"Type": "Range",
"From": "4/20/2020 12:00 AM",
"To": "4/27/2020 12:00 AM",
"Count": 0
},
{
"Type": "Range",
"From": "4/27/2020 12:00 AM",
"Count": 1
}
]
},
"totalrecordcount": -1
}
Suggestions
Suggestions provide a list of matches to the specified search parameter value, based on a table record's primary
column. This is different from a regular search request because a suggestion search only searches through a
record's primary column, while search requests search through all Dataverse search–enabled table columns.
The minimum syntax of a suggestion search HTTP request is as shown below.
The search parameter value provides a text string for the search to match and has a three-character minimum
length.
A successful search response returns an HTTP status of 200 and contains "value", which is a list consisting of text
or a document where the text is the suggestion with highlights, and the document is a dictionary
<string,object> of the suggestion result. By default, five results are returned. Suggestion highlights indicate
matches to the search parameter value and are contained within the crmhit tag in the response.
In addition, you can add one or more query parameters to customize how the suggestion search is to be done
and which results are returned. The supported query parameters are indicated in the following section.
Query parameters
usefuzzy=true | false (optional)
A list of comma-separated clauses where each clause consists of an column name followed by 'asc' (ascending)
or 'desc' (descending). This list specifies how to order the results in order of precedence. By default, results are
listed in descending order of relevance score (@search.score). For results with identical scores, the ordering will
be random.
For a set of results that contain multiple table types, the list of clauses for orderby must be globally applicable
(for example, modifiedon, createdon, @search.score). Note that specifying the orderby parameter overrides the
default. For example, to get results ranked (in order of precedence) by relevance, followed by the most recently
modified records listed higher:
“orderby”: [“@search.score desc", "modifiedon desc”]
If the query request includes a filter for a specific table type, orderby can optionally specify table-specific
columns.
entities=[list<string>] (optional)
Filters are applied while searching data and are specified in standard OData syntax.
Request
Response
{
"value": [
{
"text": "{crmhit}Mar{/crmhit}ia Sullivan",
"document": {
"@search.objectid": "52a33850-8f0a-eb11-a813-000d3a8ab142",
"@search.entityname": "contact",
"@search.objecttypecode": 2,
"fullname": "Maria Sullivan",
"entityimage_url": **null**,
"emailaddress1": "[email protected]",
"address1_city": **null**,
"address1_telephone1": **null**,
"parentcustomerid": **null**,
"parentcustomeridname": **null**,
"telephone1": **null**
}
}
]
}
Autocomplete
Provides autocompletion of user input. Autocomplete is based on a table record's primary column.
The minimum syntax of a Dataverse search HTTP request is as follows.
A successful search response returns an HTTP status of 200 and consists of "value", which is a string.
In addition, you can add one or more query parameters to customize how the search is to be done and which
results are returned. The supported query parameters are indicated in the following section.
Query parameters
usefuzzy=true | false (optional)
The default scope is searching across all Dataverse search–configured tables and columns.
filter=[string] (optional)
Filters are applied while searching data and are specified in standard OData syntax.
Request
Response
{
"value": "{crmhit}maria{/crmhit}"
}
See also
Configure Dataverse search to improve search results and performance
Compare search options in Microsoft Dataverse
Retrieve related table records with a query
Query Data using the Web API
Connect with your Dataverse environment
Discover the URL for your organization
5/21/2021 • 3 minutes to read • Edit Online
NOTE
Unsure about entity vs. table? See Developers: Understand terminology in Microsoft Dataverse.
You can create a client for a specific Microsoft Dataverse environment and not provide the user any options
about which environment they can connect with. Only valid users for that specific environment can use your
client.
If you want people using your client to be able to connect to any Dataverse environment they have access to you
could prompt them to enter the URL for their environment, but this is not recommended. Each user may have
access to multiple Dataverse environments. Users may not know or remember the URL for their environment.
Expecting people to enter this URL is bound to frustrate users.
Instead, your client should provide a list of each of the available environments based on the user’s credentials. If
there is more than one environment available, your application should prompt the user to choose which
environment they want to connect with.
With Dataverse, server and organization allocation may change as part of datacenter management and load
balancing. Therefore, a discovery service provides a way to discover which server is serving an instance at a
given time.
When accessing the Discovery Service using the OData V4 RESTful API, you can add standard $filter and
$select parameters to the service request to customize the returned list of instance data.
IMPORTANT
Effective March 2, 2020, the regional Discovery Service will be deprecated. Applications must use the global Discovery
Service that is documented in this topic.
For Dynamics 365 US Government users, a global Discovery Service endpoint is available for the GCC and GCC High
users, and the URL is different from the regular global Discovery Service URL. More information: Dynamics 365
Government URLs.
GET https://globaldisco.crm.dynamics.com/api/discovery/v2.0/Instances(UniqueName='myorg')
In the above example, the global Discovery Service is used to obtain the organization information of the
instance with a unique name of "myorg". More details about this request is provided later in this topic.
Scope of the returned information
For the global Discovery Service, the Instances entity set, returns the set of instances (environments) that the
user has access to across all geographies, when no filters are applied. The returned data has a scope as
described below.
Includes all instances in the commercial cloud where the user is provisioned and enabled, except sovereign
clouds instances are not returned
Does not include instances where the user's account is disabled
Does not include instances where users have been filtered out based on an instance security group
Does not include instances where the user has access as a result of being a delegated administrator
If the calling user has access to no instances, the response simply returns an empty list
GET https://globaldisco.crm.dynamics.com/api/discovery/v2.0/Instances(<guid>)
GET https://globaldisco.crm.dynamics.com/api/discovery/v2.0/Instances(UniqueName='myorg')
GET https://globaldisco.crm.dynamics.com/api/discovery/v2.0/Instances?
$select=DisplayName,Description&$filter=Type+eq+0
See also
Discovery Service sample (C#)
Modify your code to use global Discovery Service
4/30/2021 • 2 minutes to read • Edit Online
The Discovery Service APIs can be used by your application to discover business organization instances
(environments) that the application user has access to. If your application currently uses the Organization
Service API on the 2011 SOAP endpoint to discover organization instances, you can follow the steps in this topic
and convert your application to access organization details using the OData V4 RESTful API with the global
Discovery Service URL. If your application accesses the Discovery Service using the regional Discovery Service
URL, you will need to change the application code from using the regional URL to the global Discovery Service
URL.
A detailed description of using the Discovery Service with the RESTful API can be found on the Discover the URL
for your organization page.
IMPORTANT
When accessing the Discovery Service, it is strongly recommended that your application use the global Discovery Service
endpoint (https://globaldisco.crm.dynamics.com) and not the regional Discovery Service endpoint, which will be
deprecated on March 2, 2020. The global Discovery Service is only available when using the RESTful API.
The rest of this document describes the changes that may be needed to call the Discovery Service using the
RESTful API.
Authentication
Accessing the Discovery Service using the RESTful API requires authentication with an OAuth 2.0 access token. If
your application code uses WS-Trust SAML tokens for authentication, you need to change your application code
to acquire an OAuth 2.0 token from Azure Active Directory (AD), and then add that token in the Authorization
header of the Discovery Service API calls. More information: Use OAuth with Microsoft Dataverse.
GET https://globaldisco.crm.dynamics.com/api/discovery/v2.0/Instances
GET https://globaldisco.crm.dynamics.com/api/discovery/v2.0/Instances(Region={region})
Response
{
"value":[
{
"Id":"<GUID>",
"UniqueName":"myorg",
"UrlName":"orgurlname",
"FriendlyName":"My Org",
"State":0,
"Version":"<Version>",
"Url":"https://orgurlname.crm.dynamics.com",
"ApiUrl":"https://orgurlname.api.crm.dynamics.com"
}
]
}
GET https://globaldisco.crm.dynamics.com/api/discovery/v2.0/Instances(<guid>)
GET https://globaldisco.crm.dynamics.com/api/discovery/v2.0/Instances(UniqueName='myorg')
Response
{
"Id":"<GUID>",
"UniqueName":"myorg",
"UrlName":"orgurlname",
"FriendlyName":"My Org",
"State":0,
"Version":"<Version>",
"Url":"https://orgurlname.crm.dynamics.com",
"ApiUrl":"https://orgurlname.api.crm.dynamics.com"
}
Mapping of columns
The table shown below shows the column mapping in the responses returned from the Discovery Service when
using the two APIs. These are applicable to all above example calls.
Endpoints[WebApplication] Url
Endpoints[OrganizationService] {ApiUrl}/XRMServices/2011/Organization.svc
Endpoints[OrganizationDataService] {ApiUrl}//XRMServices/2011/OrganizationData.svc
FriendlyName FriendlyName
OrganizationId Id
OrganizationVersion Version
RESP O N SE F IEL D ( SO A P EN DP O IN T ) RESP O N SE F IEL D ( O DATA V4 REST F UL EN DP O IN T )
State State
0: Enabled
1: Disabled
UniqueName UniqueName
UrlName UrlName
See Also
Discovery Services
Use the Dataverse Web API
Global Discovery Service Sample (C#)
7/19/2021 • 2 minutes to read • Edit Online
NOTE
Unsure about entity vs. table? See Developers: Understand terminology in Microsoft Dataverse.
This sample shows how to access the global Discovery Service using the Web API.
HttpResponseMessage response =
client.GetAsync("api/discovery/v2.0/Instances", HttpCompletionOption.ResponseHeadersRead).Result;
if (response.IsSuccessStatusCode)
{
//Get the response content and parse it.
string result = response.Content.ReadAsStringAsync().Result;
JObject body = JObject.Parse(result);
JArray values = (JArray)body.GetValue("value");
if (!values.HasValues)
{
return new List<Instance>();
}
return JsonConvert.DeserializeObject<List<Instance>>(values.ToString());
}
else
{
throw new Exception(response.ReasonPhrase);
}
}
/// <summary>
/// Object returned from the Discovery Service.
/// </summary>
class Instance
{
public string Id { get; set; }
public string UniqueName { get; set; }
public string UrlName { get; set; }
public string FriendlyName { get; set; }
public int State { get; set; }
public string Version { get; set; }
public string Url { get; set; }
public string ApiUrl { get; set; }
public DateTime LastUpdated { get; set; }
}
See Also
Discover the URL for your organization
Web API data operations samples
6/18/2021 • 2 minutes to read • Edit Online
NOTE
Unsure about entity vs. table? See Developers: Understand terminology in Microsoft Dataverse.
You can use the Microsoft Dataverse Web API with a wide variety of programming languages or libraries. This
guide provides a number of code samples demonstrating how to use the Web API in different ways. This topic
introduces the samples available for each group of operations and how to perform these operations using
different languages or libraries.
Web API Samples (this topic) Web API Samples (C#) Web API Samples (Client-side
JavaScript)
Groups of operations
The following table classifies the samples by demonstrated operation groups.
Web API Basic Operations Sample How to perform basic CRUD (Create, Retrieve, Update, and
Delete) and associative operations.
More information:
- Create a table row using the Web API
- Retrieve a table row using the Web API
- Update and delete table rows using the Web API
- Associate and disassociate table rows using the Web
API
Web API Query Data Sample How to perform basic query requests.
More information:
- Query Data using the Web API
- Retrieve and execute predefined queries
Web API Conditional Operations Sample How to perform certain categories of operations that are
conditionally based upon the version of the table row
contained on the server and/or currently maintained by the
client.
More information:
- Perform conditional operations using the Web API
GRO UP DESC RIP T IO N
Web API Functions and Actions Sample How to use bound/unbound functions and actions, including
custom actions.
More information:
- Use Web API functions
- Use Web API actions
Language or library
The following table lists the topics that cover the common language- or library-specific implementation issues.
Web API Samples (C#) Describes the common elements used in this group of C#
samples which demonstrate operations using basic .NET
classes and a minimum of helper libraries.
See also
Use the Dataverse Web API
Web API Basic Operations Sample
Web API Query Data Sample
Web API Conditional Operations Sample
Web API Functions and Actions Sample
Web API Samples (C#)
Web API Basic Operations Sample
6/18/2021 • 11 minutes to read • Edit Online
NOTE
Unsure about entity vs. table? See Developers: Understand terminology in Microsoft Dataverse.
This collection of sample code snippets demonstrate how to perform basic CRUD (Create, Retrieve, Update, and
Delete) and associative operations using the Microsoft Dataverse Web API.
Web API Basic Operations Sample (C#)
This topic describes a common set of operations implemented by each sample snippet in this group. This topic
describes the HTTP requests and responses and text output that each sample will perform without the language
specific details. See the language specific descriptions and the individual samples for details about how these
operations are performed.
Demonstrates
This sample is divided into the following sections, containing Dataverse Web API operations which are discussed
in greater detail in the specified associated conceptual topics.
Section 3: Create related table rows (deep insert) Create related table rows in one operation
Section 4: Associate and disassociate existing table rows Associate and disassociate table rows using the Web API
NOTE
For brevity, less pertinent HTTP headers have been omitted. The URLs of the records will vary with the base organization
address and the ID of the row assigned by your Dataverse server.
Response
Console output
The properties available for each type are defined within the metadata document and are also documented for
each type in the Web API EntityType Reference section. For more general information, see Web API types and
operations.
2. Update the contact with values for annual income ($80,000) and job title (Junior Developer).
Request
Response
Console output
Contact 'Peter Cambel' updated with job title and annual income.
3. Retrieve the contact with its set of explicitly initialized properties. The fullname is a read-only property
that is calculated from the firstname and lastname properties, which were explicitly initialized when the
instance was created. In contrast, the description property was not explicitly initialized, so it retains its
default value, a null string.
Note that the response, in addition to the requested values and typical headers, also automatically returns
the following types of additional information:
The primary ID for the current table type, here contactid .
An ETag value, denoted by the @odata.etag key, which identifies the specific version of the
resource requested. For more information, see Perform conditional operations using the Web API.
The metadata context, denoted by the @odata.context key, provides a way to compare query
results to determine if they came from the same query.
A _transactioncurrencyid_value that indicates the local currency of the monetary transaction.
Request
Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
{
"@odata.context":"https://[Organization
URI]/api/data/v9.0/$metadata#contacts(fullname,annualincome,jobtitle,description)/$entity",
"@odata.etag":"W/\"628883\"",
"fullname":"Peter Cambel",
"annualincome":80000.0000,
"jobtitle":"Junior Developer",
"description":null,
"_transactioncurrencyid_value":"0d4ed62e-95f7-e511-80d1-00155da84c03",
"contactid":"60f77a42-5f0e-e611-80e0-00155da84c03"
}
Console output
IMPORTANT
You should always use selection and filtering in retrieval operations to optimize performance. For more information, see
Query Data using the Web API.
4. Update the contact instance by supplying new values to these same properties.
Request
PATCH https://[Organization URI]/api/data/v9.0/contacts(60f77a42-5f0e-e611-80e0-00155da84c03) HTTP/1.1
Content-Type: application/json
OData-MaxVersion: 4.0
OData-Version: 4.0
{
"jobtitle": "Senior Developer",
"annualincome": 95000,
"description": "Assignment to-be-determined"
}
Response
Console output
IMPORTANT
Only send changed properties in update requests. For more information, see Basic update.
5. Explicitly set a single property, the primary phone number. Note this is a PUT request and that the JSON key
named value is used when performing operations on individual properties.
Request
Response
Console output
6. Retrieve that same single property, the primary phone number. Again note the use of the key named value .
Request
GET https://[Organization URI]/api/data/v9.0/contacts(60f77a42-5f0e-e611-80e0-00155da84c03)/telephone1
HTTP/1.1
Accept: application/json
OData-MaxVersion: 4.0
OData-Version: 4.0
Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
{
"@odata.context":"https://[Organization URI]/api/data/v9.0/$metadata#contacts(60f77a42-5f0e-e611-80e0-
00155da84c03)/telephone1",
"value":"555-0105"
}
Console output
7. Create a similar contact but also return instance information in the same operation. This latter capability is
enabled by the Prefer: return=representation header.
Request
Response
Console output
Contact 'Peter_Alt Cambel' created:
Annual income: 80000
Job title: Junior Developer
Contact URI: https://[Organization URI]/api/data/v9.0/contacts(199250b7-6cbe-e611-80f7-00155da84c08)
8. Update this similar contact and also return instance information in the same operation. Again, this capability
is enabled by the Prefer: return=representation header.
Request
Response
Console output
Request
POST https://[Organization URI]/api/data/v9.0/accounts HTTP/1.1
Content-Type: application/json
OData-MaxVersion: 4.0
OData-Version: 4.0
{
"name": "Contoso Inc",
"telephone1": "555-5555",
"[email protected]": "https://[Organization URI]/api/data/v9.0/contacts(60f77a42-5f0e-e611-80e0-
00155da84c03)"
}
Response
Console output
2. Retrieve the primary contact for the account Contoso, Ltd., again using $expand with the primarycontactid
single-valued navigation property to access the associated contact EntityType record.
Request
Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
{
"@odata.context":"https://[Organization
URI]/api/data/v9.0/$metadata#accounts(name,primarycontactid,primarycontactid(fullname,jobtitle,annualincome)
)/$entity",
"@odata.etag":"W/\"628886\"",
"name":"Contoso Inc",
"accountid":"65f77a42-5f0e-e611-80e0-00155da84c03",
"primarycontactid":{
"@odata.etag":"W/\"628885\"",
"fullname":"Peter Cambel",
"jobtitle":"Senior Developer",
"annualincome":95000.0000,
"_transactioncurrencyid_value":"0d4ed62e-95f7-e511-80d1-00155da84c03",
"contactid":"60f77a42-5f0e-e611-80e0-00155da84c03"
}
}
Console output
Account 'Contoso Inc' has primary contact 'Peter Cambel':
Job title: Senior Developer
Income: 95000
Response
Console output
Account 'Fourth Coffee' created.
2. Selectively retrieve the newly created Fourth Coffee account and its primary contact. An expansion is
performed on the single-valued navigation property primarycontactid .
Request
Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
{
"@odata.context":"https://[Organization
URI]/api/data/v9.0/$metadata#accounts(name,primarycontactid,primarycontactid(fullname,jobtitle,annualincome)
)/$entity",
"@odata.etag":"W/\"628902\"",
"name":"Fourth Coffee",
"accountid":"6af77a42-5f0e-e611-80e0-00155da84c03",
"primarycontactid":{
"@odata.etag":"W/\"628892\"",
"fullname":"Susie Curtis",
"jobtitle":"Coffee Master",
"annualincome":48000.0000,
"_transactioncurrencyid_value":"0d4ed62e-95f7-e511-80d1-00155da84c03",
"contactid":"6bf77a42-5f0e-e611-80e0-00155da84c03"
}
}
Console output
3. Selectively retrieve the tasks associated with the primary contact retrieved in the previous operation. An
expansion is performed on the collection-valued navigation property Contact_Tasks .
Request
Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
{
"@odata.context":"https://[Organization
URI]/api/data/v9.0/$metadata#contacts(fullname,Contact_Tasks,Contact_Tasks(subject,description,scheduledstar
t,scheduledend))/$entity",
"@odata.etag":"W/\"628892\"",
"fullname":"Susie Curtis",
"contactid":"6bf77a42-5f0e-e611-80e0-00155da84c03",
"Contact_Tasks":[
{
"@odata.etag":"W/\"628903\"",
"subject":"Sign invoice",
"description":"Invoice #12321",
"scheduledstart":"2016-04-19T00:00:00Z",
"scheduledend":"2016-04-19T00:00:00Z",
"activityid":"6cf77a42-5f0e-e611-80e0-00155da84c03"
},
{
"@odata.etag":"W/\"628905\"",
"subject":"Setup new display",
"description":"Theme is - Spring is in the air",
"scheduledstart":"2016-04-20T00:00:00Z",
"scheduledend":"2016-04-20T00:00:00Z",
"activityid":"6df77a42-5f0e-e611-80e0-00155da84c03"
},
{
"@odata.etag":"W/\"628907\"",
"subject":"Conduct training",
"description":"Train team on making our new blended coffee",
"scheduledstart":"2016-06-01T00:00:00Z",
"scheduledend":"2016-06-01T00:00:00Z",
"activityid":"6ef77a42-5f0e-e611-80e0-00155da84c03"
}
]
}
Console output
Request
Response
Console output
2. Confirm the previous operation by retrieving the collection of contacts for the account Fourth Coffee. The
response contains the array with a single element, the recently assigned contact Peter Cambel.
Request
Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
{
"@odata.context":"https://[Organization
URI]/api/data/v9.0/$metadata#contacts(fullname,jobtitle)","value":[
{
"@odata.etag":"W/\"632481\"","fullname":"Peter Cambel","jobtitle":"Senior
Developer","contactid":"00b6e0e2-b010-e611-80e1-00155da84c03"
}
]
}
Console output
3. Remove the association that was just created between account Fourth Coffee and contact Peter Cambel.
Request
Response
Console output
Request
Response
Console output
Response
HTTP/1.1 204 No Content
OData-Version: 4.0
OData-EntityId: https://[Organization URI]/api/data/v9.0/opportunities(7cf77a42-5f0e-e611-80e0-00155da84c03)
Console output
6. Associate this new opportunity to this new competitor. Note that the same general syntax is used in this
many-to-many association as was used in the previous one-to-many association.
Request
Response
Console output
7. Selectively retrieve all the opportunities associated with the competitor Adventure Works. An array is
returned containing a single opportunity.
Request
Response
HTTP/1.1 200 OK
{
"@odata.context":"https://[Organization
URI]/api/data/v9.0/$metadata#competitors(name,opportunitycompetitors_association,opportunitycompetitors_asso
ciation(name,description))/$entity",
"@odata.etag":"W/\"628913\"",
"name":"Adventure Works",
"competitorid":"77f77a42-5f0e-e611-80e0-00155da84c03",
"opportunitycompetitors_association":[
{
"@odata.etag":"W/\"628917\"",
"name":"River rafting adventure",
"description":"Sales team on a river-rafting offsite and team building",
"opportunityid":"7cf77a42-5f0e-e611-80e0-00155da84c03"
}
]
}
Console output
8. Dissociate the opportunity from the competitor. Note again, that this has the same general syntax used to
remove a one-to-many association.
Request
Response
Console output
See also
Use the Dataverse Web API
Create a table row using the Web API
Retrieve a table row using the Web API
Update and delete table rows using the Web API
[Associate and disassociate table rows using the Web API](associate-disassociate-table rows-using-web-api.md)
Web API Basic Operations Sample (C#)
Web API Query Data Sample
6/18/2021 • 29 minutes to read • Edit Online
NOTE
Unsure about entity vs. table? See Developers: Understand terminology in Microsoft Dataverse.
This group of samples demonstrate how to query data using the Microsoft Dataverse Web API. This sample is
implemented as a separate project for the following languages:
Web API Query Data Sample (Client-side JavaScript)
Query Data Sample (C#)
This topic describes a common set of operations implemented by each sample in this group. This topic describes
the HTTP requests and responses and text output that each sample in this group will perform without the
language specific details. See the language specific descriptions and the individual samples for details about
how this operations are performed.
Demonstrates
This sample is divided into the following principal sections, containing Web API query data operations which are
discussed in greater detail in the associated conceptual topics.
Filter results
userquery EntityType/
savedquery EntityType/
The following sections contain a brief discussion of the Dataverse Web API operations performed, along with the
corresponding HTTP messages and associated console output.
Sample data
To ensure the queries in this sample work properly, a standard set of sample rows is created by this sample.
These sample rows will be deleted unless the user chooses to not delete them. This is the data the sample will be
querying. You may get different results depending on any existing data in your environment.
The data is added using deep insert in a single POST request and matches the following structure:
{
"name": "Contoso, Ltd. (sample)",
"primarycontactid": {
"firstname": "Yvonne", "lastname": "McKay (sample)", "jobtitle": "Coffee Master",
"annualincome": 45000, "Contact_Tasks": [
{ "subject": "Task 1", "description": "Task 1 description" },
{ "subject": "Task 2", "description": "Task 2 description" },
{ "subject": "Task 3", "description": "Task 3 description" }
]
},
"Account_Tasks": [
{ "subject": "Task 1", "description": "Task 1 description" },
{ "subject": "Task 2", "description": "Task 2 description" },
{ "subject": "Task 3", "description": "Task 3 description" }
],
"contact_customer_accounts": [
{
"firstname": "Susanna", "lastname": "Stubberod (sample)", "jobtitle": "Senior Purchaser",
"annualincome": 52000, "Contact_Tasks": [
{ "subject": "Task 1", "description": "Task 1 description" },
{ "subject": "Task 2", "description": "Task 2 description" },
{ "subject": "Task 3", "description": "Task 3 description" }
]
},
{
"firstname": "Nancy", "lastname": "Anderson (sample)", "jobtitle": "Activities Manager",
"annualincome": 55500, "Contact_Tasks": [
{ "subject": "Task 1", "description": "Task 1 description" },
{ "subject": "Task 2", "description": "Task 2 description" },
{ "subject": "Task 3", "description": "Task 3 description" }
]
},
{
"firstname": "Maria", "lastname": "Cambell (sample)", "jobtitle": "Accounts Manager",
"annualincome": 31000, "Contact_Tasks": [
{ "subject": "Task 1", "description": "Task 1 description" },
{ "subject": "Task 2", "description": "Task 2 description" },
{ "subject": "Task 3", "description": "Task 3 description" }
]
},
{
{
"firstname": "Nancy", "lastname": "Anderson (sample)", "jobtitle": "Logistics Specialist",
"annualincome": 63500, "Contact_Tasks": [
{ "subject": "Task 1", "description": "Task 1 description" },
{ "subject": "Task 2", "description": "Task 2 description" },
{ "subject": "Task 3", "description": "Task 3 description" }
]
},
{
"firstname": "Scott", "lastname": "Konersmann (sample)", "jobtitle": "Accounts Manager",
"annualincome": 38000, "Contact_Tasks": [
{ "subject": "Task 1", "description": "Task 1 description" },
{ "subject": "Task 2", "description": "Task 2 description" },
{ "subject": "Task 3", "description": "Task 3 description" }
]
},
{
"firstname": "Robert", "lastname": "Lyon (sample)", "jobtitle": "Senior Technician",
"annualincome": 78000, "Contact_Tasks": [
{ "subject": "Task 1", "description": "Task 1 description" },
{ "subject": "Task 2", "description": "Task 2 description" },
{ "subject": "Task 3", "description": "Task 3 description" }
]
},
{
"firstname": "Paul", "lastname": "Cannon (sample)", "jobtitle": "Ski Instructor",
"annualincome": 68500, "Contact_Tasks": [
{ "subject": "Task 1", "description": "Task 1 description" },
{ "subject": "Task 2", "description": "Task 2 description" },
{ "subject": "Task 3", "description": "Task 3 description" }
]
},
{
"firstname": "Rene", "lastname": "Valdes (sample)", "jobtitle": "Data Analyst III",
"annualincome": 86000, "Contact_Tasks": [
{ "subject": "Task 1", "description": "Task 1 description" },
{ "subject": "Task 2", "description": "Task 2 description" },
{ "subject": "Task 3", "description": "Task 3 description" }
]
},
{
"firstname": "Jim", "lastname": "Glynn (sample)", "jobtitle": "Senior International Sales
Manager",
"annualincome": 81400, "Contact_Tasks": [
{ "subject": "Task 1", "description": "Task 1 description" },
{ "subject": "Task 2", "description": "Task 2 description" },
{ "subject": "Task 3", "description": "Task 3 description" }
]
}
]
}
In this example, we are requesting for a specific contact. In this case, it's the primary contact of the account,
Yvonne McKay (sample) .
HTTP Request
GET https://[Organization URI]/api/data/v9.0/contacts(b848fdee-c143-e611-80d5-00155da84802)?
$select=fullname,jobtitle,annualincome HTTP/1.1
OData-MaxVersion: 4.0
OData-Version: 4.0
Content-Type: application/json; charset=utf-8
Prefer: odata.maxpagesize=10, odata.include-annotations=OData.Community.Display.V1.FormattedValue
HTTP Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
Preference-Applied: odata.include-annotations="OData.Community.Display.V1.FormattedValue"
Preference-Applied: odata.maxpagesize=10
Content-Length: 517
{
"@odata.context":"https://[Organization
URI]/api/data/v9.0/$metadata#contacts(fullname,jobtitle,annualincome)/$entity",
"@odata.etag":"W/\"619718\"",
"fullname":"Yvonne McKay (sample)",
"jobtitle":"Coffee Master",
"[email protected]":"$45,000.00",
"annualincome":45000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"15c364b2-bf43-e611-80d5-00155da84802"
}
Console output
HTTP Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
Preference-Applied: odata.include-annotations="OData.Community.Display.V1.FormattedValue"
Preference-Applied: odata.maxpagesize=10
Content-Length: 4284
{
"@odata.context":"https://[Organization
URI]/api/data/v9.0/$metadata#contacts(fullname,jobtitle,annualincome)",
"value":[
{
"@odata.etag":"W/\"619718\"",
"fullname":"Yvonne McKay (sample)",
"jobtitle":"Coffee Master",
"[email protected]":"$45,000.00",
"annualincome":45000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"15c364b2-bf43-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"619839\"",
"fullname":"Susanna Stubberod (sample)",
"jobtitle":"Senior Purchaser",
"[email protected]":"$52,000.00",
"annualincome":52000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"1cc364b2-bf43-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"619841\"",
"fullname":"Nancy Anderson (sample)",
"jobtitle":"Activities Manager",
"[email protected]":"$55,500.00",
"annualincome":55500.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"20c364b2-bf43-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"619843\"",
"fullname":"Maria Cambell (sample)",
"jobtitle":"Accounts Manager",
"[email protected]":"$31,000.00",
"annualincome":31000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"24c364b2-bf43-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"619845\"",
"fullname":"Nancy Anderson (sample)",
"jobtitle":"Logistics Specialist",
"[email protected]":"$63,500.00",
"annualincome":63500.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"28c364b2-bf43-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"619847\"",
"fullname":"Scott Konersmann (sample)",
"jobtitle":"Accounts Manager",
"[email protected]":"$38,000.00",
"annualincome":38000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"2cc364b2-bf43-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"619849\"",
"fullname":"Robert Lyon (sample)",
"jobtitle":"Senior Technician",
"[email protected]":"$78,000.00",
"annualincome":78000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"30c364b2-bf43-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"619851\"",
"fullname":"Paul Cannon (sample)",
"jobtitle":"Ski Instructor",
"[email protected]":"$68,500.00",
"annualincome":68500.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"34c364b2-bf43-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"619853\"",
"fullname":"Rene Valdes (sample)",
"jobtitle":"Data Analyst III",
"[email protected]":"$86,000.00",
"annualincome":86000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"38c364b2-bf43-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"619855\"",
"fullname":"Jim Glynn (sample)",
"jobtitle":"Senior International Sales Manager",
"[email protected]":"$81,400.00",
"annualincome":81400.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"3cc364b2-bf43-e611-80d5-00155da84802"
}
]
}
Console output
Contacts filtered by fullname containing '(sample)':
1) Yvonne McKay (sample), Coffee Master, $45,000.00
2) Susanna Stubberod (sample), Senior Purchaser, $52,000.00
3) Nancy Anderson (sample), Activities Manager, $55,500.00
4) Maria Cambell (sample), Accounts Manager, $31,000.00
5) Nancy Anderson (sample), Logistics Specialist, $63,500.00
6) Scott Konersmann (sample), Accounts Manager, $38,000.00
7) Robert Lyon (sample), Senior Technician, $78,000.00
8) Paul Cannon (sample), Ski Instructor, $68,500.00
9) Rene Valdes (sample), Data Analyst III, $86,000.00
10) Jim Glynn (sample), Senior International Sales Manager, $81,400.00
HTTP Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
Preference-Applied: odata.include-annotations="OData.Community.Display.V1.FormattedValue"
Preference-Applied: odata.maxpagesize=10
Content-Length: 4284
{
"@odata.context":"https://[Organization
URI]/api/data/v9.0/$metadata#contacts(fullname,jobtitle,annualincome)",
"value":[
{
"@odata.etag":"W/\"619718\"",
"fullname":"Yvonne McKay (sample)",
"jobtitle":"Coffee Master",
"[email protected]":"$45,000.00",
"annualincome":45000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"15c364b2-bf43-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"619839\"",
"fullname":"Susanna Stubberod (sample)",
"jobtitle":"Senior Purchaser",
"[email protected]":"$52,000.00",
"annualincome":52000.0000,
"annualincome":52000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"1cc364b2-bf43-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"619841\"",
"fullname":"Nancy Anderson (sample)",
"jobtitle":"Activities Manager",
"[email protected]":"$55,500.00",
"annualincome":55500.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"20c364b2-bf43-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"619843\"",
"fullname":"Maria Cambell (sample)",
"jobtitle":"Accounts Manager",
"[email protected]":"$31,000.00",
"annualincome":31000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"24c364b2-bf43-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"619845\"",
"fullname":"Nancy Anderson (sample)",
"jobtitle":"Logistics Specialist",
"[email protected]":"$63,500.00",
"annualincome":63500.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"28c364b2-bf43-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"619847\"",
"fullname":"Scott Konersmann (sample)",
"jobtitle":"Accounts Manager",
"[email protected]":"$38,000.00",
"annualincome":38000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"2cc364b2-bf43-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"619849\"",
"fullname":"Robert Lyon (sample)",
"jobtitle":"Senior Technician",
"[email protected]":"$78,000.00",
"annualincome":78000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"30c364b2-bf43-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"619851\"",
"fullname":"Paul Cannon (sample)",
"jobtitle":"Ski Instructor",
"[email protected]":"$68,500.00",
"annualincome":68500.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"34c364b2-bf43-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"619853\"",
"fullname":"Rene Valdes (sample)",
"jobtitle":"Data Analyst III",
"[email protected]":"$86,000.00",
"[email protected]":"$86,000.00",
"annualincome":86000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"38c364b2-bf43-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"619855\"",
"fullname":"Jim Glynn (sample)",
"jobtitle":"Senior International Sales Manager",
"[email protected]":"$81,400.00",
"annualincome":81400.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"3cc364b2-bf43-e611-80d5-00155da84802"
}
]
}
Console output
Using operators
Use the Standard filter operators ( eq , ne , gt , ge , lt , le , and , or , not ) to further refine our results. In this
example, we are requesting a list of all contacts with fullname containing (sample) and annual income greater
than 55000 .
HTTP Request
HTTP Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
Preference-Applied: odata.include-annotations="OData.Community.Display.V1.FormattedValue"
Preference-Applied: odata.maxpagesize=10
Content-Length: 2629
{
"@odata.context":"https://[Organization
URI]/api/data/v9.0/$metadata#contacts(fullname,jobtitle,annualincome)",
"value":[
"value":[
{
"@odata.etag":"W/\"619841\"",
"fullname":"Nancy Anderson (sample)",
"jobtitle":"Activities Manager",
"[email protected]":"$55,500.00",
"annualincome":55500.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"20c364b2-bf43-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"619845\"",
"fullname":"Nancy Anderson (sample)",
"jobtitle":"Logistics Specialist",
"[email protected]":"$63,500.00",
"annualincome":63500.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"28c364b2-bf43-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"619849\"",
"fullname":"Robert Lyon (sample)",
"jobtitle":"Senior Technician",
"[email protected]":"$78,000.00",
"annualincome":78000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"30c364b2-bf43-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"619851\"",
"fullname":"Paul Cannon (sample)",
"jobtitle":"Ski Instructor",
"[email protected]":"$68,500.00",
"annualincome":68500.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"34c364b2-bf43-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"619853\"",
"fullname":"Rene Valdes (sample)",
"jobtitle":"Data Analyst III",
"[email protected]":"$86,000.00",
"annualincome":86000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"38c364b2-bf43-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"619855\"",
"fullname":"Jim Glynn (sample)",
"jobtitle":"Senior International Sales Manager",
"[email protected]":"$81,400.00",
"annualincome":81400.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"3cc364b2-bf43-e611-80d5-00155da84802"
}
]
}
Console output
Contacts filtered by fullname and annualincome (<$55,000):
1) Nancy Anderson (sample), Activities Manager, $55,500.00
2) Nancy Anderson (sample), Logistics Specialist, $63,500.00
3) Robert Lyon (sample), Senior Technician, $78,000.00
4) Paul Cannon (sample), Ski Instructor, $68,500.00
5) Rene Valdes (sample), Data Analyst III, $86,000.00
6) Jim Glynn (sample), Senior International Sales Manager, $81,400.00
Setting precedence
You will use parentheses to establish the order in which your conditions are evaluated.
In this example, we are requesting a list of all contacts with fullname containing (sample) , jobtitle
containing either senior or specialist , and annualincome greater than 55000 . To get the results we want,
parentheses are used to group the jobtitle filters together. Since all operators have the same precedence,
omitting the parentheses will give the or operator the same precedence as the and operators. Filters are
applied from left to right. The order in which these statements appear in the filter can affect the results. This is
what the query in this example looks like:
$filter=contains(fullname,'(sample)') and (contains(jobtitle,'senior') or contains(jobtitle,'specialist'))
and annualincome gt 55000
.
HTTP Request
HTTP Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
Preference-Applied: odata.include-annotations="OData.Community.Display.V1.FormattedValue"
Preference-Applied: odata.maxpagesize=10
Content-Length: 1393
{
"@odata.context":"https://[Organization
URI]/api/data/v9.0/$metadata#contacts(fullname,jobtitle,annualincome)",
"value":[
{
"@odata.etag":"W/\"619845\"",
"fullname":"Nancy Anderson (sample)",
"jobtitle":"Logistics Specialist",
"[email protected]":"$63,500.00",
"annualincome":63500.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"28c364b2-bf43-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"619849\"",
"fullname":"Robert Lyon (sample)",
"jobtitle":"Senior Technician",
"[email protected]":"$78,000.00",
"annualincome":78000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"30c364b2-bf43-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"619855\"",
"fullname":"Jim Glynn (sample)",
"jobtitle":"Senior International Sales Manager",
"[email protected]":"$81,400.00",
"annualincome":81400.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"3cc364b2-bf43-e611-80d5-00155da84802"
}
]
}
Console output
Ordering results
You can specify either an ascending or descending order on the results by using the $orderby filter option . In
this example, we will query for all contacts with fullname containing (sample) and request the data in
ascending order based on the jobtitle property value and then in descending based on the annualincome
property value using this syntax: $orderby=jobtitle asc, annualincome desc . More information:Order results.
HTTP Request
GET https://[Organization URI]/api/data/v9.0/contacts?
$select=fullname,jobtitle,annualincome&$filter=contains(fullname,'(sample)')%20&$orderby=jobtitle%20asc,%20a
nnualincome%20desc HTTP/1.1
OData-MaxVersion: 4.0
OData-Version: 4.0
Content-Type: application/json; charset=utf-8
Prefer: odata.maxpagesize=10, odata.include-annotations=OData.Community.Display.V1.FormattedValue
HTTP Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
Preference-Applied: odata.include-annotations="OData.Community.Display.V1.FormattedValue"
Preference-Applied: odata.maxpagesize=10
Content-Length: 4284
{
"@odata.context":"https://[Organization
URI]/api/data/v9.0/$metadata#contacts(fullname,jobtitle,annualincome)",
"value":[
{
"@odata.etag":"W/\"619847\"",
"fullname":"Scott Konersmann (sample)",
"jobtitle":"Accounts Manager",
"[email protected]":"$38,000.00",
"annualincome":38000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"2cc364b2-bf43-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"619843\"",
"fullname":"Maria Cambell (sample)",
"jobtitle":"Accounts Manager",
"[email protected]":"$31,000.00",
"annualincome":31000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"24c364b2-bf43-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"619841\"",
"fullname":"Nancy Anderson (sample)",
"jobtitle":"Activities Manager",
"[email protected]":"$55,500.00",
"annualincome":55500.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"20c364b2-bf43-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"619718\"",
"fullname":"Yvonne McKay (sample)",
"jobtitle":"Coffee Master",
"[email protected]":"$45,000.00",
"annualincome":45000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"15c364b2-bf43-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"619853\"",
"fullname":"Rene Valdes (sample)",
"jobtitle":"Data Analyst III",
"[email protected]":"$86,000.00",
"annualincome":86000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"38c364b2-bf43-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"619845\"",
"fullname":"Nancy Anderson (sample)",
"jobtitle":"Logistics Specialist",
"[email protected]":"$63,500.00",
"annualincome":63500.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"28c364b2-bf43-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"619855\"",
"fullname":"Jim Glynn (sample)",
"jobtitle":"Senior International Sales Manager",
"[email protected]":"$81,400.00",
"annualincome":81400.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"3cc364b2-bf43-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"619839\"",
"fullname":"Susanna Stubberod (sample)",
"jobtitle":"Senior Purchaser",
"[email protected]":"$52,000.00",
"annualincome":52000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"1cc364b2-bf43-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"619849\"",
"fullname":"Robert Lyon (sample)",
"jobtitle":"Senior Technician",
"[email protected]":"$78,000.00",
"annualincome":78000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"30c364b2-bf43-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"619851\"",
"fullname":"Paul Cannon (sample)",
"jobtitle":"Ski Instructor",
"[email protected]":"$68,500.00",
"annualincome":68500.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"34c364b2-bf43-e611-80d5-00155da84802"
}
]
}
Console output
Contacts ordered by jobtitle (ascending) and annualincome (descending):
1) Scott Konersmann (sample), Accounts Manager, $38,000.00
2) Maria Cambell (sample), Accounts Manager, $31,000.00
3) Nancy Anderson (sample), Activities Manager, $55,500.00
4) Yvonne McKay (sample), Coffee Master, $45,000.00
5) Rene Valdes (sample), Data Analyst III, $86,000.00
6) Nancy Anderson (sample), Logistics Specialist, $63,500.00
7) Jim Glynn (sample), Senior International Sales Manager, $81,400.00
8) Susanna Stubberod (sample), Senior Purchaser, $52,000.00
9) Robert Lyon (sample), Senior Technician, $78,000.00
10) Paul Cannon (sample), Ski Instructor, $68,500.00
Parameter alias
Use parameter aliases to more easily reuse parameters in your filters. Parameterized aliases can be used in
$filter and $orderby options. If the alias isn’t assigned a value it is assumed to be null. You can also use
parameter aliases when calling functions. More information:Use Web API functions, Use parameter aliases with
system query options. Taking the order results operation for example, we can write that query again using
parameters and we would get the same output results.
HTTP Request
HTTP Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
Preference-Applied: odata.include-annotations="OData.Community.Display.V1.FormattedValue"
Preference-Applied: odata.maxpagesize=10
Content-Length: 4284
{
"@odata.context":"https://[Organization
URI]/api/data/v9.0/$metadata#contacts(fullname,jobtitle,annualincome)",
"value":[
{
"@odata.etag":"W/\"619847\"",
"fullname":"Scott Konersmann (sample)",
"jobtitle":"Accounts Manager",
"[email protected]":"$38,000.00",
"annualincome":38000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"2cc364b2-bf43-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"619843\"",
"fullname":"Maria Cambell (sample)",
"jobtitle":"Accounts Manager",
"[email protected]":"$31,000.00",
"annualincome":31000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"24c364b2-bf43-e611-80d5-00155da84802"
"contactid":"24c364b2-bf43-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"619841\"",
"fullname":"Nancy Anderson (sample)",
"jobtitle":"Activities Manager",
"[email protected]":"$55,500.00",
"annualincome":55500.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"20c364b2-bf43-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"619718\"",
"fullname":"Yvonne McKay (sample)",
"jobtitle":"Coffee Master",
"[email protected]":"$45,000.00",
"annualincome":45000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"15c364b2-bf43-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"619853\"",
"fullname":"Rene Valdes (sample)",
"jobtitle":"Data Analyst III",
"[email protected]":"$86,000.00",
"annualincome":86000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"38c364b2-bf43-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"619845\"",
"fullname":"Nancy Anderson (sample)",
"jobtitle":"Logistics Specialist",
"[email protected]":"$63,500.00",
"annualincome":63500.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"28c364b2-bf43-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"619855\"",
"fullname":"Jim Glynn (sample)",
"jobtitle":"Senior International Sales Manager",
"[email protected]":"$81,400.00",
"annualincome":81400.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"3cc364b2-bf43-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"619839\"",
"fullname":"Susanna Stubberod (sample)",
"jobtitle":"Senior Purchaser",
"[email protected]":"$52,000.00",
"annualincome":52000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"1cc364b2-bf43-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"619849\"",
"fullname":"Robert Lyon (sample)",
"jobtitle":"Senior Technician",
"[email protected]":"$78,000.00",
"annualincome":78000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"30c364b2-bf43-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"619851\"",
"fullname":"Paul Cannon (sample)",
"jobtitle":"Ski Instructor",
"[email protected]":"$68,500.00",
"annualincome":68500.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"34c364b2-bf43-e611-80d5-00155da84802"
}
]
}
Console output
Limit results
Returning more data than you need is bad for performance. The server will return a maximum of 5000 table
rows per request. You can limit the number of results returned using the $top query option or by adding
odata.maxpagesize in the request header. The $top query option only returns the top number of rows from the
result set and ignores the rest. The odata.maxpagesize request header specifies the number of rows returned per
page with an @odata.nextLink property to get results of the next page. For more information about
odata.maxpagesize , see the section on Pagination and see also Limits on number of rows returned.
Top results
We can apply the $top query option to limit the basic query operation to the first five contacts with fullname
containing (sample) . In this case, the request actually produces at least 10 results, but only the first 5 entries are
returned in the response.
HTTP Request
HTTP Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
Preference-Applied: odata.include-annotations="OData.Community.Display.V1.FormattedValue"
Content-Length: 2209
{
"@odata.context":"https://[Organization
URI]/api/data/v9.0/$metadata#contacts(fullname,jobtitle,annualincome)",
"value":[
{
"@odata.etag":"W/\"619718\"",
"fullname":"Yvonne McKay (sample)",
"jobtitle":"Coffee Master",
"[email protected]":"$45,000.00",
"annualincome":45000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"15c364b2-bf43-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"619839\"",
"fullname":"Susanna Stubberod (sample)",
"jobtitle":"Senior Purchaser",
"[email protected]":"$52,000.00",
"annualincome":52000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"1cc364b2-bf43-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"619841\"",
"fullname":"Nancy Anderson (sample)",
"jobtitle":"Activities Manager",
"[email protected]":"$55,500.00",
"annualincome":55500.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"20c364b2-bf43-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"619843\"",
"fullname":"Maria Cambell (sample)",
"jobtitle":"Accounts Manager",
"[email protected]":"$31,000.00",
"annualincome":31000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"24c364b2-bf43-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"619845\"",
"fullname":"Nancy Anderson (sample)",
"jobtitle":"Logistics Specialist",
"[email protected]":"$63,500.00",
"annualincome":63500.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"28c364b2-bf43-e611-80d5-00155da84802"
}
]
}
Console output
Contacts top 5 results:
1) Yvonne McKay (sample), Coffee Master, $45,000.00
2) Susanna Stubberod (sample), Senior Purchaser, $52,000.00
3) Nancy Anderson (sample), Activities Manager, $55,500.00
4) Maria Cambell (sample), Accounts Manager, $31,000.00
5) Nancy Anderson (sample), Logistics Specialist, $63,500.00
Result count
You can get just the count of rows from a collection-valued property or a count of matched table rows in a filter.
Getting a count tells us the number of possible rows in our result. However, the Dataverse server will return
5000 as the maximum count even if the result may have more. In this example, we constructed a filter with
jobtitle containing either Senior or Manager and we also requested a $count of the result. The response
contains the count in the @odata.count property as well as the results of the query. More information:Retrieve a
count of table rows.
HTTP Request
HTTP Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
Preference-Applied: odata.include-annotations="OData.Community.Display.V1.FormattedValue"
Preference-Applied: odata.maxpagesize=10
Content-Length: 2654
{
"@odata.context":"https://[Organization
URI]/api/data/v9.0/$metadata#contacts(fullname,jobtitle,annualincome)",
"@odata.count":6,
"value":[
{
"@odata.etag":"W/\"620258\"",
"fullname":"Susanna Stubberod (sample)",
"jobtitle":"Senior Purchaser",
"[email protected]":"$52,000.00",
"annualincome":52000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"bf48fdee-c143-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"620260\"",
"fullname":"Nancy Anderson (sample)",
"jobtitle":"Activities Manager",
"[email protected]":"$55,500.00",
"annualincome":55500.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"c348fdee-c143-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"620262\"",
"@odata.etag":"W/\"620262\"",
"fullname":"Maria Cambell (sample)",
"jobtitle":"Accounts Manager",
"[email protected]":"$31,000.00",
"annualincome":31000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"c748fdee-c143-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"620266\"",
"fullname":"Scott Konersmann (sample)",
"jobtitle":"Accounts Manager",
"[email protected]":"$38,000.00",
"annualincome":38000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"cf48fdee-c143-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"620268\"",
"fullname":"Robert Lyon (sample)",
"jobtitle":"Senior Technician",
"[email protected]":"$78,000.00",
"annualincome":78000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"d348fdee-c143-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"620274\"",
"fullname":"Jim Glynn (sample)",
"jobtitle":"Senior International Sales Manager",
"[email protected]":"$81,400.00",
"annualincome":81400.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"df48fdee-c143-e611-80d5-00155da84802"
}
]
}
Console output
Pagination
To retrieve a sequential subset of results for a query that returns a large number of rows, use the
odata.maxpagesize instead of $top . More information:Specify the number of rows to return in a page.
In this example, we ask for a $count and we set the odata.maxpagesize to 4 . This filter matches 10 contacts,
but we are only retrieving 4 at a time. We also use the count and the max page size to figured out how many
total pages there are. The result of the first page is returned in this request.
HTTP Request
GET https://[Organization URI]/api/data/v9.0/contacts?
$select=fullname,jobtitle,annualincome&$filter=contains(fullname,'(sample)')&$count=true HTTP/1.1
OData-MaxVersion: 4.0
OData-Version: 4.0
Content-Type: application/json; charset=utf-8
Prefer: odata.maxpagesize=4, odata.include-annotations=OData.Community.Display.V1.FormattedValue
HTTP Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
Preference-Applied: odata.include-annotations="OData.Community.Display.V1.FormattedValue"
Preference-Applied: odata.maxpagesize=4
Content-Length: 2294
{
"@odata.context":"https://[Organization
URI]/api/data/v9.0/$metadata#contacts(fullname,jobtitle,annualincome)",
"@odata.count":10,
"value":[
{
"@odata.etag":"W/\"620138\"",
"fullname":"Yvonne McKay (sample)",
"jobtitle":"Coffee Master",
"[email protected]":"$45,000.00",
"annualincome":45000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"b848fdee-c143-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"620258\"",
"fullname":"Susanna Stubberod (sample)",
"jobtitle":"Senior Purchaser",
"[email protected]":"$52,000.00",
"annualincome":52000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"bf48fdee-c143-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"620260\"",
"fullname":"Nancy Anderson (sample)",
"jobtitle":"Activities Manager",
"[email protected]":"$55,500.00",
"annualincome":55500.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"c348fdee-c143-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"620262\"",
"fullname":"Maria Cambell (sample)",
"jobtitle":"Accounts Manager",
"[email protected]":"$31,000.00",
"annualincome":31000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"c748fdee-c143-e611-80d5-00155da84802"
}
],
"@odata.nextLink":"https://[Organization URI]/api/data/v9.0/contacts?
$select=fullname,jobtitle,annualincome&$filter=contains(fullname,'(sample)')&$count=true&$skiptoken=%3Ccooki
e%20pagenumber=%222%22%20pagingcookie=%22%253ccookie%2520page%253d%25221%2522%253e%253ccontactid%2520last%25
3d%2522%257bC748FDEE-C143-E611-80D5-00155DA84802%257d%2522%2520first%253d%2522%257bB848FDEE-C143-E611-80D5-
00155DA84802%257d%2522%2520%252f%253e%253c%252fcookie%253e%22%20istracking=%22False%22%20/%3E"
}
Console output
Contacts total: 10 Contacts per page: 4.
Page 1 of 3:
1) Yvonne McKay (sample), Coffee Master, $45,000.00
2) Susanna Stubberod (sample), Senior Purchaser, $52,000.00
3) Nancy Anderson (sample), Activities Manager, $55,500.00
4) Maria Cambell (sample), Accounts Manager, $31,000.00
To retrieve page 2, use a GET request with the value of the @odata.nextLink property.
HTTP Request
HTTP Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
Preference-Applied: odata.include-annotations="OData.Community.Display.V1.FormattedValue"
Preference-Applied: odata.maxpagesize=4
Content-Length: 2294
{
"@odata.context":"https://[Organization
URI]/api/data/v9.0/$metadata#contacts(fullname,jobtitle,annualincome)",
"@odata.count":10,
"value":[
{
"@odata.etag":"W/\"620264\"",
"fullname":"Nancy Anderson (sample)",
"jobtitle":"Logistics Specialist",
"[email protected]":"$63,500.00",
"annualincome":63500.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"cb48fdee-c143-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"620266\"",
"fullname":"Scott Konersmann (sample)",
"jobtitle":"Accounts Manager",
"[email protected]":"$38,000.00",
"annualincome":38000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"cf48fdee-c143-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"620268\"",
"fullname":"Robert Lyon (sample)",
"jobtitle":"Senior Technician",
"[email protected]":"$78,000.00",
"annualincome":78000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"d348fdee-c143-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"620270\"",
"fullname":"Paul Cannon (sample)",
"jobtitle":"Ski Instructor",
"[email protected]":"$68,500.00",
"annualincome":68500.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"d748fdee-c143-e611-80d5-00155da84802"
}
],
"@odata.nextLink":"https://[Organization URI]/api/data/v9.0/contacts?
$select=fullname,jobtitle,annualincome&$filter=contains(fullname,'(sample)')&$count=true&$skiptoken=%3Ccooki
e%20pagenumber=%223%22%20pagingcookie=%22%253ccookie%2520page%253d%25222%2522%253e%253ccontactid%2520last%25
3d%2522%257bD748FDEE-C143-E611-80D5-00155DA84802%257d%2522%2520first%253d%2522%257bCB48FDEE-C143-E611-80D5-
00155DA84802%257d%2522%2520%252f%253e%253c%252fcookie%253e%22%20istracking=%22False%22%20/%3E"
}
Console output
Page 2 of 3:
1) Nancy Anderson (sample), Logistics Specialist, $63,500.00
2) Scott Konersmann (sample), Accounts Manager, $38,000.00
3) Robert Lyon (sample), Senior Technician, $78,000.00
4) Paul Cannon (sample), Ski Instructor, $68,500.00
Expanding results
To retrieve information on associated table rows, use the $expand query option on navigation properties. More
information:Retrieve related rows by expanding navigation properties.
Expand on single -valued navigation property
A Single-valued navigation property represents a many-to-one relationships. In our sample data, the account
has a relationship with a contact via the primarycontactid column (attribute). In this relationship, the account
can only have one primary contact. Using the account EntityType, we can create a query to get information
about the account and expanded information about its primary contact.
HTTP Request
HTTP Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
Preference-Applied: odata.include-annotations="OData.Community.Display.V1.FormattedValue"
Preference-Applied: odata.maxpagesize=10
Content-Length: 700
{
"@odata.context":"https://[Organization
URI]/api/data/v9.0/$metadata#accounts(name,primarycontactid,primarycontactid(fullname,jobtitle,annualincome)
)/$entity",
"@odata.etag":"W/\"620641\"",
"name":"Contoso, Ltd. (sample)",
"accountid":"b2546951-c543-e611-80d5-00155da84802",
"primarycontactid":{
"@odata.etag":"W/\"620534\"",
"fullname":"Yvonne McKay (sample)",
"jobtitle":"Coffee Master",
"[email protected]":"$45,000.00",
"annualincome":45000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"b3546951-c543-e611-80d5-00155da84802"
}
}
Console output
Account 'Contoso, Ltd. (sample)' has the following primary contact person:
Fullname: 'Yvonne McKay (sample)'
Jobtitle: 'Coffee Master'
Annualincome: '45000'
HTTP Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
Preference-Applied: odata.include-annotations="OData.Community.Display.V1.FormattedValue"
Preference-Applied: odata.maxpagesize=10
Content-Length: 737
{
"@odata.context":"https://[Organization
URI]/api/data/v9.0/$metadata#contacts(fullname,jobtitle,annualincome,account_primary_contact,account_primary
_contact(name))/$entity",
"@odata.etag":"W/\"620534\"",
"fullname":"Yvonne McKay (sample)",
"jobtitle":"Coffee Master",
"[email protected]":"$45,000.00",
"annualincome":45000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"b3546951-c543-e611-80d5-00155da84802",
"account_primary_contact":[
{
"@odata.etag":"W/\"620919\"",
"name":"Contoso, Ltd. (sample)",
"accountid":"b2546951-c543-e611-80d5-00155da84802"
}
]
}
Console output
Contact 'Yvonne McKay (sample)' is the primary contact for the following accounts:
1) Contoso, Ltd. (sample)
HTTP Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
Preference-Applied: odata.include-annotations="OData.Community.Display.V1.FormattedValue"
Preference-Applied: odata.maxpagesize=10
Content-Length: 4073
{
"@odata.context":"https://[Organization
URI]/api/data/v9.0/$metadata#accounts(name,contact_customer_accounts,contact_customer_accounts(fullname,jobt
itle,annualincome))/$entity",
"@odata.etag":"W/\"620921\"",
"name":"Contoso, Ltd. (sample)",
"accountid":"86546951-c543-e611-80d5-00155da84802",
"contact_customer_accounts":[
{
"@odata.etag":"W/\"620847\"",
"fullname":"Susanna Stubberod (sample)",
"jobtitle":"Senior Purchaser",
"[email protected]":"$52,000.00",
"annualincome":52000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"8e546951-c543-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"620849\"",
"fullname":"Nancy Anderson (sample)",
"jobtitle":"Activities Manager",
"[email protected]":"$55,500.00",
"annualincome":55500.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"92546951-c543-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"620851\"",
"fullname":"Maria Cambell (sample)",
"jobtitle":"Accounts Manager",
"[email protected]":"$31,000.00",
"annualincome":31000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"96546951-c543-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"620853\"",
"fullname":"Nancy Anderson (sample)",
"jobtitle":"Logistics Specialist",
"[email protected]":"$63,500.00",
"annualincome":63500.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"9a546951-c543-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"620855\"",
"fullname":"Scott Konersmann (sample)",
"jobtitle":"Accounts Manager",
"[email protected]":"$38,000.00",
"annualincome":38000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"9e546951-c543-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"620857\"",
"fullname":"Robert Lyon (sample)",
"jobtitle":"Senior Technician",
"[email protected]":"$78,000.00",
"annualincome":78000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"a2546951-c543-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"620859\"",
"fullname":"Paul Cannon (sample)",
"jobtitle":"Ski Instructor",
"[email protected]":"$68,500.00",
"annualincome":68500.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"a6546951-c543-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"620861\"",
"fullname":"Rene Valdes (sample)",
"jobtitle":"Data Analyst III",
"[email protected]":"$86,000.00",
"annualincome":86000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"aa546951-c543-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"620863\"",
"fullname":"Jim Glynn (sample)",
"jobtitle":"Senior International Sales Manager",
"[email protected]":"$81,400.00",
"annualincome":81400.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"ae546951-c543-e611-80d5-00155da84802"
}
]
]
}
Console output
HTTP Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
Preference-Applied: odata.include-annotations="OData.Community.Display.V1.FormattedValue"
Preference-Applied: odata.maxpagesize=10
Content-Length: 5093
{
"@odata.context":"https://[Organization
URI]/api/data/v9.0/$metadata#accounts(name,primarycontactid,contact_customer_accounts,Account_Tasks,primaryc
ontactid(fullname,jobtitle,annualincome),contact_customer_accounts(fullname,jobtitle,annualincome),Account_T
asks(subject,description))/$entity",
"@odata.etag":"W/\"620921\"",
"name":"Contoso, Ltd. (sample)",
"accountid":"86546951-c543-e611-80d5-00155da84802",
"primarycontactid":{
"@odata.etag":"W/\"620726\"",
"fullname":"Yvonne McKay (sample)",
"jobtitle":"Coffee Master",
"[email protected]":"$45,000.00",
"annualincome":45000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"87546951-c543-e611-80d5-00155da84802"
},
"contact_customer_accounts":[
{
"@odata.etag":"W/\"620847\"",
"fullname":"Susanna Stubberod (sample)",
"jobtitle":"Senior Purchaser",
"[email protected]":"$52,000.00",
"annualincome":52000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"8e546951-c543-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"620849\"",
"fullname":"Nancy Anderson (sample)",
"jobtitle":"Activities Manager",
"[email protected]":"$55,500.00",
"annualincome":55500.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"92546951-c543-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"620851\"",
"fullname":"Maria Cambell (sample)",
"jobtitle":"Accounts Manager",
"[email protected]":"$31,000.00",
"annualincome":31000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"96546951-c543-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"620853\"",
"fullname":"Nancy Anderson (sample)",
"jobtitle":"Logistics Specialist",
"[email protected]":"$63,500.00",
"annualincome":63500.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"9a546951-c543-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"620855\"",
"fullname":"Scott Konersmann (sample)",
"jobtitle":"Accounts Manager",
"[email protected]":"$38,000.00",
"annualincome":38000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"9e546951-c543-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"620857\"",
"fullname":"Robert Lyon (sample)",
"jobtitle":"Senior Technician",
"[email protected]":"$78,000.00",
"annualincome":78000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"a2546951-c543-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"620859\"",
"fullname":"Paul Cannon (sample)",
"jobtitle":"Ski Instructor",
"[email protected]":"$68,500.00",
"annualincome":68500.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"a6546951-c543-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"620861\"",
"fullname":"Rene Valdes (sample)",
"jobtitle":"Data Analyst III",
"[email protected]":"$86,000.00",
"annualincome":86000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"aa546951-c543-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"620863\"",
"fullname":"Jim Glynn (sample)",
"jobtitle":"Senior International Sales Manager",
"[email protected]":"$81,400.00",
"annualincome":81400.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"ae546951-c543-e611-80d5-00155da84802"
}
],
"Account_Tasks":[
{
"@odata.etag":"W/\"620840\"",
"subject":"Task 1",
"description":"Task 1 description",
"activityid":"8b546951-c543-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"620842\"",
"subject":"Task 2",
"description":"Task 2 description",
"activityid":"8c546951-c543-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"620844\"",
"subject":"Task 3",
"description":"Task 3 description",
"activityid":"8d546951-c543-e611-80d5-00155da84802"
}
]
}
Console output
-- Expanding multiple property types in one request --
Account 'Contoso, Ltd. (sample)' has the following primary contact person:
Fullname: 'Yvonne McKay (sample)'
Jobtitle: 'Coffee Master'
Annualincome: '45000'
Account 'Contoso, Ltd. (sample)' has the following related contacts:
1) Susanna Stubberod (sample), Senior Purchaser, $52,000.00
2) Nancy Anderson (sample), Activities Manager, $55,500.00
3) Maria Cambell (sample), Accounts Manager, $31,000.00
4) Nancy Anderson (sample), Logistics Specialist, $63,500.00
5) Scott Konersmann (sample), Accounts Manager, $38,000.00
6) Robert Lyon (sample), Senior Technician, $78,000.00
7) Paul Cannon (sample), Ski Instructor, $68,500.00
8) Rene Valdes (sample), Data Analyst III, $86,000.00
9) Jim Glynn (sample), Senior International Sales Manager, $81,400.00
Account 'Contoso, Ltd. (sample)' has the following tasks:
1) Task 1, Task 1 description
2) Task 2, Task 2 description
3) Task 3, Task 3 description
FetchXML queries
All the query options we would normally define such as $select , $filter , and $orderby are now defined in
the XML. In this operation, we query for all contacts whose fullname matches (sample) , and order the results
descending by fullname . This is the XML for this query.
HTTP Request
The request query string is sent to the server in encoded form. The encoded header looks like this.
HTTP/1.1 200 OK
OData-Version: 4.0
Preference-Applied: odata.include-annotations="OData.Community.Display.V1.FormattedValue"
Content-Length: 4345
{
"@odata.context":"https://[Organization
URI]/api/data/v9.0/$metadata#contacts(fullname,jobtitle,annualincome,_transactioncurrencyid_value,transactio
ncurrencyid,contactid)",
"value":[
{
"@odata.etag":"W/\"621502\"",
"fullname":"Yvonne McKay (sample)",
"jobtitle":"Coffee Master",
"[email protected]":"$45,000.00",
"annualincome":45000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"9255b257-c843-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"621627\"",
"fullname":"Susanna Stubberod (sample)",
"jobtitle":"Senior Purchaser",
"[email protected]":"$52,000.00",
"annualincome":52000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"9955b257-c843-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"621635\"",
"fullname":"Scott Konersmann (sample)",
"jobtitle":"Accounts Manager",
"[email protected]":"$38,000.00",
"annualincome":38000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"a955b257-c843-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"621637\"",
"fullname":"Robert Lyon (sample)",
"jobtitle":"Senior Technician",
"[email protected]":"$78,000.00",
"annualincome":78000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"ad55b257-c843-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"621641\"",
"fullname":"Rene Valdes (sample)",
"jobtitle":"Data Analyst III",
"[email protected]":"$86,000.00",
"annualincome":86000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"b555b257-c843-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"621639\"",
"fullname":"Paul Cannon (sample)",
"jobtitle":"Ski Instructor",
"[email protected]":"$68,500.00",
"annualincome":68500.0000,
"annualincome":68500.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"b155b257-c843-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"621629\"",
"fullname":"Nancy Anderson (sample)",
"jobtitle":"Activities Manager",
"[email protected]":"$55,500.00",
"annualincome":55500.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"9d55b257-c843-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"621633\"",
"fullname":"Nancy Anderson (sample)",
"jobtitle":"Logistics Specialist",
"[email protected]":"$63,500.00",
"annualincome":63500.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"a555b257-c843-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"621631\"",
"fullname":"Maria Cambell (sample)",
"jobtitle":"Accounts Manager",
"[email protected]":"$31,000.00",
"annualincome":31000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"a155b257-c843-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"621643\"",
"fullname":"Jim Glynn (sample)",
"jobtitle":"Senior International Sales Manager",
"[email protected]":"$81,400.00",
"annualincome":81400.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"b955b257-c843-e611-80d5-00155da84802"
}
]
}
Console output
FetchXML pagination
The way FetchXML handles paging is different than how query filter handles it. In FetchXML, you can specify a
count column that will indicate how many results to return per page. In the same request, you use the page
column to specify the page number you want. This operation will make a request for page 3 from the previous
FetchXML sample. Based on our sample data, we should have ten contacts in our result. Breaking each page
down to only four contacts per page, we should have three pages. Page 3 should contain only two contacts. If we
then ask for page 4, the system will return zero results.
<fetch mapping="logical"
output-format="xml-platform"
version="1.0"
distinct="false"
page="3"
count="4">
<entity name="contact">
<attribute name="fullname" />
<attribute name="jobtitle" />
<attribute name="annualincome" />
<order descending="true"
attribute="fullname" />
<filter type="and">
<condition value="%(sample)%"
attribute="fullname"
operator="like" />
</filter>
</entity>
</fetch>
HTTP Request
The request query string is sent to the server in encoded form. The encoded header looks like this.
HTTP Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
Preference-Applied: odata.include-annotations="OData.Community.Display.V1.FormattedValue"
Content-Length: 1037
{
"@odata.context":"https://[Organization
URI]/api/data/v9.0/$metadata#contacts(fullname,jobtitle,annualincome,_transactioncurrencyid_value,transactio
ncurrencyid,contactid)",
"value":[
{
"@odata.etag":"W/\"621631\"",
"fullname":"Maria Cambell (sample)",
"jobtitle":"Accounts Manager",
"[email protected]":"$31,000.00",
"annualincome":31000.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"a155b257-c843-e611-80d5-00155da84802"
},
{
"@odata.etag":"W/\"621643\"",
"fullname":"Jim Glynn (sample)",
"jobtitle":"Senior International Sales Manager",
"[email protected]":"$81,400.00",
"annualincome":81400.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802",
"contactid":"b955b257-c843-e611-80d5-00155da84802"
}
]
}
Console output
Predefined queries
You can use the Web API to execute predefined queries. More information:Retrieve and execute predefined
queries.
Saved query
In this operation, we will make a request for the savedqueryid GUID of the saved query named Active
Accounts . Then using the GUID and the savedQuery parameter, we will query for a list of active accounts.
Getting the saved query's GUID.
HTTP Request
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
Content-Length: 251
{
"@odata.context":"https://[Organization URI]/api/data/v9.0/$metadata#savedqueries(name,savedqueryid)",
"value":[
{
"@odata.etag":"W/\"443067\"",
"name":"Active Accounts",
"savedqueryid":"00000000-0000-0000-00aa-000010001002"
}
]
}
HTTP Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
REQ_ID: 2bc532c4-d445-44cd-adae-1909a616d6bc
OData-Version: 4.0
Preference-Applied: odata.include-annotations="OData.Community.Display.V1.FormattedValue"
Content-Length: 446
{
"@odata.context":"https://[Organization
URI]/api/data/v9.0/$metadata#accounts(name,_primarycontactid_value,primarycontactid,accountid)",
"value":[
{
"@odata.etag":"W/\"621613\"",
"name":"Contoso, Ltd. (sample)",
"_primarycontactid_value@OData.Community.Display.V1.FormattedValue":"Yvonne McKay (sample)",
"_primarycontactid_value":"9255b257-c843-e611-80d5-00155da84802",
"accountid":"9155b257-c843-e611-80d5-00155da84802"
}
]
}
Console output
-- Saved Query --
Saved Query (Active Accounts):
1) Contoso, Ltd. (sample)
User query
This sample creates a user query, executes it, then deletes it from the system. This user query is asking for any
contacts whose fullname contains (sample) , jobtitle contains manager , and annualincome greater than
55000 . Our sample data has two contacts matching this query.
HTTP Response
Pragma: no-cache
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
Content-Length: 246
{
"@odata.context":"https://[Organization URI]/api/data/v9.0/$metadata#userqueries(name,userqueryid)",
"value":[
{
"@odata.etag":"W/\"621698\"",
"name":"My User Query",
"userqueryid":"7ec390ab-c943-e611-80d5-00155da84802"
}
]
}
Getting the user query's content passing the GUID value with the userQuery parameter.
HTTP Request
HTTP Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
Content-Length: 1040
{
"@odata.context":"https://[Organization
URI]/api/data/v9.0/$metadata#contacts(fullname,contactid,jobtitle,annualincome,_transactioncurrencyid_value,
transactioncurrencyid)",
"value":[
{
"@odata.etag":"W/\"621643\"",
"fullname":"Jim Glynn (sample)",
"contactid":"b955b257-c843-e611-80d5-00155da84802",
"jobtitle":"Senior International Sales Manager",
"[email protected]":"$81,400.00",
"annualincome":81400.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802"
},
{
"@odata.etag":"W/\"621629\"",
"fullname":"Nancy Anderson (sample)",
"contactid":"9d55b257-c843-e611-80d5-00155da84802",
"jobtitle":"Activities Manager",
"[email protected]":"$55,500.00",
"annualincome":55500.0000,
"_transactioncurrencyid_value@OData.Community.Display.V1.FormattedValue":"US Dollar",
"_transactioncurrencyid_value":"518c78c9-d3f6-e511-80d0-00155da84802"
}
]
}
Console output
-- User Query --
Saved User Query:
1) Jim Glynn (sample), Senior International Sales Manager, $81,400.00
2) Nancy Anderson (sample), Activities Manager, $55,500.00
See also
Use the Dataverse Web API
Query Data using the Web API
Retrieve and execute predefined queries
Web API Query Data Sample (C#)
Web API Query Data Sample (Client-side JavaScript)
Web API Conditional Operations Sample
6/18/2021 • 7 minutes to read • Edit Online
NOTE
Unsure about entity vs. table? See Developers: Understand terminology in Microsoft Dataverse.
This collection of samples demonstrate how to perform operations that are conditionally based upon the
version of the table row contained on the Microsoft Dataverse server and/or currently maintained by the client.
For more information, see Perform conditional operations using the Web API. This sample is implemented as a
separate project for the following languages:
Web API Conditional Operations Sample (C#)
The Dataverse Web API follows the conventions of the OData v4.0 protocol, which uses ETags to implement
resource version control. Web API conditional operations depend upon this versioning mechanism.
This topic explains the structure and content of the samples at a higher, language-neutral level. It details the
HTTP requests and responses, and the associated program output, where applicable. Review the linked sample
topics above to obtain language-specific implementations and related details about how to perform the
operations described in this topic.
Demonstrates
This sample is divided into three principal sections, listed in the following table. Each section contains a set of
related Web API operations which are discussed in greater detail in the associated conceptual section of the
topic Perform conditional operations using the Web API .
The following sections contain a brief discussion of the Dataverse Web API operations performed, along with the
corresponding HTTP messages and associated console output which is the same for each language
implementation. For brevity, less pertinent HTTP headers have been omitted. The URIs of the table rows will vary
with the base organization address and the ID of the row assigned by your Dataverse server.
Sample data
The sample creates the following table row before the principal code sections are executed.
Conditional GET
This section of the program demonstrates how to perform conditional retrievals in order to optimize network
bandwidth and server processing while still maintaining the most current row state on the client. More
information:Conditional retrievals
1. Attempt to retrieve the account Contoso Ltd. only if it does not match the current version, identified by the
initial ETag value that was returned when the account row was created. This condition is represented by the
If-None-Match header.
Request
Response
Console output
The response value, 304 Not Modified , indicates that the current table row is the most current, so the server
does not return the requested row in the response body.
2. Update the account by modifying its primary telephone number property.
Request
Console output
3. Re-attempt the same conditional GET operation, again using the original ETag value. This time the operation
returns the requested data because the version on the server is different (and newer) than the version
identified in the request. As in all table row retrievals, the response includes an ETag header that identifies the
current version.
Request
Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
ETag: W/"628460"
{
"@odata.context":"https://[Organization
URI]/api/data/v9.0/$metadata#accounts(name,revenue,telephone1,description)/$entity",
"@odata.etag":"W/\"628460\"",
"name":"Contoso Ltd",
"revenue":5000000.0000,
"telephone1":"555-0001",
"description":"Parent company of Contoso Pharmaceuticals, etc.",
"accountid":"14e151db-9b4f-e611-80e0-00155da84c08",
"_transactioncurrencyid_value":"0d4ed62e-95f7-e511-80d1-00155da84c03"
}
Console output
Response
Console output
Expected Error: The version of the existing record doesn't match the property provided.
Account not deleted using ETag 'W/"628448"', status code: '412'.
2. Attempt to update the account if and only if it matches the original ETag value. Again, this condition is
represented by the If-Match header and the operation fails for the same reason.
Request
Response
HTTP/1.1 412 Precondition Failed
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
{
"error":{
"code":"","message":"The version of the existing record doesn't match the RowVersion property
provided.", . . .
}
}
Console output
Expected Error: The version of the existing record doesn't match the property provided.
Account not updated using ETag 'W/"628448"', status code: '412'.
3. Re-attempt an update, but instead use the current ETag value obtained from the last row retrieval in the
previous section.
Request
Response
Console output
4. Confirm the update succeeded by retrieving and outputting the current account state. This uses a basic GET
request.
Request
Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
ETag: W/"628461"
OData-Version: 4.0
{
"@odata.context":"https://[Organization
URI]/api/data/v9.0/$metadata#accounts(name,revenue,telephone1,description)/$entity",
"@odata.etag":"W/\"628461\"",
"name":"Contoso Ltd",
"revenue":6000000.0000,
"telephone1":"555-0003",
"description":"Parent company of Contoso Pharmaceuticals, etc.",
"accountid":"14e151db-9b4f-e611-80e0-00155da84c08",
"_transactioncurrencyid_value":"0d4ed62e-95f7-e511-80d1-00155da84c03"
}
Console output
{
"@odata.context": "https://[Organization
URI]/api/data/v9.0/$metadata#accounts(name,revenue,telephone1,description)/$entity",
"@odata.etag": "W/\"628461\"",
"name": "Contoso Ltd",
"revenue": 6000000.0,
"telephone1": "555-0003",
"description": "Parent company of Contoso Pharmaceuticals, etc.",
"accountid": "14e151db-9b4f-e611-80e0-00155da84c08",
"_transactioncurrencyid_value": "0d4ed62e-95f7-e511-80d1-00155da84c03"
}
Response
HTTP/1.1 412 Precondition Failed
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
{
"error":{
"code":"","message":"A record with matching key values already exists.", . . .
}
}
Console output
2. Attempt to perform the same update operation without creation. To accomplish this, the conditional
If-Match header is used with a value of * . This operation succeeds because the row exists on the server.
Request
Response
Console output
3. Retrieve and output the current account state with a basic GET request. Note that the returned ETag value
has changed to reflect the new, updated version of the account row.
Request
Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
ETag: W/"628463"
OData-Version: 4.0
{
"@odata.context":"https://[Organization
URI]/api/data/v9.0/$metadata#accounts(name,revenue,telephone1,description)/$entity",
"@odata.etag":"W/\"628463\"",
"name":"Contoso Ltd","revenue":7500000.0000,
"telephone1":"555-0005",
"description":"Parent company of Contoso Pharmaceuticals, etc.",
"accountid":"14e151db-9b4f-e611-80e0-00155da84c08",
"_transactioncurrencyid_value":"0d4ed62e-95f7-e511-80d1-00155da84c03"
}
Console output
{
"@odata.context": "https://[Organization
URI]/api/data/v9.0/$metadata#accounts(name,revenue,telephone1,description)/$entity",
"@odata.etag": "W/\"628463\"",
"name": "Contoso Ltd",
"revenue": 7500000.0,
"telephone1": "555-0005",
"description": "Parent company of Contoso Pharmaceuticals, etc.",
"accountid": "14e151db-9b4f-e611-80e0-00155da84c08",
"_transactioncurrencyid_value": "0d4ed62e-95f7-e511-80d1-00155da84c03"
}
Request
Response
Console output
5. Just as in step 2, attempt to update the account if it exists. Again, this condition is represented by the
If-Match header with a value of * . This operation fails because this table row was just deleted. However, if
this If-Match header was absent, then the resulting basic upsert operation should successfully create a new
row.
Request
PATCH https://[Organization URI]/api/data/v9.0/accounts(14e151db-9b4f-e611-80e0-00155da84c08) HTTP/1.1
If-Match: *
OData-MaxVersion: 4.0
OData-Version: 4.0
Accept: application/json
Content-Type: application/json; charset=utf-8
{
"telephone1": "555-0006",
"revenue": 7500000
}
Response
Console output
There is no need to cleanup sample data because the one account row was already deleted in step 4.
See also
Use the Dataverse Web API
Perform conditional operations using the Web API
Web API Conditional Operations Sample (C#)
Web API Functions and Actions Sample
7/19/2021 • 8 minutes to read • Edit Online
NOTE
Unsure about entity vs. table? See Developers: Understand terminology in Microsoft Dataverse.
This group of samples demonstrate how to perform bound and unbound functions and actions, including
custom actions, using the Microsoft Dataverse Web API. This sample is implemented as a separate project for
the following languages:
Functions and Actions Sample (C#)
This topic explains the structure and content of the sample at a higher, language-neutral level. Review the linked
sample topics above for language-specific implementation details about how to perform the operations
described in this topic.
Demonstrates
This sample is divided into the following principal sections, containing Web API functions and actions operations
which are discussed in greater detail in the associated conceptual topics.
Sample data
WhoAmI Function
systemuser EntityType
GetTimeZoneCodeByLocalizedName Function
CalculateTotalTimeIncident Function
WinOpportunity Action
opportunity EntityType
TO P IC SEC T IO N A SSO C IAT ED TO P IC ( S)
AddToQueue Action
WhoAmI Function
systemuser EntityType
letter EntityType
Bound actions
contact EntityType
Unbound actions
account EntityType
Unbound actions
contact EntityType
The following sections contain a brief discussion of the Dataverse Web API operations performed, along with the
corresponding HTTP messages and associated console output.
Sample data
To ensure the operations in this sample work properly, we first create sample data on the Dataverse server.
These sample data will be deleted from the server unless the user chooses to not delete them. The data in this
sample are created individually as follows.
Create an account (e.g.: Fourth Coffee ) and associate it with an incident that has three 30 minute tasks
(90 minutes total). After the tasks are created, they are then marked as completed. The operation will
calculate the total time it took to complete these three tasks.
{
title: "Sample Case",
"[email protected]": accountUri,
Incident_Tasks: [
{
subject: "Task 1",
actualdurationminutes: 30
},
{
subject: "Task 2",
actualdurationminutes: 30
},
{
subject: "Task 3",
actualdurationminutes: 30
}
]
};
Create an account and associate it with an opportunity. This opportunity will be mark as won in the
sample operation.
{
name: "Sample Account for WebAPIFunctionsAndActions sample",
opportunity_customer_accounts: [{
name: "Opportunity to win"
}]
};
Create a letter activity. The letter will be added to the current user's queue in the sample operation.
{
description: "Example letter"
}
Create a contact to use with a custom action sample_AddNoteToContact in the sample operation.
{
firstname: "Jon",
lastname: "Fogg"
}
Sample operations
The sample operations in this topic are organized in the following ways.
Working with functions: These operations show bound and unbound functions that either accept parameters
or not.
Working with actions: These operations show bound and unbound actions that either accept parameters or
not.
Custom actions: These operations show bound and unbound actions and how to handle custom error
exceptions.
Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
Content-Length: 273
{
"@odata.context":"https://[Organization
URI]/api/data/v9.0/$metadata#Microsoft.Dynamics.CRM.WhoAmIResponse",
"BusinessUnitId":"0d6cc84a-d3f6-e511-80d0-00155da84802",
"UserId":"b08dc84a-d3f6-e511-80d0-00155da84802",
"OrganizationId":"0f47eae2-a906-4ae4-9215-f09875979f6a"
}
GET https://[Organization
URI]/api/data/v9.0/GetTimeZoneCodeByLocalizedName(LocalizedStandardName=@p1,LocaleId=@p2)?
@p1='Pacific%20Standard%20Time'&@p2=1033 HTTP/1.1
OData-MaxVersion: 4.0
OData-Version: 4.0
Content-Type: application/json; charset=utf-8
Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
Content-Length: 154
{
"@odata.context":"https://[Organization
URI]/api/data/v9.0/$metadata#Microsoft.Dynamics.CRM.GetTimeZoneCodeByLocalizedNameResponse",
"TimeZoneCode":4
}
Console output
Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
Content-Length: 148
{
"@odata.context":"https://[Organization
URI]/api/data/v9.0/$metadata#Microsoft.Dynamics.CRM.CalculateTotalTimeIncidentResponse",
"TotalTime":90
}
Console output
{
"Status":3,
"OpportunityClose":{
"subject":"Won Opportunity",
"[email protected]":"https://[Organization URI]/api/data/v9.0/opportunities(47920da5-fb4a-e611-
80d5-00155da84802)"
}
}
Response
Console output
{
"Target":{
"activityid":"4c920da5-fb4a-e611-80d5-00155da84802",
"@odata.type":"Microsoft.Dynamics.CRM.letter"
}
}
Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
Content-Length: 170
{
"@odata.context":"https://[Organization
URI]/api/data/v9.0/$metadata#Microsoft.Dynamics.CRM.AddToQueueResponse",
"QueueItemId":"67bdfabd-fc4a-e611-80d5-00155da84802"
}
Console output
{
"NoteTitle":"The Title of the Note",
"NoteText":"The text content of the note."
}
Response
HTTP/1.1 200 OK
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
Content-Length: 149
{
"@odata.context":"https://[Organization URI]/api/data/v9.0/$metadata#annotations/$entity",
"annotationid":"ba146d0b-fd4a-e611-80d5-00155da84802"
}
Response
HTTP/1.1 200 OK
OData-Version: 4.0
Content-Length: 450
{
"@odata.context":"https://[Organization
URI]/api/data/v9.0/$metadata#annotations(subject,notetext,objectid_contact,objectid_contact(fullname))/$enti
ty",
"@odata.etag":"W/\"622978\"",
"subject":"The Title of the Note",
"notetext":"The text content of the note.",
"annotationid":"ba146d0b-fd4a-e611-80d5-00155da84802",
"objectid_contact":{
"@odata.etag":"W/\"622968\"",
"fullname":"Jon Fogg",
"contactid":"4d920da5-fb4a-e611-80d5-00155da84802"
}
}
Console output
Custom action: sample_AddNoteToContact
A note with the title 'The Title of the Note' and the content 'The text content of the note.' was
created and associated with the contact Jon Fogg.
{
"CustomerType":"account",
"AccountName":"Account Customer Created in WebAPIFunctionsAndActions sample"
}
Response
{
"CustomerType":"contact",
"AccountName":"Account Customer Created in WebAPIFunctionsAndActions sample"
}
Response
HTTP/1.1 500 Internal Server Error
Content-Type: application/json; odata.metadata=minimal
OData-Version: 4.0
Content-Length: 2760
{
"error":{
"code":"",
"message":"ContactFirstName and ContactLastName are required when CustomerType is contact."
}
}
Console output
Expected custom error: ContactFirstName and ContactLastName are required when CustomerType is contact.
See also
Use the Dataverse Web API
Use Web API functions
Use Web API actions
Web API Functions and Actions Sample (C#)
CDSWebApiService class library (C#)
7/19/2021 • 6 minutes to read • Edit Online
NOTE
Unsure about entity vs. table? See Developers: Understand terminology in Microsoft Dataverse.
A .NET Framework sample class library that uses JSON objects and common HTTP messaging operations with
the Microsoft Dataverse Web API. Use of these class methods result in less complicated application code,
implementation of performance best practices, and improved error processing.
This class library demonstrates how to:
Make your code 'DRY'er by wrapping common operations by Http methods.
Manage an HttpClient in a thread-safe manner.
Manage Service Protection Limit API 429 Too Many Requests errors that a client application should expect.
More information: Service Protection API Limits
Using the provided Visual Studio project, you can build a class library and include this functionality in your own
application code. You can find the CDSWebApiService class library source code and Visual Studio solution at
PowerApps-Samples/cds/webapi/C#/CDSWebApiService.
Example
This example shows how to instantiate a CDSWebAPIService instance and create a contact row.
This example expects that the connection string is set in the App.config file as shown below.
The code will access the connection string to instantiate the CDSWebApiService.
//Get configuration data from App.config connectionStrings
static readonly string connectionString =
ConfigurationManager.ConnectionStrings["Connect"].ConnectionString;
static readonly ServiceConfig config = new ServiceConfig(connectionString);
Properties
This class exposes only the BaseAddress property. This is the configured BaseAddress used by the HttpClient. It
can be useful to build complete URIs when needed since most cases will expect relative URIs.
Methods
This class provides the following public methods:
PostCreate
Creates a table row (entity record) synchronously and returns the URI.
Parameters
NAME TYPE DESC RIP T IO N
Return Value
The Uri of the created table row (entity record)
Remarks
This method is provided because creating entities is a common operation and the URI is returned in the
OData-EntityId header. Having this specialized method allows for less code than having only the Post method,
which returns only a JObject.
More information: Create a table row using the Web API.
PostCreateAsync
The asynchronous version of PostCreate.
Post
Sends a POST request synchronously and returns the response as a JObject.
Parameters
NAME TYPE DESC RIP T IO N
Return Value
A JObject containing the response.
Remarks
This method can be used for any operation using the POST http method, but it only includes the response
content. Use PostCreate to create table rows (entity records) and return only the URI of the created row.
More information:
Create with data returned
Use Web API actions
PostAsync
The asynchronous version of Post.
Patch
Sends a PATCH request synchronously.
Parameters
NAME TYPE DESC RIP T IO N
Remarks
Patch is frequently used to Update or Upsert table rows.
More information:
Basic update
Upsert a table
PatchAsync
The asynchronous version of Patch.
Get
Sends a GET request synchronously and returns data
Parameters
NAME TYPE DESC RIP T IO N
Return Value
A JToken representing the requested data.
Remarks
More information:
Query Data using the Web API
Retrieve a table row using the Web API
Use Web API functions
GetAsync
The asynchronous version of Get.
Delete
Sends a DELETE request synchronously.
Parameters
NAME TYPE DESC RIP T IO N
Remarks
More information:
Basic delete
Remove a reference to a table
Delete a single property value
DeleteAsync
The asynchronous version of Delete.
Put
Sends a PUT request synchronously.
Parameters
NAME TYPE DESC RIP T IO N
Remarks
Put is used to update specific table columns.
Note : The Http PUT method is also used to update table or column definitions (metadata). This method cannot
be used for that purpose. It is specifically for business data.
More information:
Update a single property value
Change the reference in a single-valued navigation property
PutAsync
The asynchronous version of Put.
OAuthMessageHandler
When the internal HttpClient is initialized in the CDSWebApiService constructor, an instance of this class is set as
an HttpMessageHandler. This class works with the ADAL libraries to ensure that the accessToken will be
refreshed each time a request is sent. If the accessToken expires, the ADAL library methods will automatically
refresh it.
More information: Example demonstrating a DelegatingHandler
ServiceConfig
The CDSWebApiService class should be initialized with a connection string via the ServiceConfig class.
The ServiceConfig constructor accepts a connection string, typically from the App.config configuration, and the
data defined there is parsed into a ServiceConfig instance which the CDSWebApiService constructor requires.
Properties
The following are the properties of the ServiceConfig class.
The ClientId and RedirectUrl values are for sample applications. You can use these to run the samples, but you
should register your own applications and enter the corresponding values for these properties.
More information: Walkthrough: Register an app with Azure Active Directory
ServiceException
This class simply extends Exception and provides additional properties from an error response.
Properties
NAME TYPE DESC RIP T IO N
NOTE
Unsure about entity vs. table? See Developers: Understand terminology in Microsoft Dataverse.
This sample demonstrates how to perform basic CRUD (Create, Retrieve, Update, and Delete) and association
and dissociation operations on Microsoft Dataverse table rows (entity records), using the Dataverse Web API.
NOTE
This sample implements the Dataverse operations and console output detailed in Web API Basic Operations Sample and
uses the common C# constructs described in Web API Samples (C#).
Prerequisites
The following is required to build and run the CDSWebApiService C# samples :
Microsoft Visual Studio 2019.
Access to Dataverse with privileges to perform CRUD operations.
<add name="Connect"
connectionString="Url=https://yourorg.api.crm.dynamics.com;
Authority=null;
ClientId=51f81489-12ee-4a9e-aaae-a2591f45987d;
RedirectUrl=app://58145B91-0C36-4500-8554-080854F2AC97;
[email protected];
Password=y0urp455w0rd;
CallerObjectId=null;
Version=9.1;
MaxRetries=3;
TimeoutInSeconds=180;
"/>
NOTE
The ClientId and RedirectUrl shown above can be used to test the code in this article. You are not required
to register an application just to test the article code.
5. Make sure that the BasicOperations project is set as the startup project. The name of the project should
be bold to indicate it is the startup project. If the name is not bold, right-click it in the solution explorer
and select Set as Star tup Project .
6. Press F5 to run the program in debug mode.
Code listing
This sample depends on the assembly included in the CDSWebAPIService project. For information on the
methods this class provides see: Web API CDSWebApiService class Sample (C#).
The following is the code from the Program.cs file:
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Configuration;
namespace PowerApps.Samples
{
internal class Program
{
//Get configuration data from App.config connectionStrings
private static readonly string connectionString =
ConfigurationManager.ConnectionStrings["Connect"].ConnectionString;
try
{
using (CDSWebApiService svc = new CDSWebApiService(config))
{
Console.WriteLine("--Starting Basic Operations--");
Console.WriteLine("--Section 1 started--");
//Create a contact
var contact1 = new JObject
{
{ "firstname", "Rafel" },
{ "lastname", "Shillo" }
};
Uri contact1Uri = svc.PostCreate("contacts", contact1);
Console.WriteLine($"Contact '{contact1["firstname"]} " +
$"{contact1["lastname"]}' created.");
entityUris.Add(contact1Uri); //To delete later
Console.WriteLine($"Contact URI: {contact1Uri}");
//Update a contact
JObject contact1Add = new JObject
{
{ "annualincome", 80000 },
{ "annualincome", 80000 },
{ "jobtitle", "Junior Developer" }
};
svc.Patch(contact1Uri, contact1Add);
Console.WriteLine(
$"Contact '{contact1["firstname"]} {contact1["lastname"]}' " +
$"updated with jobtitle and annual income");
//Retrieve a contact
var retrievedcontact1 = svc.Get(contact1Uri.ToString() +
"?$select=fullname,annualincome,jobtitle,description");
Console.WriteLine($"Contact '{retrievedcontact1["fullname"]}' retrieved: \n" +
$"\tAnnual income: {retrievedcontact1["annualincome"]}\n" +
$"\tJob title: {retrievedcontact1["jobtitle"]} \n" +
//description is initialized empty.
$"\tDescription: {retrievedcontact1["description"]}.");
/// <summary>
/// Demonstrates creation of entity instance and simultaneous association to another,
/// existing entity.
/// </summary>
///
Console.WriteLine("\n--Section 2 started--");
//Create a new account and associate with existing contact in one operation.
var account1 = new JObject
{
{ "name", "Contoso Ltd" },
{ "telephone1", "555-5555" },
{ "[email protected]", contact1Uri }
};
var account1Uri = svc.PostCreate("accounts", account1);
entityUris.Add(account1Uri); //To delete later
Console.WriteLine($"Account '{account1["name"]}' created.");
Console.WriteLine($"Account URI: {account1Uri}");
//Retrieve account name and primary contact info
JObject retrievedAccount1 = svc.Get($"{account1Uri}?$select=name," +
$"&$expand=primarycontactid($select=fullname,jobtitle,annualincome)") as JObject;
/// <summary>
/// Demonstrates creation of entity instance and related entities in a single operation.
/// </summary>
///
Console.WriteLine("\n--Section 3 started--");
//Create the following entries in one operation: an account, its
// associated primary contact, and open tasks for that contact. These
// entity types have the following relationships:
// Accounts
// |---[Primary] Contact (N-to-1)
// |---Tasks (1-to-N)
//Build the Account object inside-out, starting with most nested type(s)
JArray tasks = new JArray();
JObject task1 = new JObject
{
{ "subject", "Sign invoice" },
{ "description", "Invoice #12321" },
{ "scheduledend", DateTimeOffset.Parse("4/19/2019") }
};
tasks.Add(task1);
JObject task2 = new JObject
{
{ "subject", "Setup new display" },
{ "description", "Theme is - Spring is in the air" },
{ "scheduledstart", DateTimeOffset.Parse("4/20/2019") }
};
tasks.Add(task2);
JObject task3 = new JObject
{
{ "subject", "Conduct training" },
{ "description", "Train team on making our new blended coffee" },
{ "scheduledstart", DateTimeOffset.Parse("6/1/2019") }
};
tasks.Add(task3);
//Retrieve account, primary contact info, and assigned tasks for contact.
//Dataverse only supports querying-by-expansion one level deep, so first query
// account-primary contact.
var retrievedAccount2 = svc.Get($"{account2Uri}?$select=name," +
$"&$expand=primarycontactid($select=fullname,jobtitle,annualincome)");
/// <summary>
/// Demonstrates associating and disassociating of existing entity instances.
/// </summary>
Console.WriteLine("\n--Section 4 started--");
//Add 'Rafel Shillo' to the contact list of 'Fourth Coffee',
// a 1-to-N relationship.
JObject rel1 = new JObject
{
{ "@odata.id", contact1Uri }
}; //relationship object for msg content
Uri navUri1 = new Uri($"{account2Uri}/contact_customer_accounts/$ref");
//Create relationship
svc.Post(navUri1.ToString(), rel1);
Console.WriteLine($"Contact '{retrievedcontact1["fullname"]}' " +
$"associated to account '{account2["name"]}'.");
svc.Delete(dis1Uri);
//'Rafel Shillo' was removed from the the contact list of 'Fourth Coffee'
svc.Post(navUri2.ToString(), rel2);
Console.WriteLine($"Opportunity '{oppor1["name"]}' associated with competitor
'{comp1["name"]}'.");
Console.WriteLine("\n--Section 5 started--");
//Delete all the created sample entities. Note that explicit deletion is not required
// for contact tasks because these are automatically cascade-deleted with owner.
if (!deleteCreatedRecords)
{
Console.Write("\nDo you want these entity records deleted? (y/n) [y]: ");
String answer = Console.ReadLine();
answer = answer.Trim();
if (!(answer.StartsWith("y") || answer.StartsWith("Y") || answer == string.Empty))
if (!(answer.StartsWith("y") || answer.StartsWith("Y") || answer == string.Empty))
{ entityUris.Clear(); }
else
{
Console.WriteLine("\nDeleting created records.");
}
}
else
{
Console.WriteLine("\nDeleting created records.");
}
See also
Use the Dataverse Web API
Web API CDSWebApiService class Sample (C#)
Create a table row using the Web API
Update and delete table rows using the Web API
Retrieve an table row using the Web API
Web API Samples
Web API Basic Operations Sample Web API Query Data Sample (C#)
Web API Conditional Operations Sample (C#)
Web API Functions and Actions Sample (C#)
Query Data sample (C#)
7/19/2021 • 15 minutes to read • Edit Online
NOTE
Unsure about entity vs. table? See Developers: Understand terminology in Microsoft Dataverse.
This sample demonstrates how to perform data queries of Microsoft Dataverse table rows (entity records), using
the Dataverse Web API. Those query operations include:
Selecting specific properties
Using Query Functions
Ordering and alias
Limit results
Expanding results
Aggregate results
FetchXml queries
Using predefined queries
NOTE
This sample implements the Dataverse operations and console output detailed in Web API Basic Operations Sample and
uses the methods available in the CDSWebApiService class for message processing, performance enhancements, and error
management.
Prerequisites
The following is required to build and run the sample:
Microsoft Visual Studio 2019.
Access to Dataverse with privileges to perform the operations described above.
Code listing
This sample depends on the assembly built from in the CDSWebApiService project. For information on the
methods this class provides see CDSWebApiService class.
The following is the code from the Program.cs file:
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Net;
namespace PowerApps.Samples
{
internal class Program
{
//Get configuration data from App.config connectionStrings
private static readonly string connectionString =
ConfigurationManager.ConnectionStrings["Connect"].ConnectionString;
// Basic query: Query using $select against a contact entity to get the properties you
want.
// For performance best practice, always use $select, otherwise all properties are
returned
Console.WriteLine("-- Basic Query --");
// Filter criteria:
// Applying filters to get targeted data.
// 1) Using standard query functions (e.g.: contains, endswith, startswith)
// 2) Using Dataverse query functions (e.g.: LastXhours, Last7Days, Today, Between, In,
...)
// 3) Using filter operators and logical operators (e.g.: eq, ne, gt, and, or, etc…)
// 4) Set precedence using parenthesis (e.g.: ((criteria1) and (criteria2)) or
(criteria3)
// For more info, see:
//https://docs.microsoft.com/en-us/powerapps/developer/data-platform/webapi/query-data-
web-api#filter-results
WriteContactResultsTable(
"Contacts filtered by fullname containing '(sample)':",
containsSampleinFullNameCollection["value"]);
WriteContactResultsTable(
"Contacts that were created within the last 1hr:",
createdInLastHourCollection["value"]);
WriteContactResultsTable(
"Contacts with '(sample)' in name and income above $55,000:",
highIncomeContacts["value"]);
WriteContactResultsTable(
"Contacts with '(sample)' in name senior jobtitle or high income:",
seniorOrSpecialistsCollection["value"]);
WriteContactResultsTable(
"Contacts ordered by jobtitle (Ascending) and annualincome (descending)",
orderedResults["value"]);
WriteContactResultsTable(
"Contacts ordered by jobtitle (Ascending) and annualincome (descending)",
orderedResultsWithParams["value"]);
//To limit records returned, use the $top query option. Specifying a limit number for
$top
//returns at most that number of results per request. Extra results are ignored.
//For more information, see:
// https://docs.microsoft.com/powerapps/developer/data-platform/webapi/query-data-web-
api#use-top-query-option
Console.WriteLine("\n-- Top Results --");
//Result count - count the number of results matching the filter criteria.
//Tip: Use count together with the "odata.maxpagesize" to calculate the number of pages
in
//the query. Note: Dataverse has a max record limit of 5000 records per response.
Console.WriteLine("\n-- Result Count --");
//1) Get a count of a collection without the data.
JToken count = svc.Get($"contacts/$count");
Console.WriteLine($"\nThe contacts collection has {count} contacts.");
// 2) Get a count along with the data.
WriteContactResultsTable($"{countWithData["@odata.count"]} " +
$"Contacts with 'senior' or 'manager' in job title:",
countWithData["value"]);
// Tip: For performance best practice, always use $select statement in an expand option.
Console.WriteLine("\n-- Expanding Results --");
WriteContactResultsTable(
$"Account '{account2["name"]}' has the following contact customers:",
account2["contact_customer_accounts"]);
//4) Expand using multiple navigation property types in a single request, specifically:
// primarycontactid, contact_customer_accounts, and Account_Tasks.
WriteContactResultsTable(
$"Account '{account3["name"]}' has the following contact customers:",
account3["contact_customer_accounts"]);
// 5) Multi-level expands
DisplayExpandedValuesFromTask(contosoTasks["value"]);
//Use FetchXML to query for all contacts whose fullname contains '(sample)'.
//Note: XML string must be URI encoded. For more information, see:
//https://docs.microsoft.com/powerapps/developer/data-platform/webapi/retrieve-and-
execute-predefined-queries#use-custom-fetchxml
Console.WriteLine("\n-- FetchXML -- ");
string fetchXmlQuery =
"<fetch mapping='logical' output-format='xml-platform' version='1.0'
distinct='false'>" +
"<entity name ='contact'>" +
"<attribute name ='fullname' />" +
"<attribute name ='jobtitle' />" +
"<attribute name ='annualincome' />" +
"<order descending ='true' attribute='fullname' />" +
"<filter type ='and'>" +
"<condition value ='%(sample)%' attribute='fullname' operator='like' />" +
$"<condition value ='{contosoId.ToString()}' attribute='parentcustomerid'
operator='eq' />" +
"</filter>" +
"</entity>" +
"</fetch>";
JToken contacts = svc.Get(
$"contacts?fetchXml={WebUtility.UrlEncode(fetchXmlQuery)}",
formattedValueHeaders);
//1) Saved Query - retrieve "Active Accounts", run it, then display the results.
Console.WriteLine("\n-- Saved Query -- ");
DeleteRequiredRecords(svc, deleteCreatedRecords);
entityUris.ForEach(x =>
{
Console.Write(".");
svc.Delete(x);
});
Console.WriteLine($"\n{message}");
//header
Console.WriteLine($"\t|{"Full Name",col1}|" +
$"{"Job Title",col2}|" +
$"{"Annual Income",col3}");
Console.WriteLine($"\t|{new string('-', col1 * -1),col1}|" +
$"{new string('-', col2 * -1),col2}|" +
$"{new string('-', col3 * -1),col3}");
//rows
foreach (JObject contact in collection)
{
Console.WriteLine($"\t|{contact["fullname"],col1}|" +
$"{contact["jobtitle"],col2}|" +
$"{contact["[email protected]"],col3}");
}
}
Console.WriteLine($"\t|{"Subject",col1}|" +
$"{"Contact",col2}|" +
$"{"Account",col3}|" +
$"{"Account CreatedBy",col4}");
Console.WriteLine($"\t|{new string('-', col1 * -1),col1}|" +
$"{new string('-', col2 * -1),col2}|" +
$"{new string('-', col3 * -1),col3}|" +
$"{new string('-', col4 * -1),col4}");
//rows
foreach (JObject task in collection)
{
Console.WriteLine($"\t|{task["subject"],col1}|" +
$"{task["regardingobjectid_contact_task"]["fullname"],col2}|" +
$"{task["regardingobjectid_contact_task"]["parentcustomerid_account"]["name"],col3}|" +
$"{task["regardingobjectid_contact_task"]["parentcustomerid_account"]["createdby"]
["fullname"],col4}");
//Console.WriteLine($"\n\tSubject: " +
// $"{task["subject"]}");
//Console.WriteLine($"\t\tContact: " +
// $"{task["regardingobjectid_contact_task"]["fullname"]}");
//Console.WriteLine($"\t\t\tAccount: " +
// $"{task["regardingobjectid_contact_task"]["parentcustomerid_account"]["name"]}");
//Console.WriteLine($"\t\t\t\tAccount Created by: " +
// $"{task["regardingobjectid_contact_task"]["parentcustomerid_account"]["createdby"]
["fullname"]}");
}
}
}
}
See also
Query Data using the Web API
Web API Query Data Sample
Use the Dataverse Web API
Conditional Operations sample (C#)
7/19/2021 • 5 minutes to read • Edit Online
NOTE
Unsure about entity vs. table? See Developers: Understand terminology in Microsoft Dataverse.
This sample shows how to perform conditional message operations when accessing table rows (entity records)
of the Microsoft Dataverse. The sample uses the Dataverse Web API and the CDSWebApiService class.
Messages using a conditional statement, such as "If-None-Match", in the message header are sent to Dataverse.
NOTE
This sample implements the Dataverse operations and console output detailed in Web API Conditional Operations Sample
and uses the methods available in the CDSWebApiService class for message processing, performance enhancements, and
error management.
Prerequisites
The following is required to build and run the sample:
Microsoft Visual Studio 2019.
Access to Dataverse with privileges to perform the operations described above.
Code listing
This sample depends on the assembly built from in the CDSWebApiService project. For information on the
methods this class provides see CDSWebApiService class.
The following is the code from the Program.cs file:
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Net;
namespace PowerApps.Samples
{
/// <summary>
/// This program demonstrates use of conditional operations with the
/// Dataverse Web API.
/// </summary>
class Program
{
//Get environment configuration data from the connection string in the App.config file.
static readonly string connectionString =
ConfigurationManager.ConnectionStrings["Connect"].ConnectionString;
static readonly ServiceConfig config = new ServiceConfig(connectionString);
try
{
// Use the wrapper class that handles message processing, error handling, and more.
using (CDSWebApiService svc = new CDSWebApiService(config))
{
Console.WriteLine("--Starting conditional operations demonstration--\n");
// Attempt to retrieve the account record using a conditional GET defined by a message
header with
// the current ETag value.
try
{
retrievedaccount1 = svc.Get(
path: account1Uri.ToString() + queryOptions,
headers: new Dictionary<string, List<string>> {
{ "If-None-Match", new List<string> {initialAcctETagVal}}}
);
// Expected result
Console.WriteLine("Modified account record retrieved using ETag: {0}",
initialAcctETagVal);
Console.WriteLine("Notice the update ETag value and telephone number");
}
catch (ServiceException e)
{
if (e.StatusCode == (int)HttpStatusCode.NotModified) // Not expected
{
Console.WriteLine("Unexpected outcome: Entity was modified so something should
be returned.");
}
else { throw e; }
}
try
{
svc.Delete(
uri: account1Uri,
headers: new Dictionary<string, List<string>> {
{ "If-Match", new List<string> {initialAcctETagVal}}}
);
try
{
svc.Patch(
uri: account1Uri,
body: accountUpdate,
headers: new Dictionary<string, List<string>> {
{ "If-Match", new List<string> {initialAcctETagVal}}}
);
See also
Perform conditional operations using the Web API
Web API Conditional Operations Sample
Use the Dataverse Web API
Web API Functions and Actions Sample (C#)
7/19/2021 • 7 minutes to read • Edit Online
NOTE
Unsure about entity vs. table? See Developers: Understand terminology in Microsoft Dataverse.
This sample demonstrates how to call bound and unbound functions and actions, including custom actions,
using the Microsoft Dataverse Web API.
NOTE
This sample implements the operations detailed in the Web API Functions and Actions Sample. This sample also uses the
CDSWebApiService class methods for messaging, performance enhancements, and error processing.
Prerequisites
Prerequisites for all Dataverse Web API C# samples are detailed in the Prerequisites section of the parent topic
Web API Samples (C#).
Code listing
Program.cs partial listing
sing System;
using System.Collections.Generic;
using System.Configuration;
using System.Net;
namespace PowerApps.Samples
{
/// <summary>
/// This program demonstrates use of functions and actions with the
/// Power Platform data service Web API.
/// </summary>
/// <remarks>Be sure to fill out App.config with your test environment information
/// and import the provided managed solution into your test environment using the web app
/// before running this program.</remarks>
/// <see cref="Https://docs.microsoft.com/powerapps/developer/data-platform/webapi/samples/functions-
actions-csharp"/>
public class Program
{
public static void Main()
{
Console.Title = "Function and Actions demonstration";
// Track entity instance URIs so those records can be deleted prior to exit.
Dictionary<string, Uri> entityUris = new Dictionary<string, Uri>();
try
{
// Get environment configuration information from the connection string in App.config.
ServiceConfig config = new ServiceConfig(
ConfigurationManager.ConnectionStrings["Connect"].ConnectionString);
// Use the service class that handles HTTP messaging, error handling, and
// performance optimizations.
using (CDSWebApiService svc = new CDSWebApiService(config))
{
// Create any entity instances required by the program code that follows
CreateRequiredRecords(svc, entityUris);
// Retrieve the current user's full name from the WhoAmI function:
Console.Write("\tGetting information on the current user..");
JToken currentUser = svc.Get("WhoAmI");
Console.WriteLine("completed.");
Console.WriteLine("\tCurrent user's full name is '{0}'.", user["fullname"]);
#endregion Call an unbound function with no parameters
Console.WriteLine(
"\tThe time zone '{0}' has the code '{1}'.", timeZoneName, timeZoneCode);
#endregion Call an unbound function that requires parameters
// Retrieve the total time (minutes) spent on all tasks associated with
// incident "Sample Case".
string boundUri = entityUris["Sample Case"] +
@"/Microsoft.Dynamics.CRM.CalculateTotalTimeIncident()";
Console.WriteLine("\tOpportunity won.");
#endregion Call an unbound action that requires parameters
Console.WriteLine("completed.");
//Add the letter activity to current user's queue, then return its queue ID.
JObject targetUri = JObject.Parse(
@"{activityid: '" + letterActivityId + @"', '@odata.type':
'Microsoft.Dynamics.CRM.letter' }");
Console.WriteLine("\tA note with the title '{0}' was created and " +
"associated with the contact 'Jon Fogg'.", note["NoteTitle"]);
#endregion Call a bound custom action that requires parameters
// Because the CreateCustomer custom action does not return any data about the created
instance,
// we must query the customer instance to figure out its URI.
JToken customer = svc.Get("accounts?$filter=name eq 'New account customer
(sample)'&$select=accountid&$top=1");
Uri customerUri = new Uri(svc.BaseAddress + "accounts(" + customer["value"][0]
["accountid"] + ")");
entityUris.Add( customerName, customerUri );
// Try to call the same custom action with invalid parameters, here the same name is
// not valid for a contact. (ContactFirstname and ContactLastName parameters are
// required when CustomerType is contact.
customerAttributes = JObject.Parse(
@"{CustomerType: 'contact', AccountName: '" + customerName + "'}");
try
{
customerUri = svc.PostCreate("sample_CreateCustomer", customerAttributes);
Console.WriteLine("\tCall to the custom CreateCustomer action succeeded, which was
not expected.");
}
catch (AggregateException e)
{
Console.WriteLine("\tCall to the custom CreateCustomer action did not succeed (as
was expected).");
foreach (Exception inner in (e as AggregateException).InnerExceptions)
{ Console.WriteLine("\t -" + inner.Message); }
}
#endregion Call an unbound custom action that requires parameters
DeleteEntityRecords(svc, entityUris);
}
}
catch (Exception e)
{
Console.BackgroundColor = ConsoleColor.Red; // Highlight exceptions
if ( e is AggregateException )
{
foreach (Exception inner in (e as AggregateException).InnerExceptions)
{ Console.WriteLine("\n" + inner.Message); }
}
else if ( e is ServiceException)
{
var ex = e as ServiceException;
Console.WriteLine("\nMessage send response: status code {0}, {1}",
ex.StatusCode, ex.ReasonPhrase);
}
Console.ReadKey(); // Pause terminal
}
}
}
}
See also
Use the Dataverse Web API
Use Web API functions
Use Web API actions
Web API Samples
Web API Functions and Actions Sample
Web API CDSWebApiService Parallel Operations
Sample (C#)
7/19/2021 • 4 minutes to read • Edit Online
NOTE
Unsure about entity vs. table? See Developers: Understand terminology in Microsoft Dataverse.
This example shows how to use a System.Threading.Tasks.Parallel.ForEach Method loop to enable data
parallelism over a set of table rows (entity records) to create in Dataverse.
This sample uses the CDSWebApiService class synchronous methods within operations. Because the
CDSWebApiService class can manage Service Protection API limits, this code can be resilient to the transient
429 errors that clients should expect. It will retry a configurable number of times.
More information:
Web API CDSWebApiService class Sample (C#)
Service Protection API Limits
This sample is based on the How to: Write a simple Parallel.ForEach loop example, but modified to perform
create and delete operations with Dataverse entities using the synchronous methods provided by the
CDSWebApiService class.
NOTE
If you want to use Fiddler to observe the expected service protection API limits, you will need to set the number of rows
to create to be around 10,000. They will start to appear after 5 minutes. Note how the application retries the failures and
completes the flow of all the rows.
Prerequisites
The following is required to build and run the CDSWebApiService C# samples :
Microsoft Visual Studio 2019.
Access to Microsoft Dataverse with privileges to perform CRUD operations.
<add name="Connect"
connectionString="Url=https://yourorg.api.crm.dynamics.com;
Authority=null;
ClientId=51f81489-12ee-4a9e-aaae-a2591f45987d;
RedirectUrl=app://58145B91-0C36-4500-8554-080854F2AC97;
[email protected];
Password=y0urp455w0rd;
CallerObjectId=null;
Version=9.1;
MaxRetries=3;
TimeoutInSeconds=180;
"/>
5. Make sure that the ParallelOperations project is set as the startup project. The name of the project
should be bold to indicate it is the startup project. If the name is not bold, right-click it in the solution
explorer and select Set as Star tup Project .
6. Press F5 to run the program in debug mode.
Code listing
This sample depends on the assembly included in the CDSWebAPIService project. For information on the
methods this class provides see: Web API CDSWebApiService class Sample (C#).
The following is the code from the Program.cs file:
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Configuration;
using System.Threading.Tasks;
namespace PowerApps.Samples
{
internal class Program
{
//Get configuration data from App.config connectionStrings
private static readonly string connectionString =
ConfigurationManager.ConnectionStrings["Connect"].ConnectionString;
var count = 0;
try
{
using (CDSWebApiService svc = new CDSWebApiService(serviceConfig))
{
Console.WriteLine($"Creating {accountsToImport.Count} accounts");
var startCreate = DateTime.Now;
{
//Add the Uri returned to the ConcurrentBag to delete later
accountsToDelete.Add(svc.PostCreate("accounts", account));
});
See also
Use the Dataverse Web API
Web API CDSWebApiService class Sample (C#)
Web API CDSWebApiService Async Parallel Operations Sample (C#)
Web API CDSWebApiService Basic Operations Sample (C#)
Create a table row using the Web API
Update and delete table rows using the Web API
Web API CDSWebApiService Async Parallel
Operations Sample (C#)
7/19/2021 • 3 minutes to read • Edit Online
NOTE
Unsure about entity vs. table? See Developers: Understand terminology in Microsoft Dataverse.
This sample demonstrates using Task Parallel Library (TPL) dataflow components Dataflow (Task Parallel Library)
with asynchronous requests.
TPL provides capabilities to add parallelism and concurrency to applications. These capabilities are an important
part of maximizing throughput when performing operations that will add or update data within Dataverse.
This sample uses the CDSWebApiService class asynchronous methods within asynchronous operations. Because
the CDSWebApiService class can manage Service Protection API limits, this code can be resilient to the transient
429 errors that clients should expect. It will retry a configurable number of times. More information: Service
Protection API Limits
This sample simply creates a configurable number of account rows (records) to create, which it will in turn
delete. This sample uses dataflow components to process the rows and transform the results of the create
operation into the next phase that deletes these rows. Because of the nature of this data flow, delete operations
for previously created rows will start before all the rows to create are finished.
Prerequisites
The following is required to build and run the CDSWebApiService C# samples :
Microsoft Visual Studio 2019.
Access to Microsoft Dataverse with privileges to perform CRUD operations.
5. Make sure that the AsyncParallelOperations project is set as the startup project. The name of the
project should be bold to indicate it is the startup project. If the name is not bold, right-click it in the
solution explorer and select Set as Star tup Project .
6. Press F5 to run the program in debug mode.
Code listing
This sample depends on the assembly included in the CDSWebAPIService project. For information on the
methods this class provides see: Web API CDSWebApiService class Sample (C#).
The following is the code from the Program.cs file:
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;
namespace PowerApps.Samples
{
internal class Program
{
//Get configuration data from App.config connectionStrings
private static readonly string connectionString =
ConfigurationManager.ConnectionStrings["Connect"].ConnectionString;
var count = 0;
double secondsToComplete;
See also
Use the Dataverse Web API
Web API CDSWebApiService class Sample (C#)
Web API CDSWebApiService Basic Operations Sample (C#)
Web API CDSWebApiService Parallel Operations Sample (C#)
Create a table using the Web API
Update and delete table rows using the Web API
Global Discovery Service Sample (C#)
7/19/2021 • 2 minutes to read • Edit Online
NOTE
Unsure about entity vs. table? See Developers: Understand terminology in Microsoft Dataverse.
This sample shows how to access the global Discovery Service using the Web API.
HttpResponseMessage response =
client.GetAsync("api/discovery/v2.0/Instances", HttpCompletionOption.ResponseHeadersRead).Result;
if (response.IsSuccessStatusCode)
{
//Get the response content and parse it.
string result = response.Content.ReadAsStringAsync().Result;
JObject body = JObject.Parse(result);
JArray values = (JArray)body.GetValue("value");
if (!values.HasValues)
{
return new List<Instance>();
}
return JsonConvert.DeserializeObject<List<Instance>>(values.ToString());
}
else
{
throw new Exception(response.ReasonPhrase);
}
}
/// <summary>
/// Object returned from the Discovery Service.
/// </summary>
class Instance
{
public string Id { get; set; }
public string UniqueName { get; set; }
public string UrlName { get; set; }
public string FriendlyName { get; set; }
public int State { get; set; }
public string Version { get; set; }
public string Url { get; set; }
public string ApiUrl { get; set; }
public DateTime LastUpdated { get; set; }
}
See Also
Discover the URL for your organization
Web API Data operations Samples (Client-side
JavaScript)
7/19/2021 • 5 minutes to read • Edit Online
NOTE
Unsure about entity vs. table? See Developers: Understand terminology in Microsoft Dataverse.
This article provides common understanding about Web API samples using client-side JavaScript. While each
sample focuses on a different aspect of Microsoft Dataverse Web API, they all follow similar process and
structure described in this topic.
Web API Basic Operations Sample Web API Basic Operations Sample Demonstrates how to create, retrieve,
(Client-side JavaScript) update, delete, associate and
disassociate Dataverse table rows
(entity records).
Web API Query Data Sample (Client- Web API Query Data Sample Demonstrates how to use OData v4
side JavaScript) query syntax and functions as well as
Dataverse query functions. Includes
demonstration of working with pre-
defined queries and using FetchXML to
perform queries.
Web API Conditional Operations Web API Conditional Operations Demonstrates how to perform
Sample (Client-side JavaScript) Sample conditional operations. The behavior of
these operations depends on criteria
you specify.
Web API Functions and Actions Web API Functions and Actions Demonstrates how to use bound and
Sample (Client-side JavaScript) Sample unbound functions and actions,
including custom actions.
/**
* @function request
* @description Generic helper function to handle basic XMLHttpRequest calls.
* @param {string} action - The request action. String is case-sensitive.
* @param {string} uri - An absolute or relative URI. Relative URI starts with a "/".
* @param {object} data - An object representing an entity. Required for create and update actions.
* @returns {Promise} - A Promise that returns either the request object or an error object.
*/
Sdk.request = function (action, uri, data) {
if (!RegExp(action, "g").test("POST PATCH PUT GET DELETE")) { // Expected action verbs.
throw new Error("Sdk.request: action parameter must be one of the following: " +
"POST, PATCH, PUT, GET, or DELETE.");
}
if (!typeof uri === "string") {
throw new Error("Sdk.request: uri parameter must be a string.");
}
if ((RegExp(action, "g").test("POST PATCH PUT")) && (data === null || data === undefined)) {
throw new Error("Sdk.request: data parameter must not be null for operations that create or
modify data.");
}
}
};
request.send(JSON.stringify(data));
});
};
The Sdk.request function returns a promise. When the request wrapped by the promise is completed,
the promise is either resolved or rejected. If it is resolved, the function in the following then method will
be called. If it is rejected, the function in the following catch method will be called. If the function within
the then method itself returns a promise, the chain of operations within consecutive then methods can
continue. Returning a promise allows us to chain these sample operations together in a way that is
preferred by many developers to traditional callback functions. For more information about promise, see
JavaScript Promise.
See also
Use the Dataverse Web API
Web API Samples
Web API Samples (C#)
Web API Basic Operations Sample (Client-side
JavaScript)
7/19/2021 • 16 minutes to read • Edit Online
NOTE
Unsure about entity vs. table? See Developers: Understand terminology in Microsoft Dataverse.
This sample demonstrates how to perform basic CRUD (create, retrieve, update, and delete) and association and
dissociation operations on tables rows (entity records) using client-side JavaScript.
NOTE
This sample implements the operations detailed in the Web API Basic Operations Sample and uses the common JavaScript
constructs described in Web API Samples (Client-side JavaScript)
Prerequisites
To run this sample, the following is required:
Access to Microsoft Dataverse environment.
A user account with privileges to import solutions and perform CRUD operations, typically a system
administrator or system customizer security role.
Code sample
This sample includes two web resources:
WebAPIBasicOperations.html
WebAPIBasicOperations.js
WebAPIBasicOperations.html
The WebAPIBasicOperations.html web resource provides the context in which the JavaScript code will run.
<html>
<head>
<title>Microsoft CRM Web API Basic Operations Example</title>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=Edge" />
<script src="../ClientGlobalContext.js.aspx" type="text/javascript"></script>
<script src="scripts/es6promise.js" type="text/javascript"></script>
<script src="scripts/WebAPIBasicOperations.js" type="text/javascript"></script>
<style type="text/css">
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
#preferences {
border: inset;
padding: 10px 10px;
}
#output_area {
border: inset;
background-color: gainsboro;
padding: 10px 10px;
}
</style>
</head>
<body>
<h1>Microsoft CRM Web API Basic Operations Example</h1>
<p>This page demonstrates the CRM Web API's basic operations using JavaScript.</p>
<h2>Instructions</h2>
<p>
Choose your preferences and run the JavaScript code.
Use your browser's developer tools to view the output written to the console (e.g.: in IE 11 or
Microsoft Edge,
press F12 to load the Developer Tools).
</p>
<p>
Remove sample data (Choose whether you want to delete sample data created during this execution):
<br />
<input name="removesampledata" type="radio" value="yes" checked />
Yes
<input name="removesampledata" type="radio" value="no" />
No
</p>
<input type="button" name="start_sample" value="Start Sample" onclick="Sdk.startSample()" />
</body>
</html>
WebAPIBasicOperations.js
The WebAPIBasicOperations.js web resource is the JavaScript library that defines the operations this sample
performs.
"use strict";
var Sdk = window.Sdk || {};
/**
* @function getClientUrl
* @description Get the client URL.
* @returns {string} The client URL.
*/
Sdk.getClientUrl = function () {
Sdk.getClientUrl = function () {
var context;
// GetGlobalContext defined by including reference to
// ClientGlobalContext.js.aspx in the HTML page.
if (typeof GetGlobalContext != "undefined") {
context = GetGlobalContext();
} else {
if (typeof Xrm != "undefined") {
// Xrm.Page.context defined within the Xrm.Page object model for form scripts.
context = Xrm.Page.context;
} else {
throw new Error("Context is not available.");
}
}
return context.getClientUrl();
};
/**
* An object instantiated to manage detecting the
* Web API version in conjunction with the
* Sdk.retrieveVersion function
*/
Sdk.versionManager = new function () {
//Start with base version
var _webAPIMajorVersion = 8;
var _webAPIMinorVersion = 0;
//Use properties to increment version and provide WebAPIPath string used by Sdk.request;
Object.defineProperties(this, {
"WebAPIMajorVersion": {
get: function () {
return _webAPIMajorVersion;
},
set: function (value) {
if (typeof value != "number") {
throw new Error("Sdk.versionManager.WebAPIMajorVersion property must be a number.")
}
_webAPIMajorVersion = parseInt(value, 10);
}
},
"WebAPIMinorVersion": {
get: function () {
return _webAPIMinorVersion;
},
set: function (value) {
if (isNaN(value)) {
throw new Error("Sdk.versionManager._webAPIMinorVersion property must be a number.")
}
_webAPIMinorVersion = parseInt(value, 10);
}
},
"WebAPIPath": {
get: function () {
return "/api/data/v" + _webAPIMajorVersion + "." + _webAPIMinorVersion;
}
}
})
/**
* @function request
* @description Generic helper function to handle basic XMLHttpRequest calls.
* @param {string} action - The request action. String is case-sensitive.
* @param {string} uri - An absolute or relative URI. Relative URI starts with a "/".
* @param {object} data - An object representing an entity. Required for create and update actions.
* @param {object} addHeader - An object with header and value properties to add to the request
* @returns {Promise} - A Promise that returns either the request object or an error object.
*/
Sdk.request = function (action, uri, data, addHeader) {
if (!RegExp(action, "g").test("POST PATCH PUT GET DELETE")) { // Expected action verbs.
throw new Error("Sdk.request: action parameter must be one of the following: " +
"POST, PATCH, PUT, GET, or DELETE.");
}
if (!typeof uri === "string") {
throw new Error("Sdk.request: uri parameter must be a string.");
}
if ((RegExp(action, "g").test("POST PATCH PUT")) && (!data)) {
throw new Error("Sdk.request: data parameter must not be null for operations that create or modify
data.");
}
if (addHeader) {
if (typeof addHeader.header != "string" || typeof addHeader.value != "string") {
throw new Error("Sdk.request: addHeader parameter must have header and value properties that are
strings.");
}
}
/**
* @function startSample
* @description Runs the sample.
* This sample demonstrates basic CRUD+ operations.
* Results are sent to the debugger's console window.
*/
Sdk.startSample = function () {
// Initializing.
Sdk.SampleVariables.deleteData = document.getElementsByName("removesampledata")[0].checked;
Sdk.SampleVariables.entitiesToDelete = []; // Reset the array.
Sdk.SampleVariables.contact1Uri = "";
Sdk.SampleVariables.account1Uri = "";
Sdk.SampleVariables.account2Uri = "";
Sdk.SampleVariables.contact2Uri = "";
Sdk.SampleVariables.opportunity1Uri = "";
Sdk.SampleVariables.competitor1Uri = "";
/**
* Behavior of this sample varies by version
* So starting by retrieving the version;
*/
Sdk.retrieveVersion()
.then(function () {
return Sdk.basicCreateAndUpdatesAsync()
})
.then(function () {
return Sdk.createWithAssociationAsync()
})
.then(function () {
return Sdk.createRelatedAsync()
})
.then(function () {
return Sdk.associateExistingAsync()
})
.then(function () {
return Sdk.deleteSampleData()
})
.catch(function (err) {
console.log("ERROR: " + err.message);
});
Sdk.retrieveVersion = function () {
return new Promise(function (resolve, reject) {
Sdk.request("GET", "/RetrieveVersion")
.then(function (request) {
try {
var RetrieveVersionResponse = JSON.parse(request.response);
var fullVersion = RetrieveVersionResponse.Version;
var versionData = fullVersion.split(".");
Sdk.versionManager.WebAPIMajorVersion = parseInt(versionData[0], 10);
Sdk.versionManager.WebAPIMinorVersion = parseInt(versionData[1], 10);
resolve();
} catch (err) {
reject(new Error("Error processing version: " + err.message))
}
})
.catch(function (err) {
reject(new Error("Error retrieving version: " + err.message))
})
});
};
Sdk.basicCreateAndUpdatesAsync = function () {
return new Promise(function (resolve, reject) {
// Section 1.
//
// Create the contact using POST request.
// A new entry will be added regardless if a contact with this info already exists in the system or not.
console.log("--Section 1 started--");
var contact = {};
contact.firstname = "Peter";
contact.lastname = "Cambel";
// NOTE: For performance best practices, use $select to limit the properties you want to return
// See also: https://msdn.microsoft.com/library/gg334767.aspx#bkmk_requestProperties
var query = "?$select=" + properties;
return Sdk.request("GET", Sdk.SampleVariables.contact1Uri + query, null);
})
.then(function (request) {
// Process response from previous request.
var contact1 = JSON.parse(request.response);
var successMsg = "Contact '%s' retrieved:\n"
+ "\tAnnual income: %s \n"
+ "\tJob title: %s \n"
+ "\tDescription: %s";
console.log(successMsg,
contact1.fullname, // This property is read-only. Calculated from firstname and lastname.
contact1.annualincome,
contact1.jobtitle,
contact1.description); // Description will be "null" because it has not been set yet.
Sdk.createWithAssociationAsync = function () {
return new Promise(function (resolve, reject) {
// Section 2.
//
// Create a new account entity and associate it with an existing contact using POST request.
console.log("\n--Section 2 started--");
var account = {};
account.name = "Contoso, Ltd.";
account.telephone1 = "555-5555";
account["[email protected]"] = Sdk.SampleVariables.contact1Uri; //relative URI ok. E.g.:
"/contacts(###)".
"/contacts(###)".
Sdk.createRelatedAsync = function () {
return new Promise(function (resolve, reject) {
// Section 3.
//
// Create related entities (deep insert).
// Create the following entities in one operation using deep insert technique:
// account
// |--- contact
// |--- tasks
// Then retrieve properties of these entities
//
// Constructing the entity relationship.
console.log("\n--Section 3 started--");
var account = {};
account.name = "Fourth Coffee";
account.primarycontactid = {
firstname: "Susie",
lastname: "Curtis",
jobtitle: "Coffee Master",
annualincome: 48000.00,
Contact_Tasks: [
{
subject: "Sign invoice",
description: "Invoice #12321",
scheduledend: new Date("April 19th, 2016")
},
{
{
subject: "Setup new display",
description: "Theme is - Spring is in the air",
scheduledstart: new Date("4/20/2016")
},
{
subject: "Conduct training",
description: "Train team on making our new blended coffee",
scheduledstart: new Date("6/1/2016")
}
]
};
Sdk.associateExistingAsync = function () {
return new Promise(function (resolve, reject) {
// Section 4
//
// Entity associations:
// Associate to existing entities via the different relationship types:
// 1) 1:N relationship - Associate an existing contact to an existing account
// (e.g.: contact - Peter Cambel to account - Fourth Coffee).
// 2) N:N relationship - Associate an competitor to opportunity.
console.log("\n--Section 4 started--");
var contact = {};
contact["@odata.id"] = Sdk.SampleVariables.contact1Uri;
Sdk.deleteSampleData = function () {
return new Promise(function (resolve, reject) {
});
};
See also
Use the Dataverse Web API
Create a table row using the Web API
Retrieve a table row using the Web API
Update and delete table rows using the Web API
Web API Samples
Web API Basic Operations Sample
Web API Basic Operations Sample (C#)
Web API Samples (Client-side JavaScript)
Web API Query Data Sample (Client-side JavaScript)
Web API Conditional Operations Sample (Client-side JavaScript)
Web API Functions and Actions Sample (Client-side JavaScript)
Web API Conditional Operations Sample (Client-
side JavaScript)
7/19/2021 • 8 minutes to read • Edit Online
NOTE
Unsure about entity vs. table? See Developers: Understand terminology in Microsoft Dataverse.
This sample demonstrates how to perform conditional operations using Microsoft Dataverse Web API using
client-side JavaScript.
NOTE
This sample implements the operations detailed in the Web API Conditional Operations Sample and uses the common
client-side JavaScript constructs described in Web API Samples (Client-side JavaScript)
Prerequisites
To run this sample, the following is required:
Access to Dataverse environment.
A user account with privileges to import solutions and perform CRUD operations, typically a system
administrator or system customizer security role.
Code sample
This sample includes two web resources:
WebAPIConditionalOperations.html
WebAPIConditionalOperations.js
WebAPIConditionalOperations.html
The WebAPIConditionalOperations.html web resource provides the context in which the JavaScript code will run.
<!DOCTYPE html>
<html>
<head>
<title>Microsoft CRM Web API Conditional Operations Example</title>
<meta charset="utf-8" />
<script src="../ClientGlobalContext.js.aspx" type="text/javascript"></script>
<script src="scripts/es6promise.js"></script>
<script src="scripts/WebAPIConditionalOperations.js"></script>
<style type="text/css">
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
</style>
</head>
<body>
<h1>Microsoft CRM Web API Conditional Operations Example</h1>
<p>This page demonstrates the CRM Web API's Conditional Operations using JavaScript.</p>
<h2>Instructions</h2>
<p>
Choose your preferences and run the JavaScript code.
Use your browser's developer tools to view the output written to the console (e.g.: in IE11 or Microsoft
Edge,
press F12 to load the Developer Tools).
</p>
<form id="preferences">
<p>This sample deletes the single record it creates.</p>
<input type="button" name="start_samples" value="Start Sample" onclick="Sdk.startSample()" />
</form>
</body>
</html>
WebAPIConditionalOperations.js
The WebAPIConditionalOperations.js web resource is the JavaScript library that defines the operations this
sample performs.
"use strict";
var Sdk = window.Sdk || {};
/**
* @function getClientUrl
* @description Get the client URL.
* @return {string} The client URL.
*/
Sdk.getClientUrl = function () {
var context;
// GetGlobalContext defined by including reference to
// ClientGlobalContext.js.aspx in the HTML page.
if (typeof GetGlobalContext != "undefined")
{ context = GetGlobalContext(); }
else
{
if (typeof Xrm != "undefined") {
// Xrm.Page.context defined within the Xrm.Page object model for form scripts.
context = Xrm.Page.context;
}
else { throw new Error("Context is not available."); }
}
return context.getClientUrl();
}
// Global variables.
var clientUrl = Sdk.getClientUrl(); // e.g.: https://org.crm.dynamics.com
var webAPIPath = "/api/data/v8.1"; // Path to the web API.
var webAPIPath = "/api/data/v8.1"; // Path to the web API.
var account1Uri; // e.g.: Contoso Ltd (sample)
var initialAcctETagVal; // The initial ETag value of the account created
var updatedAcctETagVal; // The ETag value of the account after it is updated
/**
* @function request
* @description Generic helper function to handle basic XMLHttpRequest calls.
* @param {string} action - The request action. String is case-sensitive.
* @param {string} uri - An absolute or relative URI. Relative URI starts with a "/".
* @param {object} data - An object representing an entity. Required for create and update actions.
* @param {object} addHeader - An object with header and value properties to add to the request
* @returns {Promise} - A Promise that returns either the request object or an error object.
*/
Sdk.request = function (action, uri, data, addHeader) {
if (!RegExp(action, "g").test("POST PATCH PUT GET DELETE")) { // Expected action verbs.
throw new Error("Sdk.request: action parameter must be one of the following: " +
"POST, PATCH, PUT, GET, or DELETE.");
}
if (!typeof uri === "string") {
throw new Error("Sdk.request: uri parameter must be a string.");
}
if ((RegExp(action, "g").test("POST PATCH PUT")) && (!data)) {
throw new Error("Sdk.request: data parameter must not be null for operations that create or modify
data.");
}
if (addHeader) {
if (typeof addHeader.header != "string" || typeof addHeader.value != "string") {
throw new Error("Sdk.request: addHeader parameter must have header and value properties that are
strings.");
}
}
/**
* @function startSample
* @description Runs the sample.
* This sample demonstrates conditional operations using CRM Web API.
* Results are sent to the debugger's console window.
*/
Sdk.startSample = function () {
// Initializing...
console.log("-- Sample started --");
},
// Catch error.
function (error) {
// Not found error expected.
console.log("Expected Error: %s", error.message);
console.log("\tAccount not updated because it doesn't exist.");
}
)
.catch(function (error) {
console.log(error.message);
});
}
See also
Use the Dataverse Web API
Perform conditional operations using the Web API
Web API Samples
Web API Conditional Operations Sample
Web API Conditional Operations Sample (C#)
Web API Samples (Client-side JavaScript)
Web API Basic Operations Sample (Client-side JavaScript)
Web API Query Data Sample (Client-side JavaScript)
Web API Functions and Actions Sample (Client-side JavaScript)
Web API Functions and Actions Sample (Client-side
JavaScript)
7/19/2021 • 15 minutes to read • Edit Online
NOTE
Unsure about entity vs. table? See Developers: Understand terminology in Microsoft Dataverse.
This sample demonstrates how to perform bound and unbound functions and actions, including custom actions,
using the Microsoft Dataverse Web API using client-side JavaScript.
NOTE
This sample implements the operations detailed in the Web API Functions and Actions Sample and uses the common
client-side JavaScript constructs described in Web API Samples (Client-side JavaScript)
In this section
Prerequisites
Run this sample
Code sample
Prerequisites
To run this sample, the following is required:
Access to Dataverse environment.
A user account with privileges to import solutions and perform CRUD operations, typically a system
administrator or system customizer security role.
Code sample
This sample includes two web resources:
WebAPIFunctionsAndActions.html
WebAPIFunctionsAndActions.js
WebAPIFunctionsAndActions.html
The WebAPIFunctionsAndActions.html web resource provides the context in which the JavaScript code will run.
<!DOCTYPE html>
<html>
<head>
<title>Microsoft CRM Web API Functions and Actions Example</title>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=Edge" />
<script src="../ClientGlobalContext.js.aspx" type="text/javascript"></script>
<script src="scripts/es6promise.js"></script>
<script src="scripts/WebAPIFunctionsAndActions.js"></script>
<style type="text/css">
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
#preferences {
border: inset;
padding: 10px 10px;
}
#output_area {
border: inset;
background-color: gainsboro;
padding: 10px 10px;
}
</style>
</head>
<body>
<h1>Microsoft CRM Web API Functions and Actions Example</h1>
<p>This page demonstrates the CRM Web API's Functions and Actions using JavaScript.</p>
<h2>Instructions</h2>
<p>
Choose your preferences and run the JavaScript code.
Use your browser's developer tools to view the output written to the console (e.g.: in IE11 or Microsoft
Edge,
press F12 to load the Developer Tools).
</p>
<form id="preferences">
<p>
Remove sample data (Choose whether you want to delete sample data created for this sample):<br />
<input name="removesampledata" type="radio" value="yes" checked /> Yes
<input name="removesampledata" type="radio" value="no" /> No
</p>
<input type="button" name="start_samples" value="Start Samples" onclick="Sdk.startSample()" />
</form>
</body>
</html>
WebAPIFunctionsAndActions.js
The WebAPIFunctionsAndActions.js web resource is the JavaScript library that defines the operations this
sample performs.
"use strict";
var Sdk = window.Sdk || {};
/**
* @function getClientUrl
* @description Get the client URL.
* @return {string} The client URL.
*/
Sdk.getClientUrl = function () {
Sdk.getClientUrl = function () {
var context;
// GetGlobalContext defined by including reference to
// ClientGlobalContext.js.aspx in the HTML page.
if (typeof GetGlobalContext != "undefined") {
context = GetGlobalContext();
} else {
if (typeof Xrm != "undefined") {
// Xrm.Page.context defined within the Xrm.Page object model for form scripts.
context = Xrm.Page.context;
} else {
throw new Error("Context is not available.");
}
}
return context.getClientUrl();
};
// Global variables
var entitiesToDelete = []; // Entity URIs to be deleted later
// (if user chooses to delete sample data).
var deleteData = true; // Controls whether sample data are deleted at the end of this
sample run.
var clientUrl = Sdk.getClientUrl(); // ie.: https://org.crm.dynamics.com
var webAPIPath = "/api/data/v8.1"; // Path to the web API.
var incidentUri; // Incident created with three closed tasks.
var opportunityUri; // Closed opportunity to re-open before deleting.
var letterUri; // Letter to add to contact's queue.
var myQueueUri; // The contact's queue uri.
var contactUri; // Add a note to this contact.
var CUSTOMERACCOUNTNAME = "Account Customer Created in WebAPIFunctionsAndActions sample"; // For custom
action.
/**
* @function getWebAPIPath
* @description Get the full path to the Web API.
* @return {string} The full URL of the Web API.
*/
Sdk.getWebAPIPath = function () {
return Sdk.getClientUrl() + webAPIPath;
}
/**
* @function request
* @description Generic helper function to handle basic XMLHttpRequest calls.
* @param {string} action - The request action. String is case-sensitive.
* @param {string} uri - An absolute or relative URI. Relative URI starts with a "/".
* @param {object} data - An object representing an entity. Required for create and update actions.
* @param {object} addHeader - An object with header and value properties to add to the request
* @returns {Promise} - A Promise that returns either the request object or an error object.
*/
Sdk.request = function (action, uri, data, addHeader) {
if (!RegExp(action, "g").test("POST PATCH PUT GET DELETE")) { // Expected action verbs.
throw new Error("Sdk.request: action parameter must be one of the following: " +
"POST, PATCH, PUT, GET, or DELETE.");
}
if (!typeof uri === "string") {
throw new Error("Sdk.request: uri parameter must be a string.");
}
if ((RegExp(action, "g").test("POST PATCH PUT")) && (!data)) {
throw new Error("Sdk.request: data parameter must not be null for operations that create or modify
data.");
}
if (addHeader) {
if (typeof addHeader.header != "string" || typeof addHeader.value != "string") {
throw new Error("Sdk.request: addHeader parameter must have header and value properties that are
strings.");
}
}
/**
* @function Sdk.startSample
* @description Initiates a chain of promises to show use of Functions and Actions with the Web API.
* Functions and actions represent re-usable operations you can perform using the Web API.
* For more info, see https://msdn.microsoft.com/library/mt607990.aspx#bkmk_actions
* The following standard CRM Web API functions and actions are invoked:
* - WhoAmI, a basic unbound function
* - GetTimeZoneCodeByLocalizedName, an unbound function that requires parameters
* - CalculateTotalTimeIncident, a bound function
* - WinOpportunity, an unbound action that takes parameters
* - AddToQueue, a bound action that takes parameters
* - In addition, a custom bound and an unbound action contained within the solution are invoked.
*/
Sdk.startSample = function () {
// Initializing.
deleteData = document.getElementsByName("removesampledata")[0].checked;
entitiesToDelete = []; // Reset the array.
var parameters = {
Target: {
activityid: letterActivityId,
"@odata.type": "Microsoft.Dynamics.CRM.letter"
}
}
//Adding the letter to the user's default queue.
return Sdk.request("POST", myQueueUri + "/Microsoft.Dynamics.CRM.AddToQueue", parameters);
})
.then(function (request) {
var queueItemId = JSON.parse(request.response).QueueItemId;
console.log("\tQueueItemId returned from AddToQueue Action: %s\n", queueItemId);
var parameters = {
CustomerType: "account",
AccountName: CUSTOMERACCOUNTNAME
}
/**
* @function Sdk.deleteEntities
* @description Deletes the entities created by this sample
*/
Sdk.deleteEntities = function () {
Sdk.deleteEntities = function () {
return new Promise(function (resolve, reject) {
// Adding to the opportunityclose URI it will get deleted before the opportunity.
entitiesToDelete.unshift(opportunityCloseUri)
/*
These deletions have to be done consecutively in a specific order to avoid a Generic SQL error
which can occur because of relationship behavior actions for the delete event.
*/
/**
* @function Sdk.getUsersFullName
* @description Retrieves the current user's full name.
* @returns {Promise} - A Promise that returns the full name of the user
*/
Sdk.getUsersFullName = function () {
return new Promise(function (resolve, reject) {
//Use WhoAmI Function (https://msdn.microsoft.com/library/mt607925.aspx)
Sdk.request("GET", "/WhoAmI")
.then(function (request) {
//Returns WhoAmIResponse ComplexType (https://msdn.microsoft.com/library/mt607982.aspx)
var myId = JSON.parse(request.response).UserId;
var myId = JSON.parse(request.response).UserId;
//Retrieve the systemuser Entity fullname property (https://msdn.microsoft.com/library/mt608065.aspx)
return Sdk.request("GET", "/systemusers(" + myId + ")?$select=fullname")
})
.then(function (request) {
//Return the users full name
resolve(JSON.parse(request.response).fullname);
})
.catch(function (err) {
reject("Error in Sdk.getUsersFullName function: " + err.message);
});
});
};
/**
* @function Sdk.createRequiredRecords
* @description Creates data required by this sample program.
* - Create an account with three 30 minute tasks.
* - Create another account associated with an opportunity.
* - Create a letter.
* - Create a contact.
* @returns {Promise} - resolve the promise if all goes well; reject otherwise.
*/
Sdk.createRequiredRecords = function () {
console.log("-- Creating sample data --");
// Create a parent account, an associated incident with three
// associated tasks(required for CalculateTotalTimeIncident).
return new Promise(function (resolve, reject) {
Sdk.createAccountWithIncidentAndThree30MinuteClosedTasks()
.then(function (iUri) {
incidentUri = iUri;
resolve()
})
.catch(function (err) {
reject("Error in Sdk.createRequiredRecords function: " + err.message);
});
});
}
/**
* @function Sdk.createAccountwithIncidentAndThree30MinuteClosedTasks
* @description Create an account and associate three 30 minute tasks. Close the tasks.
* @description Create an account and associate three 30 minute tasks. Close the tasks.
* @returns {Promise} - A Promise that returns the uri of an incident created.
*/
Sdk.createAccountWithIncidentAndThree30MinuteClosedTasks = function () {
return new Promise(function (resolve, reject) {
var iUri; // incidentUri
// Create a parent account for the incident.
Sdk.request("POST", "/accounts", { name: "Fourth Coffee" })
.then(function (request) {
// Capture the URI of the created account so it can be deleted later.
var accountUri = request.getResponseHeader("OData-EntityId");
entitiesToDelete.push(accountUri);
// Define an incident associated with the account with three related tasks.
// Each task has a 30 minute duration.
var incident = {
title: "Sample Case",
"[email protected]": accountUri,
Incident_Tasks: [
{
subject: "Task 1",
actualdurationminutes: 30
},
{
subject: "Task 2",
actualdurationminutes: 30
},
{
subject: "Task 3",
actualdurationminutes: 30
}
]
};
// Create the incident and related tasks.
return Sdk.request("POST", "/incidents", incident)
})
.then(function (request) {
iUri = request.getResponseHeader("OData-EntityId");
/**
* @function Sdk.createAccountwithOpportunityToWin
* @description Create an account and an associated opportunity.
* @returns {Promise} - A Promise that returns the uri of an opportunity.
*/
Sdk.createAccountWithOpportunityToWin = function () {
return new Promise(function (resolve, reject) {
var accountUri;
var account = {
name: "Sample Account for WebAPIFunctionsAndActions sample",
opportunity_customer_accounts: [{
name: "Opportunity to win"
}]
};
Sdk.request("POST", "/accounts", account) // Create the account.
.then(function (request) {
accountUri = request.getResponseHeader("OData-EntityId");
entitiesToDelete.push(accountUri);
See also
Use the Dataverse Web API
Use Web API functions
Use Web API actions
Web API Samples
Web API Functions and Actions Sample
Web API Functions and Actions Sample (C#)
Web API Samples (Client-side JavaScript)
Web API Basic Operations Sample (Client-side JavaScript)
Web API Query Data Sample (Client-side JavaScript)
Web API Conditional Operations Sample (Client-side JavaScript)
Web API Query Data Sample (Client-side
JavaScript)
7/19/2021 • 19 minutes to read • Edit Online
NOTE
Unsure about entity vs. table? See Developers: Understand terminology in Microsoft Dataverse.
This sample demonstrates how to perform basic query requests using the Microsoft Dataverse Web API using
client-side JavaScript.
NOTE
This sample implements the operations detailed in the Web API Query Data Sample and uses the common client-side
JavaScript constructs described in Web API Samples (Client-side JavaScript)
Prerequisites
To run this sample, the following is required:
Access to Dataverse environment.
A user account with privileges to import solutions and perform CRUD operations, typically a system
administrator or system customizer security role.
Code sample
This sample includes two web resources:
WebAPIQuery.html
WebAPIQuery.js
WebAPIQuery.html
The WebAPIQuery.html web resource provides the context in which the JavaScript code will run.
<!DOCTYPE html>
<html>
<head>
<title>Microsoft CRM Web API Query Example</title>
<meta charset="utf-8" />
<script src="../ClientGlobalContext.js.aspx" type="text/javascript"></script>
<script src="scripts/es6promise.js"></script>
<script src="scripts/WebAPIQuery.js"></script>
<style type="text/css">
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
#preferences {
border: inset;
padding: 10px 10px;
}
#output_area {
border: inset;
background-color: gainsboro;
padding: 10px 10px;
}
</style>
</head>
<body>
<h1>Microsoft CRM Web API Query Example</h1>
<p>This page demonstrates the CRM Web API's Query operations using JavaScript.</p>
<h2>Instructions</h2>
<p>Choose your preferences and run the JavaScript code.
Use your browser's developer tools to view the output written to the console (e.g.: in IE 11 or
Microsoft Edge,
press F12 to load the Developer Tools).</p>
<form id="preferences">
<p>
Remove sample data (Choose whether you want to delete sample data created during this
execution):
<br />
<input name="removesampledata" type="radio" value="yes" checked /> Yes
<input name="removesampledata" type="radio" value="no" /> No
</p>
<input type="button" name="start_samples" value="Start Sample" onclick="Sdk.startSample()" />
</form>
</body>
</html>
WebAPIQuery.js
The WebAPIQuery.js web resource is the JavaScript library that defines the operations this sample performs.
"use strict";
var Sdk = window.Sdk || {};
/**
* @function getClientUrl
* @description Get the client URL.
* @returns {string} The client URL.
*/
Sdk.getClientUrl = function () {
var context;
// GetGlobalContext defined by including reference to
// ClientGlobalContext.js.aspx in the HTML page.
if (typeof GetGlobalContext != "undefined")
{ context = GetGlobalContext(); }
{ context = GetGlobalContext(); }
else
{
if (typeof Xrm != "undefined") {
// Xrm.Page.context defined within the Xrm.Page object model for form scripts.
context = Xrm.Page.context;
}
else { throw new Error("Context is not available."); }
}
return context.getClientUrl();
}
// Global variables.
var entitiesToDelete = []; // Entity URIs to be deleted (if user chooses to delete sample data)
var deleteData = true; // Delete data by default unless user chooses not to delete.
var clientUrl = Sdk.getClientUrl(); // e.g.: https://org.crm.dynamics.com
var webAPIPath = "/api/data/v8.1"; // Path to the web API.
var account1Uri; // e.g.: Contoso Inc (sample)
var contact1Uri; // e.g.: Yvonne McKey (sample)
var page2Uri; // URI of next page in pagination sample.
/**
* @function request
* @description Generic helper function to handle basic XMLHttpRequest calls.
* @param {string} action - The request action. String is case-sensitive.
* @param {string} uri - An absolute or relative URI. Relative URI starts with a "/".
* @param {object} data - An object representing an entity. Required for create and update action.
* @param {boolean} formattedValue - If "true" then include formatted value; "false" otherwise.
* For more info on formatted value, see:
* https://msdn.microsoft.com/library/gg334767.aspx#bkmk_includeFormattedValues
* @param {number} maxPageSize - Indicate the page size. Default is 10 if not defined.
* @returns {Promise} - A Promise that returns either the request object or an error object.
*/
Sdk.request = function (action, uri, data, formattedValue, maxPageSize) {
if (!RegExp(action, "g").test("POST PATCH PUT GET DELETE")) { // Expected action verbs.
throw new Error("Sdk.request: action parameter must be one of the following: " +
"POST, PATCH, PUT, GET, or DELETE.");
}
if (!typeof uri === "string") {
throw new Error("Sdk.request: uri parameter must be a string.");
}
if ((RegExp(action, "g").test("POST PATCH PUT")) && (data === null || data === undefined)) {
throw new Error("Sdk.request: data parameter must not be null for operations that create or modify
data.");
}
if (maxPageSize === null || maxPageSize === undefined) {
maxPageSize = 10; // Default limit is 10 entities per page.
}
/**
* @funnction output
* @description Generic helper function to output data to console.
* @param {array} collection - Array of entities.
* @param {string} label - Text label for what the collection contains.
* @param {array} properties - Array of properties appropriate for the collection.
*/
Sdk.output = function (collection, label, properties) {
console.log(label);
collection.forEach(function (row, i) {
var prop = [];
properties.forEach(function (p) {
var f = p + "@OData.Community.Display.V1.FormattedValue";
prop.push((row[f] ? row[f] : row[p])); // Get formatted value if one exists for this property.
})
console.log("\t%s) %s", i + 1, prop.join(", "));
});
}
/**
* @function startSample
* @description Runs the sample.
* This sample demonstrates basic query operations.
* Results are sent to the debugger's console window.
*/
Sdk.startSample = function () {
// Initializing...
deleteData = document.getElementsByName("removesampledata")[0].checked;
entitiesToDelete = []; //Reset the array.
account1Uri = "";
contact1Uri = "";
page2Uri = "";
// Basic query:
// Query using $select option against a contact entity to get the properties you want.
// For performance best practice, always use $select otherwise all properties are returned.
console.log("-- Basic Query --");
var query = "?$select=" + contactProperties.join(); // Array defined in the global scope.
return Sdk.request("GET", contact1Uri + query, null, true);
})
.then(function (request) {
var contact1 = JSON.parse(request.response);
console.log("Contact basic info:\n\tFullname: '%s'\n\tJobtitle: '%s'\n\tAnnualincome: '%s'
(unformatted)",
contact1.fullname, contact1.jobtitle, contact1.annualincome);
console.log("\tAnnualincome: %s (formatted)\n",
contact1["[email protected]"]);
// Filter criteria:
// Applying filters to get targeted data.
// 1) Using standard query functions (e.g.: contains, endswith, startswith)
// 2) Using CRM query functions (e.g.: LastXhours, Last7Days, Today, Between, In, ...)
// 3) Using filter operators and logical operators (e.g.: eq, ne, gt, and, or, etc…)
// 4) Set precedence using parenthesis (e.g.: ((criteria1) and (criteria2)) or (criteria3)
// For more info, see: https://msdn.microsoft.com/library/gg334767.aspx#bkmk_filter
console.log("-- Filter Criteria --");
// Order results:
// Filtered results can be order in descending or ascending order.
console.log("\n-- Order Results --");
var filter = "&$filter=contains(fullname,'(sample)') " +
"&$orderby=jobtitle asc, annualincome desc";
var query = "?$select=" + contactProperties.join() + filter;
return Sdk.request("GET", "/contacts" + query, null, true);
})
.then(function (request) {
var collection = JSON.parse(request.response).value;
Sdk.output(collection, "Contacts ordered by jobtitle (Ascending) and annualincome (descending):",
contactProperties);
// Parameterized Aliases.
// Aliases can be used as parameters in a query. These parameters can be used in $filter and
$orderby options.
// Using the previous operation as basis, parameterizing the query will give us the same results.
// For more info, see:
https://msdn.microsoft.com/library/gg309638.aspx#bkmk_passParametersToFunctions
console.log("\n-- Parameterized Aliases --");
var filter = "&$filter=contains(@p1,'(sample)') " +
"&$orderby=@p2 asc, @p3 desc&@p1=fullname&@p2=jobtitle&@p3=annualincome";
var query = "?$select=" + contactProperties.join() + filter;
return Sdk.request("GET", "/contacts" + query, null, true);
})
.then(function (request) {
var collection = JSON.parse(request.response).value;
Sdk.output(collection, "Contacts list using parameterized aliases:", contactProperties);
// Result count.
// Count the number of results matching the filter criteria.
// 1) Get a count of a collection without the data.
// 2) Get a count along with the data.
// HINT: Use count together with the "odata.maxpagesize" to calculate the number of pages in the
query.
// NOTE: CRM has a max record limit of 5000 records per response.
console.log("\n-- Result Count --");
return Sdk.request("GET", "/contacts/$count"); // Count is returned in response body.
})
.then(function (request) {
console.log("The contacts collection has %s contacts.", request.response); // Count maximum is 5000.
// Pagination:
// For large data sets, you can limit the number of records returned per page.
// Then offer a "next page" and "previous page" links for users to browse through all the data.
// NOTE: This is why you should not use $top with maxpagesize. $top will limit results returned
// preventing you from accessing all possible results in the query.
// For example: If your query has 10 entities in the result and you limit your result to
$top=5
// then, you can't get to the remaining 5 results; but with "maxpagesize" (without $top), you
can.
// HINT: Save the URI of the current page so users can go "next" and "previous".
console.log("\n-- Pagination --");
var filter = "&$filter=contains(fullname,'(sample)')&$count=true";
var query = "?$select=" + contactProperties.join() + filter;
return Sdk.request("GET", "/contacts" + query, null, true, 4); // 4 records per page.
})
.then(function (request) {
var count = JSON.parse(request.response)["@odata.count"];
var maxpages = Math.ceil(count / 4);
console.log("Contacts total: %s \tContacts per page: %s.\tOutputting first 2 pages.", count, 4);
var collection = JSON.parse(request.response).value;
Sdk.output(collection, "Page 1 of " + maxpages + ":", contactProperties);
Sdk.output(collection, "Page 1 of " + maxpages + ":", contactProperties);
// 2) Expand using partner property (e.g.: from contact to account via the
'account_primary_contact')
var expand = "&$expand=account_primary_contact($select=" + accountProperties.join() + ")";
var query = "?$select=" + contactProperties.join() + expand;
return Sdk.request("GET", contact1Uri + query, null, true);
})
.then(function (request) {
var contact = JSON.parse(request.response);
var label = "Contact '" + contact.fullname + "' is the primary contact for the following accounts:";
Sdk.output(contact.account_primary_contact, label, accountProperties);
// FetchXML
// Using FetchXML to query for all contacts whose fullname contains '(sample)'.
// NOTE: XML string must be URI encoded.
// For more information, see: https://msdn.microsoft.com/library/gg328117.aspx
console.log("\n-- FetchXML -- ");
var fetchXML = "<fetch mapping=\"logical\" output-format=\"xml-platform\" version=\"1.0\"
distinct=\"false\"> \
<entity name=\"contact\"> \
<attribute name=\"fullname\" /> \
<attribute name=\"jobtitle\" /> \
<attribute name=\"annualincome\" /> \
<order descending=\"true\" attribute=\"fullname\" /> \
<filter type=\"and\"> \
<condition value=\"%(sample)%\" attribute=\"fullname\" operator=\"like\" /> \
</filter> \
</entity> \
</fetch> ";
return Sdk.request("GET", "/contacts?fetchXml=" + encodeURIComponent(fetchXML), null, true);
})
.then(function(request){
var collection = JSON.parse(request.response).value;
Sdk.output(collection, "Contacts Fetched by fullname containing '(sample)':", contactProperties);
// FetchXML pagination.
// Noticed the attribute "page=3" and "count=4" in this XML.
// We want to retrieve entities in page 3 but limit results to only 4 entities.
// If the result return zero records for the page, that means we have reached the end of the result
set.
// For more info, see: https://msdn.microsoft.com/library/mt607533.aspx#bkmk_useFetchXML
var fetchXML = "<fetch mapping=\"logical\" output-format=\"xml-platform\" version=\"1.0\" \
distinct=\"false\" page=\"3\" count=\"4\"> \
<entity name=\"contact\"> \
<attribute name=\"fullname\" /> \
<attribute name=\"jobtitle\" /> \
<attribute name=\"annualincome\" /> \
<order descending=\"true\" attribute=\"fullname\" /> \
<filter type=\"and\"> \
<condition value=\"%(sample)%\" attribute=\"fullname\" operator=\"like\" /> \
</filter> \
</entity> \
</fetch> ";
return Sdk.request("GET", "/contacts?fetchXml=" + encodeURIComponent(fetchXML), null, true);
})
.then(function(request){
var collection = JSON.parse(request.response).value;
if (collection.length == 0) {
if (collection.length == 0) {
console.log("There are no records on this page."); // We have reached the end of our query
result set.
} else {
Sdk.output(collection, "Contacts Fetched by fullname containing '(sample)' - Page 3:",
contactProperties);
}
// Saved Query
// Get the Saved Query "Active Accounts" and display results to output.
console.log("\n-- Saved Query -- ");
var filter = "&$filter=name eq 'Active Accounts'";
var query = "?$select=name,savedqueryid" + filter;
return Sdk.request("GET", "/savedqueries" + query, null, true); // Requesting for saved query GUID.
})
.then(function(request){
// Get the savedqueryid GUID and then use it to request for the entities in that query.
var activeAccount = JSON.parse(request.response).value[0]; // Get the first matched.
var savedqueryid = activeAccount.savedqueryid;
// User Query
// Create a user query then get it from the server and execute that query for results.
// For more info, see: https://msdn.microsoft.com/library/gg509053.aspx
console.log("\n-- User Query -- ");
var userquery = {
"name": "My User Query",
"description": "User query to display contact info.",
"querytype": 0,
"returnedtypecode": "contact",
"fetchxml": "<fetch mapping=\"logical\" output-format=\"xml-platform\" version=\"1.0\"
distinct=\"false\"> \
<entity name=\"contact\"> \
<attribute name=\"fullname\" /> \
<attribute name=\"contactid\" /> \
<attribute name=\"jobtitle\" /> \
<attribute name=\"annualincome\" /> \
<order descending=\"false\" attribute=\"fullname\" /> \
<filter type=\"and\"> \
<condition value=\"%(sample)%\" attribute=\"fullname\" operator=\"like\" /> \
<condition value=\"%Manager%\" attribute=\"jobtitle\" operator=\"like\" /> \
<condition value=\"55000\" attribute=\"annualincome\" operator=\"gt\" /> \
</filter> \
</entity> \
</fetch> "
};
See also
Use the Dataverse Web API
Query Data using the Web API
Web API Samples
Web API Query Data Sample
Web API Query Data Sample (C#)
Web API Samples (Client-side JavaScript)
Web API Basic Operations Sample (Client-side JavaScript)
Web API Conditional Operations Sample (Client-side JavaScript)
Web API Functions and Actions Sample (Client-side JavaScript)