Skip to content

Dataverse quickstart & FAQ

This page is for integration partners building against akoyaGO and for internal akoyaGO developers writing against the platform. It curates the parts of Microsoft's Dataverse documentation that matter most for akoyaGO-style integrations, with akoyaGO-flavored examples.

Nothing here replicates Microsoft Learn. Every concept links to the authoritative Microsoft source. What's here is ordering, akoyaGO context, and the specific gotchas foundation-platform partners hit.

What is Dataverse?

Dataverse is Microsoft's low-code data platform — the same storage and API layer that backs Dynamics 365. akoyaGO tables live in a Dataverse environment, reachable through a REST/OData Web API and an older SOAP Organization Service. Partners should use the Web API unless building in-process plugins.

See also: What is Dataverse? — Microsoft Learn.

Before you start

You need three things:

  1. Credentials — see Authentication.
  2. An environment URLhttps://{org}.crm.dynamics.com. Your foundation admin or GOsupport will tell you which {org} to use.
  3. An HTTP client — examples below show the same call in raw HTTP, curl, Python (requests), and C# (ServiceClient). Use whichever fits your stack.

Examples conventions

Examples below assume ORG (the environment URL), access_token (a valid bearer token), headers (a standard Dataverse headers dict — Authorization, Accept, OData-Version, OData-MaxVersion), and service (a ServiceClient instance). The first example below shows how to set these up; later examples reference them directly.

Your first five minutes

1. Know the Web API endpoint pattern

Every Dataverse request shares a base URL:

https://{org}.crm.dynamics.com/api/data/v9.2/

Each table has a plural entity set name appended — akoya_gifts, akoya_funds, contacts. A single record is addressed by its GUID in parentheses: akoya_gifts({giftid}).

See also: Web API reference — Microsoft Learn.

In akoyaGO: the Web API section at the top of every entity reference page gives the exact endpoint for that table.

2. Authenticate

Acquire a Bearer token via Entra ID OAuth 2.0 (see Authentication). Pass it as Authorization: Bearer {token} on every request.

3. Make your first GET — five funds

GET {org}/api/data/v9.2/akoya_funds?$top=5&$select=akoya_fundname,akoya_fundcode
Accept: application/json
OData-Version: 4.0
OData-MaxVersion: 4.0
ACCESS_TOKEN="eyJ0eXAi..."   # from the token endpoint

curl "https://{org}.crm.dynamics.com/api/data/v9.2/akoya_funds?\$top=5&\$select=akoya_fundname,akoya_fundcode" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Accept: application/json" \
  -H "OData-Version: 4.0" \
  -H "OData-MaxVersion: 4.0"
import requests

ORG = "https://{org}.crm.dynamics.com"
access_token = "eyJ0eXAi..."   # from the token endpoint

headers = {
    "Authorization": f"Bearer {access_token}",
    "Accept": "application/json",
    "OData-Version": "4.0",
    "OData-MaxVersion": "4.0",
}
r = requests.get(
    f"{ORG}/api/data/v9.2/akoya_funds",
    params={"$top": 5, "$select": "akoya_fundname,akoya_fundcode"},
    headers=headers,
)
r.raise_for_status()
for fund in r.json()["value"]:
    print(fund["akoya_fundname"])
// service is a ServiceClient instance from Microsoft.PowerPlatform.Dataverse.Client

var query = new QueryExpression("akoya_fund") {
    ColumnSet = new ColumnSet("akoya_fundname", "akoya_fundcode"),
    TopCount = 5
};
var funds = service.RetrieveMultiple(query);
foreach (var f in funds.Entities)
    Console.WriteLine(f.GetAttributeValue<string>("akoya_fundname"));

4. Narrow the payload with $select

Dataverse returns every column by default. Always pass $select — it reduces payload size, improves response time, and avoids accidentally depending on columns that may change.

GET {org}/api/data/v9.2/akoya_funds?$select=akoya_fundname,akoya_fundcode,akoya_netassets&$top=5
Accept: application/json
OData-Version: 4.0
OData-MaxVersion: 4.0
curl "https://{org}.crm.dynamics.com/api/data/v9.2/akoya_funds?\$select=akoya_fundname,akoya_fundcode,akoya_netassets&\$top=5" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Accept: application/json" \
  -H "OData-Version: 4.0" \
  -H "OData-MaxVersion: 4.0"
