# Dynamics 365

Microsoft Dynamics 365 is a suite of enterprise resource planning (ERP) and customer relationship management (CRM) applications. The Sling Dynamics 365 connector extracts data from the Dataverse Web API (OData v4), supporting core CRM entities like accounts, contacts, leads, opportunities, and activities.

{% hint style="success" %}
**CLI Pro Required**: APIs require a [CLI Pro token](https://docs.slingdata.io/sling-cli/cli-pro) or [Platform Plan](https://docs.slingdata.io/sling-platform/platform).
{% endhint %}

## Setup

The following credentials and inputs are accepted:

**Secrets:**

* `client_id` **(required)** -> Azure AD Application (client) ID
* `client_secret` **(required)** -> Azure AD client secret
* `tenant_id` **(required)** -> Azure AD Directory (tenant) ID
* `environment_url` **(required)** -> Dynamics 365 environment URL (e.g., `org12345.crm.dynamics.com`)

**Inputs:**

* `anchor_date` (optional) -> Starting date for historical data extraction (default: 1 year ago). Format: `YYYY-MM-DDTHH:MM:SSZ`

### Getting Your Credentials

1. **Register an Azure AD Application:**
   * Go to the [Azure Portal](https://portal.azure.com/) > **Microsoft Entra ID** > **App registrations**
   * Click **New registration**
   * Name your app (e.g., "Sling Dynamics 365 Connector")
   * Set **Supported account types** to "Single tenant"
   * Click **Register**
   * Copy the **Application (client) ID** and **Directory (tenant) ID**
2. **Create a Client Secret:**
   * In your app registration, go to **Certificates & secrets**
   * Click **New client secret**
   * Set an expiry and click **Add**
   * Copy the secret **Value** immediately (it won't be shown again)
3. **Add API Permissions:**
   * Go to **API permissions** > **Add a permission**
   * Select **Dynamics CRM** > **Delegated permissions**
   * Check `user_impersonation`
   * Click **Add permissions**
4. **Create an Application User in Dynamics 365:**
   * Go to the [Power Platform Admin Center](https://admin.powerplatform.microsoft.com/)
   * Select your environment > **Settings** > **Users + permissions** > **Application users**
   * Click **New app user**
   * Select your Azure AD app and assign a **Security Role** (e.g., System Administrator or a custom read-only role)
5. **Get Your Environment URL:**
   * In the Power Platform Admin Center, select your environment
   * Copy the **Environment URL** (e.g., `org12345.crm.dynamics.com`)
   * Do **not** include `https://` — just the hostname

{% hint style="warning" %}
**Important:** Keep your Client Secret secure. Never commit it to version control. The Application User must be created in the Power Platform Admin Center before the connector can access data.
{% endhint %}

### Using `sling conns`

{% code overflow="wrap" %}

```bash
sling conns set DYNAMICS365 type=api spec=dynamics365 secrets='{ client_id: xxxxxxxx, client_secret: xxxxxxxx, tenant_id: xxxxxxxx, environment_url: org12345.crm.dynamics.com }'
```

{% endcode %}

### Environment Variable

See [here](https://docs.slingdata.io/sling-cli/environment#dot-env-file-.env.sling) to learn more about the `.env.sling` file.

{% code overflow="wrap" %}

```bash
export DYNAMICS365='{ type: api, spec: dynamics365, secrets: { client_id: "xxxxxxxx", client_secret: "xxxxxxxx", tenant_id: "xxxxxxxx", environment_url: "org12345.crm.dynamics.com" } }'
```

{% endcode %}

### Sling Env File YAML

See [here](https://docs.slingdata.io/sling-cli/environment#sling-env-file-env.yaml) to learn more about the sling `env.yaml` file.

```yaml
connections:
  DYNAMICS365:
    type: api
    spec: dynamics365
    secrets:
      client_id: "xxxxxxxx"
      client_secret: "xxxxxxxx"
      tenant_id: "xxxxxxxx"
      environment_url: "org12345.crm.dynamics.com"
```

**With anchor date for historical data:**

```yaml
connections:
  DYNAMICS365:
    type: api
    spec: dynamics365
    secrets:
      client_id: "xxxxxxxx"
      client_secret: "xxxxxxxx"
      tenant_id: "xxxxxxxx"
      environment_url: "org12345.crm.dynamics.com"
    inputs:
      anchor_date: "2020-01-01T00:00:00Z"
```

## Replication

Here's an example replication configuration to sync Dynamics 365 data to a PostgreSQL database:

```yaml
source: DYNAMICS365
target: MY_POSTGRES

defaults:
  mode: incremental
  object: dynamics365.{stream_name}

streams:
  accounts:
  contacts:
  activitypointers:
  emails:
  tasks:
  phonecalls:
  systemusers:
  teams:
  businessunits:
```

**Sync all available endpoints:**

```yaml
source: DYNAMICS365
target: MY_POSTGRES

defaults:
  mode: incremental
  object: dynamics365.{stream_name}

streams:
  '*':
```

## Endpoints

### Core CRM Entities

| Endpoint   | Description                                | Incremental |
| ---------- | ------------------------------------------ | ----------- |
| `accounts` | Companies and organizations tracked in CRM | Yes         |
| `contacts` | Individual people associated with accounts | Yes         |

### Sales Entities

{% hint style="info" %}
Sales endpoints require Dynamics 365 Sales to be installed in your environment. If not installed, these endpoints will be skipped automatically.
{% endhint %}

| Endpoint        | Description                              | Incremental |
| --------------- | ---------------------------------------- | ----------- |
| `leads`         | Potential sales prospects                | Yes         |
| `opportunities` | Sales opportunities in the pipeline      | Yes         |
| `quotes`        | Price quotes for potential sales         | Yes         |
| `salesorders`   | Accepted quotes that become sales orders | Yes         |
| `invoices`      | Billing invoices for sales orders        | Yes         |
| `products`      | Product catalog items                    | Yes         |

### Service Entities

| Endpoint    | Description                                                | Incremental |
| ----------- | ---------------------------------------------------------- | ----------- |
| `incidents` | Customer support cases/tickets (requires Customer Service) | Yes         |

### Activity Entities

| Endpoint           | Description                                   | Incremental |
| ------------------ | --------------------------------------------- | ----------- |
| `activitypointers` | Base activity type aggregating all activities | Yes         |
| `emails`           | Email activity records                        | Yes         |
| `tasks`            | Task activity records                         | Yes         |
| `phonecalls`       | Phone call activity records                   | Yes         |
| `appointments`     | Appointment/meeting activity records          | Yes         |

### Marketing Entities

| Endpoint    | Description                                     | Incremental |
| ----------- | ----------------------------------------------- | ----------- |
| `campaigns` | Marketing campaign records (requires Marketing) | Yes         |

### Administration & Reference

| Endpoint                | Description                                 | Incremental |
| ----------------------- | ------------------------------------------- | ----------- |
| `systemusers`           | System users (employees, service accounts)  | Yes         |
| `teams`                 | Teams for organizing users                  | Yes         |
| `businessunits`         | Business units for organizational hierarchy | Yes         |
| `transactioncurrencies` | Currency definitions                        | Yes         |
| `annotations`           | Notes and attachments linked to records     | Yes         |

## Incremental Sync

The Dynamics 365 connector uses time-based incremental sync with the `modifiedon` timestamp:

* **First run:** Fetches all records from `anchor_date` (default: 1 year ago) to present
* **Subsequent runs:** Only fetches records modified after the last sync
* **Update tracking:** Uses OData `$filter=modifiedon ge {timestamp}` with `$orderby=modifiedon asc`

## Rate Limiting

The Dynamics 365 Dataverse API has a rate limit of 6,000 requests per 5-minute sliding window per user. The connector automatically:

* Uses conservative rate limiting (15 requests/second)
* Retries with exponential backoff on 429 (rate limit) responses
* Retries on 503 (service unavailable) responses

## Module Requirements

Some endpoints require specific Dynamics 365 modules to be installed:

| Module                            | Required Endpoints                                            |
| --------------------------------- | ------------------------------------------------------------- |
| **Dynamics 365 Sales**            | leads, opportunities, quotes, salesorders, invoices, products |
| **Dynamics 365 Customer Service** | incidents                                                     |
| **Dynamics 365 Marketing**        | campaigns                                                     |

If a module is not installed, those endpoints will stop gracefully with 0 records instead of failing. Core Dataverse entities (accounts, contacts, activities, systemusers, teams, businessunits, transactioncurrencies, annotations) are always available.

## Troubleshooting

### Authentication Issues

* Verify your `tenant_id` matches the Azure AD directory where the app is registered
* Ensure the Application User is created in the Power Platform Admin Center with a security role
* Check that the `environment_url` does not include `https://` — use just the hostname (e.g., `org12345.crm.dynamics.com`)

### No Records Returned

* Some endpoints require Dynamics 365 Sales, Customer Service, or Marketing to be installed
* Verify your Application User's security role has read access to the entities
* Check that data exists within the `anchor_date` range

### Entity Not Found (404)

* This typically means the required Dynamics 365 module is not installed in your environment
* The connector will stop gracefully and return 0 records for these endpoints

If you are facing issues connecting, please reach out to us at <support@slingdata.io>, on [discord](https://discord.gg/q5xtaSNDvp) or open a Github Issue [here](https://github.com/slingdata-io/sling-cli/issues).
