> For the complete documentation index, see [llms.txt](https://docs.digibee.com/documentation/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.digibee.com/documentation/connectors-and-triggers/connectors/enterprise-applications/dynamics-365-finance-and-operations.md).

# Dynamics 365 Finance & Operations

The **Dynamics 365 Finance & Operations** connector enables integrations with [Microsoft Dynamics 365 Finance & Operations](https://learn.microsoft.com/en-us/dynamics365/fin-ops-core/) (F\&O), also known as Finance and Supply Chain Management.

It reads and writes the app's public **OData data entities**, such as `CustomersV3`, `VendorsV2`, `SalesOrderHeadersV2`, and `ReleasedProductsV2`. Supported operations include CRUD, query options, bound and unbound actions and functions, cross-company access, and `$batch`.

It's a specialization of the generic OData integration, using the `/data` service root, composite `dataAreaId` keys, and Azure AD (Azure Key) authentication. Entities are discovered automatically from the environment's `$metadata`, and cross-company access is supported.

{% hint style="info" %}

#### **Which Dynamics connector should I use?**

Use **Dynamics 365 Finance & Operations** for Finance and Supply Chain (`*.operations.dynamics.com`, `/data` entities). Use [**Dynamics 365**](/documentation/connectors-and-triggers/connectors/enterprise-applications/dynamics-365.md) for Customer Engagement, Dataverse, and CRM (`*.crm.dynamics.com`, `/api/data/v9.x`). Use the generic [**OData**](/documentation/connectors-and-triggers/connectors/web-protocols/odata.md) connector for any other OData service.
{% endhint %}

## **Set up authentication (Azure AD)**

The connector authenticates as an Azure AD **service principal** (OAuth 2.0 client credentials). You set this up once. Below are the steps relevant to Digibee. For the exact portal clicks, follow the linked Microsoft docs, since their UI changes over time.

1. **Register an app** in Microsoft Entra ID. In the Entra admin center, go to **App registrations**, then **New registration**. Copy the **Application (client) ID** and the **Directory (tenant) ID**. See [Register an application](https://learn.microsoft.com/en-us/entra/identity-platform/quickstart-register-app) for the full procedure.
2. **Create a client secret.** Go to **Certificates & secrets**, then **Client secrets**, then **New client secret**. Copy the secret **value** right away, it's shown only once. See [Add and manage app credentials](https://learn.microsoft.com/en-us/entra/identity-platform/how-to-add-credentials) for the full procedure.
3. **Grant the app access inside Finance & Operations.** In the F\&O app, go to **System administration**, then **Setup**, then **Microsoft Entra ID applications**. Add a row with the app's **Client ID**, a name, and a **User ID** mapped to a user whose **security role** grants access to the entities you'll use.

{% hint style="warning" %}
**Step 3 is required.** Skip it, and requests will still **authenticate** (a valid token is issued), but they'll get **rejected** with `401`/`403`, because the service principal isn't authorized in F\&O.
{% endhint %}

4. **Create the Digibee Azure Key account** using the values from steps 1 and 2:

| Azure Key account field | Value                            |
| ----------------------- | -------------------------------- |
| Client ID               | Application (client) ID (step 1) |
| Client Secret           | The secret **value** (step 2)    |
| Tenant ID               | Directory (tenant) ID (step 1)   |

The connector requests the token for scope `https://{your-env}.operations.dynamics.com/.default`. Only override it with **OAuth Scope** if your resource differs. See [Accounts](/documentation/developer-guide/development-cycle/build-overview/accounts.md).

{% hint style="info" %}
Client secrets expire. Microsoft caps them at 24 months and recommends less. Rotate the secret and update the Azure Key account before it expires, or discovery and requests will start failing with auth errors.
{% endhint %}

You'll also need the **environment URL**, for example `https://myenv.operations.dynamics.com`.

## **Concepts**

The terms below appear throughout the connector's parameters, so it helps to understand them before configuring a request. Each concept maps to a specific field you'll fill in later.

<table><thead><tr><th width="160">Term</th><th>Meaning</th></tr></thead><tbody><tr><td><strong>Data entity</strong></td><td>The OData collection you're working with, for example <code>CustomersV3</code>. Path: <code>/data/CustomersV3</code>.</td></tr><tr><td><strong>Composite key</strong></td><td>F&#x26;O keys are usually composite and include <code>dataAreaId</code> (the legal entity, or company), for example <code>dataAreaId='usmf',CustomerAccount='US-001'</code>.</td></tr><tr><td><strong>Cross-company</strong></td><td>By default, a query returns only the user's default company. Enable <strong>Cross-company</strong> to span all legal entities (<code>cross-company=true</code>).</td></tr><tr><td><strong><code>$metadata</code></strong></td><td>F&#x26;O exposes entity and field metadata at <code>/data/$metadata</code>. The connector uses it to power the guided <strong>Entity Set</strong> picker and field editor.</td></tr></tbody></table>

## **Parameters**

The table below lists all configuration parameters for the connector. Parameters that support [Double Braces expressions](/documentation/connectors-and-triggers/double-braces/overview.md) are marked with ✅ in the **Supports DB** column.

{% tabs %}
{% tab title="General" %}

<table><thead><tr><th width="112">Parameter</th><th width="231">Description</th><th width="100">Type</th><th width="110">Supports DB</th><th width="150">Default</th></tr></thead><tbody><tr><td><strong>Alias</strong></td><td>A name for this connector's output, so you can reference it later in the flow with <a href="/pages/cijmbAfZNYu7oJBb7Kd7#referencing-previous-steps-using-previous-steps-access">Double Braces</a>.</td><td>String</td><td>✅</td><td><code>dynamics-finops-01</code></td></tr><tr><td><strong>Fail On Client Error (4xx)</strong></td><td>Interrupt the pipeline on a 4xx response.</td><td>Boolean</td><td>❌</td><td><code>false</code></td></tr><tr><td><strong>Fail On Server Error (5xx)</strong></td><td>Interrupt the pipeline on a 5xx response.</td><td>Boolean</td><td>❌</td><td><code>false</code></td></tr></tbody></table>
{% endtab %}

{% tab title="Authentication" %}

<table><thead><tr><th width="112">Parameter</th><th width="200">Description</th><th width="83">Type</th><th width="81">Supports DB</th><th width="130">Default</th><th width="125">Visible when</th></tr></thead><tbody><tr><td><strong>Environment URL</strong></td><td>Finance &#x26; Operations environment root URL.</td><td>String</td><td>✅</td><td><code>https://myenv.operations.dynamics.com</code></td><td>—</td></tr><tr><td><strong>Use Dynamic Account</strong></td><td>If enabled, the connector resolves the account at runtime. If disabled, it uses the account configured statically below.</td><td>Boolean</td><td>❌</td><td><code>false</code></td><td><strong>Use Dynamic Account</strong> is enabled</td></tr><tr><td><strong>Scoped</strong></td><td>If enabled, isolates the stored account from other sub-processes. Not available for accounts referenced in headers or in the request body. To learn more, read the <a href="/spaces/jvO5S91EQURCEhbZOuuZ/pages/cbkYqH3fxzeL7zOKNawz">Dynamic Accounts documentation</a>.</td><td>Boolean</td><td>❌</td><td><code>false</code></td><td><strong>Use Dynamic Account</strong> is enabled</td></tr><tr><td><strong>Account Name</strong></td><td>Name of the account defined in the <a href="/pages/0kxlZ011XP4A2T43v9kh"><strong>Store Account</strong></a> connector.</td><td>String</td><td>✅</td><td>N/A</td><td><strong>Use Dynamic Account</strong> is disabled</td></tr><tr><td><strong>Account</strong></td><td>Account containing the Azure AD credentials (client ID, secret, and tenant). Supported type: <strong>Azure Key</strong>. Learn more about <a href="/spaces/jvO5S91EQURCEhbZOuuZ/pages/fS1QLzAg8rGSSJFwtrvy">Accounts</a>.</td><td>Account</td><td>—</td><td>N/A</td><td></td></tr></tbody></table>

{% hint style="info" %}
Authentication uses the OAuth 2.0 client-credentials flow with Azure AD (Microsoft Entra ID) and injects the resulting token as a `Bearer` header. The token is cached per combination of client, tenant, and scope.

OData v4 is used and fixed for Finance & Operations.
{% endhint %}
{% endtab %}

{% tab title="Dynamics API" %}

### **Operation parameter**

<table><thead><tr><th width="112">Parameter</th><th width="231">Description</th><th width="100">Type</th><th width="110">Supports DB</th><th width="150">Default</th></tr></thead><tbody><tr><td><strong>Operation</strong></td><td>The action to perform. It shapes the request. See the supported operations below.</td><td>Select</td><td>❌</td><td><code>Query</code></td></tr></tbody></table>

#### **Supported operations**

The **Operation** parameter maps to an OData request under the `/data` service root:

<table><thead><tr><th width="115">Operation</th><th width="98">HTTP</th><th width="141">Path</th><th width="141">Body</th><th>Notes</th></tr></thead><tbody><tr><td><strong>Batch</strong></td><td><code>POST</code></td><td><code>/data/$batch</code></td><td>multipart/JSON</td><td>Batches multiple operations in one request.</td></tr><tr><td><strong>Create</strong></td><td><code>POST</code></td><td><code>/data/{EntitySet}</code></td><td>JSON</td><td>Creates a record.</td></tr><tr><td><strong>Custom Query</strong></td><td><code>GET</code></td><td><code>/data/{EntitySet}?{$query}</code></td><td>—</td><td>Same as Query. Use it when combining raw and structured query parameters.</td></tr><tr><td><strong>Delete</strong></td><td><code>DELETE</code></td><td><code>/data/{EntitySet}({key})</code></td><td>—</td><td>Deletes a record.</td></tr><tr><td><strong>Get by ID</strong></td><td><code>GET</code></td><td><code>/data/{EntitySet}({key})</code></td><td>—</td><td>Reads a single record by key.</td></tr><tr><td><strong>Invoke Action</strong></td><td><code>POST</code></td><td><code>/data/{EntitySet}({key})/{Action}</code> or <code>/data/{Action}</code></td><td>JSON</td><td>Invokes a bound action (with key and entity set) or an unbound action.</td></tr><tr><td><strong>Invoke Function</strong></td><td><code>GET</code></td><td><code>/data/{EntitySet}({key})/{Function}</code> or <code>/data/{Function}</code></td><td>—</td><td>Invokes a bound or unbound function.</td></tr><tr><td><strong>Query</strong></td><td><code>GET</code></td><td><code>/data/{EntitySet}?{$query}</code></td><td>—</td><td>Reads the collection with query options.</td></tr><tr><td><strong>Update</strong></td><td><code>PATCH</code>/<code>PUT</code></td><td><code>/data/{EntitySet}({key})</code></td><td>JSON</td><td>Updates a record, partially (<code>PATCH</code>) or fully (<code>PUT</code>).</td></tr></tbody></table>

### **Common parameters**

These parameters appear regardless of the operation selected.

<table><thead><tr><th width="112">Parameter</th><th width="231">Description</th><th width="100">Type</th><th width="110">Supports DB</th><th width="150">Default</th></tr></thead><tbody><tr><td><strong>Cross-company</strong></td><td>Returns rows across all legal entities, instead of only the default company (<code>cross-company=true</code>).</td><td>Boolean</td><td>❌</td><td><code>false</code></td></tr><tr><td><strong>Output Format</strong></td><td>Controls the shape of the response returned to the pipeline. <strong>Values Only</strong> unwraps and returns only the <code>value</code> array. <strong>Full Response</strong> returns the entire response, including <code>@odata.*</code> fields. <strong>Single Entity</strong> returns the first or only entity.</td><td>Select</td><td>❌</td><td><code>Values Only</code></td></tr><tr><td><strong>Headers</strong></td><td>Additional request headers, as key/value pairs.</td><td>Key/Value</td><td>✅</td><td>N/A</td></tr></tbody></table>

### **Operation-specific parameters**

The remaining parameters only appear for certain operations, grouped below by purpose.

**Entity Key** — used by **Delete**, **Get by ID**, **Invoke Action**, **Invoke Function**, and **Update**.

<table><thead><tr><th width="112">Parameter</th><th width="231">Description</th><th width="100">Type</th><th width="110">Supports DB</th><th width="150">Default</th></tr></thead><tbody><tr><td><strong>Entity Key</strong></td><td>Single or composite key. F&#x26;O keys are usually composite, for example <code>dataAreaId='usmf',CustomerAccount='US-001'</code>.</td><td>String</td><td>✅</td><td>N/A</td></tr></tbody></table>

**Action / Function Name** — used by **Invoke Action** and **Invoke Function**.

<table><thead><tr><th width="112">Parameter</th><th width="231">Description</th><th width="100">Type</th><th width="110">Supports DB</th><th width="150">Default</th></tr></thead><tbody><tr><td><strong>Action / Function Name</strong></td><td>OData action or function name. Bound when an <strong>Entity Set</strong> is set, unbound otherwise.</td><td>String</td><td>✅</td><td>N/A</td></tr></tbody></table>

**Body** — used by **Batch**, **Create**, **Invoke Action**, and **Update**.

<table><thead><tr><th width="112">Parameter</th><th width="231">Description</th><th width="100">Type</th><th width="110">Supports DB</th><th width="150">Default</th></tr></thead><tbody><tr><td><strong>Body</strong></td><td>JSON payload for Create and Update, action parameters for Invoke Action, or the <code>$batch</code> payload.</td><td>JSON</td><td>✅</td><td><code>{}</code></td></tr></tbody></table>

**Interactive Mode** — used by **Create** and **Update**.

<table><thead><tr><th width="112">Parameter</th><th width="231">Description</th><th width="100">Type</th><th width="110">Supports DB</th><th width="150">Default</th></tr></thead><tbody><tr><td><strong>Interactive Mode</strong></td><td>If enabled, the connector displays the payload fields for you to edit. Otherwise, provide a raw JSON body.</td><td>Boolean</td><td>❌</td><td><code>false</code></td></tr></tbody></table>

**Query parameters** — used by **Custom Query** and **Query**.

<table><thead><tr><th width="112">Parameter</th><th width="231">Description</th><th width="100">Type</th><th width="110">Supports DB</th><th width="150">Default</th></tr></thead><tbody><tr><td><strong>Filter (<code>$filter</code>)</strong></td><td>OData filter expression, for example <code>CustomerGroupId eq '10'</code>.</td><td>String</td><td>✅</td><td>N/A</td></tr><tr><td><strong>Select (<code>$select</code>)</strong></td><td>Comma-separated list of fields to include in the response.</td><td>String</td><td>✅</td><td>N/A</td></tr><tr><td><strong>Order By (<code>$orderby</code>)</strong></td><td>Sorts the results by one or more properties, in ascending or descending order, for example <code>CustomerAccount asc</code>.</td><td>String</td><td>✅</td><td>N/A</td></tr><tr><td><strong>Expand (<code>$expand</code>)</strong></td><td>Related entities to include inline.</td><td>String</td><td>✅</td><td>N/A</td></tr><tr><td><strong>Top (<code>$top</code>)</strong></td><td>Maximum number of records to return.</td><td>Number</td><td>✅</td><td>N/A</td></tr><tr><td><strong>Skip (<code>$skip</code>)</strong></td><td>Records to skip, for pagination.</td><td>Number</td><td>✅</td><td>N/A</td></tr><tr><td><strong>Include Count (<code>$count</code>)</strong></td><td>Include the total count in the response.</td><td>Boolean</td><td>❌</td><td><code>false</code></td></tr></tbody></table>
{% endtab %}

{% tab title="Advanced Settings" %}

<table><thead><tr><th width="112">Parameter</th><th width="231">Description</th><th width="83">Type</th><th width="81">Supports DB</th><th width="100">Default</th><th width="125">Visible when</th></tr></thead><tbody><tr><td><strong>Request Timeout (seconds)</strong></td><td>Maximum time for the HTTP request.</td><td>Integer</td><td>❌</td><td><code>30</code></td><td>—</td></tr><tr><td><strong>Custom Query String</strong></td><td>Raw extra query parameters appended verbatim.</td><td>String</td><td>✅</td><td>N/A</td><td>—</td></tr><tr><td><strong>Update Method</strong></td><td><code>PATCH</code> for a partial update, or <code>PUT</code> for a full replacement.</td><td>Select</td><td>❌</td><td><code>PATCH</code></td><td>Operation is <strong>Update</strong></td></tr></tbody></table>
{% endtab %}

{% tab title="Documentation" %}

<table><thead><tr><th width="124.6002197265625">Parameter</th><th width="230.79998779296875">Description</th><th width="100">Data type</th><th width="110.4000244140625">Supports DB</th><th width="150.1904296875">Default value</th></tr></thead><tbody><tr><td><strong>Documentation</strong></td><td>Optional field to describe the connector configuration and any relevant business rules.</td><td>String</td><td>❌</td><td>N/A</td></tr></tbody></table>
{% endtab %}
{% endtabs %}

## **Examples**

### **Query customers in one company**

```
Environment URL = https://myenv.operations.dynamics.com
Entity Set      = CustomersV3
Operation       = Query
Select          = CustomerAccount,OrganizationName
Filter          = CustomerGroupId eq '10'
Top             = 2
```

**Resulting request:**

{% code overflow="wrap" %}

```html
GET /data/CustomersV3?$select=CustomerAccount,OrganizationName&$filter=CustomerGroupId eq '10'&$top=2
```

{% endcode %}

**Output** (when the Output Format is set to `Values Only`) :

```json
[
  { "CustomerAccount": "US-001", "OrganizationName": "Contoso Retail" },
  { "CustomerAccount": "US-004", "OrganizationName": "Fabrikam" }
]
```

### **Get one record by composite key**

```
Entity Set = CustomersV3
Operation  = Get by ID
Entity Key = dataAreaId='usmf',CustomerAccount='US-001'
```

**Resulting request:**

```html
GET /data/CustomersV3(dataAreaId='usmf',CustomerAccount='US-001')
```

### **Query across all companies**

```
Entity Set    = SalesOrderHeadersV2
Operation     = Query
Cross-company = true
```

**Resulting request:**

```html
GET /data/SalesOrderHeadersV2?cross-company=true
```

## **Known limitations**

* OData v4 only. Finance & Operations doesn't expose v2.
* No FetchXML. That's Dataverse and CRM-only. Use the [**Dynamics 365**](/documentation/connectors-and-triggers/connectors/enterprise-applications/dynamics-365.md) connector for that.
* No automatic pagination. Follow `@odata.nextLink` yourself, or control paging manually with `$top` and `$skip`.
* Guided entity and field discovery requires a reachable environment and a valid Azure Key account. If discovery can't reach the environment, you can still provide the Entity Set through a Double Braces expression.
* Composite keys, `@odata.bind` lookups for navigation properties, and enum and option values are the most error-prone areas. Validate against the entity's `$metadata`.

## **Common errors**

<table><thead><tr><th width="145">Status</th><th>Cause</th></tr></thead><tbody><tr><td><code>400</code></td><td>Malformed composite key syntax or <code>$filter</code> expression.</td></tr><tr><td><code>401</code> / <code>403</code></td><td>The app isn't registered as an F&#x26;O Application User, is missing the required security role, or the token scope is wrong.</td></tr><tr><td><code>404</code></td><td>Wrong entity set name.</td></tr></tbody></table>

Enable **Fail On Client Error (4xx)** or **Fail On Server Error (5xx)** to have the connector stop the pipeline when these errors occur.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.digibee.com/documentation/connectors-and-triggers/connectors/enterprise-applications/dynamics-365-finance-and-operations.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