r = requests.get(
    f"{ORG}/api/data/v9.2/akoya_funds",
    params={
        "$select": "akoya_fundname,akoya_fundcode,akoya_netassets",
        "$top": 5,
    },
    headers=headers,
)
var query = new QueryExpression("akoya_fund") {
    ColumnSet = new ColumnSet("akoya_fundname", "akoya_fundcode", "akoya_netassets"),
    TopCount = 5
};
var funds = service.RetrieveMultiple(query);

See also: Query data using the Web API — Microsoft Learn.

5. Follow lookups with $expand

Lookups aren't populated in the response by default — you get a raw _xxx_value GUID field. To pull related data inline, use $expand with the navigation property name (PascalCase, not the lowercase logical name).

GET {org}/api/data/v9.2/akoya_funds?$select=akoya_fundname&$expand=akoya_PrimaryDonor($select=akoya_formaldefault),akoya_GiftDefaultAccount($select=akoya_accountnum)
Accept: application/json
OData-Version: 4.0
OData-MaxVersion: 4.0
curl "https://{org}.crm.dynamics.com/api/data/v9.2/akoya_funds?\$select=akoya_fundname&\$expand=akoya_PrimaryDonor(\$select=akoya_formaldefault),akoya_GiftDefaultAccount(\$select=akoya_accountnum)" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Accept: application/json" \
  -H "OData-Version: 4.0" \
  -H "OData-MaxVersion: 4.0"
r = requests.get(
    f"{ORG}/api/data/v9.2/akoya_funds",
    params={
        "$select": "akoya_fundname",
        "$expand": "akoya_PrimaryDonor($select=akoya_formaldefault),akoya_GiftDefaultAccount($select=akoya_accountnum)",
    },
    headers=headers,
)
// ServiceClient — use LinkEntity for joins, or a follow-up Retrieve via EntityReference
var query = new QueryExpression("akoya_fund") {
    ColumnSet = new ColumnSet("akoya_fundname", "akoya_primarydonor", "akoya_giftdefaultaccount")
};
query.LinkEntities.Add(new LinkEntity {
    LinkFromEntityName = "akoya_fund",
    LinkFromAttributeName = "akoya_primarydonor",
    LinkToEntityName = "akoya_donor",
    LinkToAttributeName = "akoya_donorid",
    Columns = new ColumnSet("akoya_formaldefault"),
    EntityAlias = "donor",
    JoinOperator = JoinOperator.LeftOuter
});
var funds = service.RetrieveMultiple(query);

The navigation property name is PascalCase (akoya_PrimaryDonor), not lowercase. The Lookups — quick reference table on every entity reference page lists both.

See also: Retrieve related records — Microsoft Learn.

Common query patterns

Pattern Query option Microsoft Learn
Filter $filter=akoya_endowed eq true Filter results
Sort $orderby=akoya_fundname asc Order rows
Page size $top=50 (max 5000 per request) Specify number of rows
Paginate Follow @odata.nextLink in the response Page results
Count only /$count or $count=true Count rows
Fetch by ID akoya_funds({fundid}) Retrieve a record

Client libraries and tools

There's no single "right" way to talk to Dataverse. Pick what fits your stack.

Option Typical use Notes
curl / HTTPie Command-line exploration, CI scripts Zero dependencies. Pair with jq for parsing.
Postman / Bruno GUI exploration, partner handoff Save a collection of akoyaGO requests and share it.
Python — requests + msal ETL, scripts, Python-stack integrations msal handles token acquisition; requests is idiomatic HTTP.
Dataverse .NET SDK (Microsoft.PowerPlatform.Dataverse.Client) C# / .NET apps, Dataverse plugins ServiceClient wraps auth + retry + batching; QueryExpression for typed queries. Required for writing plugins.
Node.js / TypeScript JavaScript-stack integrations No official SDK; MSAL + fetch works. Community packages exist.
XrmToolBox Admin-side inspection, schema exploration Windows-only. Indispensable for tracing integration issues.

Frequently asked questions

Web API basics

Q: What's the base URL?

https://{org}.crm.dynamics.com/api/data/v9.2/. Replace {org} with your environment's subdomain.

Q: What API version should I target?

v9.2. It's the current stable Web API version. Older (v9.0, v9.1) still work, but newer features ship only in 9.2.

Q: Which headers do I need?

At minimum: Authorization: Bearer {token}, Accept: application/json, OData-Version: 4.0, OData-MaxVersion: 4.0. For writes, add Content-Type: application/json. For conditional updates, add If-Match: *.

Q: What's OData v4?

A query protocol Microsoft built on top of HTTP. It defines query options ($filter, $select, $expand, $orderby, etc.) that Dataverse exposes. See also: OData documentation.

Schema terminology

Q: Logical name, schema name, entity set name, navigation property — what's what?

Term Example (Gift) When to use
Logical name akoya_gift In XML, C# SDK, and attribute references in $filter. Lowercase.
Schema name akoya_Gift Rarely needed at runtime; used in customization XML.
Entity set name akoya_gifts In the Web API URL path.
Primary ID attribute akoya_giftid Primary-key column name.
Navigation property akoya_Donor, akoya_Fund1 In $expand and @odata.bind. PascalCase.

See also: Entity metadata overview — Microsoft Learn.

Q: What's @odata.bind and when do I need it?

Use @odata.bind on writes to set a lookup column. You bind a record URL rather than a raw GUID:

{
  "akoya_Donor@odata.bind": "/akoya_donors(00000000-0000-0000-0000-000000000000)"
}

See also: Associate and disassociate entities — Microsoft Learn.

Q: Why is _xxx_value different from the navigation property?

On reads, Dataverse returns lookups as _akoya_donor_value (lowercase, underscore-wrapped) — the raw GUID of the related record. The navigation property (akoya_Donor, PascalCase) is what you use in $expand to pull the related record inline. Every entity reference page lists both.

Data patterns

Q: How do I upsert in akoyaGO?

akoyaGO tables don't have alternate keys configured, so standard upsert syntax (PATCH /akoya_funds(akoya_fundcode='F-001234')) won't work. Use lookup then PATCH — query by the business identifier first, then PATCH by GUID. See the find-by-code example and the Find then update scenario on any entity reference page.

Q: What are _Base currency fields?

Every money column has a system-maintained _Base sibling that holds the value in the organization's base currency. akoya_totalgift is in the transaction currency (transactioncurrencyid); akoya_totalgift_base is the converted value. Never write to _Base columns directly — they're system-calculated.

Q: How do I query a solution-specific entity vs a standard one?

The same way. Logical names tell you which is which — akoyaGO customs start with akoya_, standards (contact, account, list) don't. There's no URL or query-option difference.

Q: How do I do incremental sync?

Dataverse supports change tracking — a delta-query mechanism. On entities with change tracking enabled, Dataverse returns an @odata.deltaLink URL you can follow for subsequent incremental pulls. All documented akoyaGO entities have change tracking enabled except akoya_CriteriaList.

See also: Use change tracking to synchronize data — Microsoft Learn.

Production concerns

Q: What are the rate limits?

Dataverse enforces per-user service-protection limits: 6000 API requests per 5-minute rolling window per app user / connection identity, with concurrency caps. Exceeding yields 429 Too Many Requests with a Retry-After header.

See also: Service protection API limits — Microsoft Learn.

Q: How do I handle 429?

Respect Retry-After. Back off exponentially on repeated failures. Never ignore a 429 — repeated hits lead to longer cool-downs.

Q: How do I do bulk writes?

Use the $batch endpoint. One HTTP request wraps many operations and can be transactional (all-or-nothing per change set). Batch is the right choice for creating large numbers of records or making many related updates.

See also: Execute batch operations — Microsoft Learn.

Q: How do I act on behalf of a specific user?

Pass the MSCRMCallerID request header with the user's system user ID. The request then runs under that user's security context, provided your app has impersonation rights.

See also: Impersonate another user — Microsoft Learn.

Debugging

Q: Best tool for testing queries without writing code?

Postman or Bruno for REST. XrmToolBox has excellent plugins (FetchXML Builder, Plugin Trace Viewer) for schema exploration and plugin debugging. FetchXML queries can be translated to OData through fetchxmlbuilder.com.

Q: How do I trace a failing request?

Dataverse returns structured JSON error responses on 4xx / 5xx. Inspect error.message and error.innererror.trace — the trace often names the exact plugin, workflow, or business rule that rejected the request.

See also: Compose HTTP requests and handle errors — Microsoft Learn.

What's next