> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://help.moveworks.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://help.moveworks.com/_mcp/server.

# Set up authentication with Azure AD (Entra ID)

The Conversations API is in **Controlled Availability**. Endpoints, behaviors, and configuration surfaces may change during CA. Contact your Moveworks account team to participate.

This guide walks through configuring Azure AD (Entra ID) to authenticate against the Moveworks Conversations API.

## Azure AD token claims

The Azure AD v2.0 access token claims that matter for this setup:

| Claim | Value                                                                                                                                                                                                          |
| ----- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `aud` | The Application (client) ID or Application ID URI of your registered app.                                                                                                                                      |
| `oid` | The user's stable Azure AD object ID. This is the claim Moveworks uses to resolve the user. It is a default claim in v2.0 access tokens, so no Token configuration change is required on the app registration. |
| `iss` | `https://login.microsoftonline.com/{tenant-id}/v2.0`                                                                                                                                                           |

The Moveworks credential is configured with the Application (client) ID as the Audience and reads the `oid` claim as the user identifier.

## Prerequisites

Confirm the following with your Moveworks account team:

* Your Moveworks tenant is provisioned for the Conversations API and your account team has provided the **Bot Name** (used in the `Assistant-Name` header on every API call).
* You have access to the Moveworks Admin Portal (`https://{org}.moveworks.com`).
* You have admin access to Azure AD (Entra ID).
* Each user's Azure AD object ID is already resolvable against your Moveworks user roster via your Entra / MS Graph identity integration.

## Step 1: Configure the app registration in Entra ID

#### Register or open your app

In the Azure portal, go to **Entra ID → App registrations → New registration** (or open your existing app).

#### Set the platform and redirect URI

Under **Authentication → Add a platform**, choose **Single-page application (SPA)** and add your redirect URI there. Azure treats SPA as a public client that authenticates with PKCE alone, which is what the token request in step 3 assumes.

If your architecture requires the **Web** platform instead, Azure treats it as a confidential client and every token request must include a `client_secret` (or `client_assertion`). This is a valid path, but the curl in step 3.2 will not work as written — you must add `client_secret` to the POST body and manage that secret in your backend. If you register the app under the **Web** platform and omit the secret, Azure returns `AADSTS7000218: The request body must contain the following parameter: 'client_assertion' or 'client_secret'`. For most Conversations API integrations, SPA + PKCE is the simpler choice.

#### Note the Application (client) ID

Copy the Application (client) ID from the app's Overview page. You will enter this value as the **Audience** on the Moveworks credential in step 2, and it is what Azure stamps as `aud` in access tokens issued for this app.

#### Expose an API scope

Under **Expose an API**, confirm the **Application ID URI** (Azure defaults to `api://{client-id}`; keep the default unless you have a reason to change it). Click **Add a scope** and create a delegated scope named `access_as_user`. Set "Who can consent?" to **Admins and users** so users can consent at sign-in time. Save the scope.

Then go to **API permissions → Add a permission → My APIs**, select this same app, expand **Delegated permissions**, check the scope you just created (`access_as_user`), and click **Add permissions**. Grant admin consent if your organization requires it.

This is required for Azure to issue tokens when your client requests the `{client-id}/.default` scope in step 3. Without an exposed API scope granted to this app, the token request fails.

#### Assign users or groups

Assign the users or groups that should have access.

This guide assumes v2.0 access tokens. If your app registration's Manifest has `accessTokenAcceptedVersion: 1` (or omitted), tokens are issued in v1 format and the `iss` claim will be `https://sts.windows.net/{tenant-id}/` rather than the value used below. Set `accessTokenAcceptedVersion: 2` in the Manifest or request tokens from the v2.0 endpoint.

## Step 2: Create the JWT OAuth credential in Moveworks

In the Moveworks Admin Portal, go to `https://{org}.moveworks.com/http-connectors/connector_studio/home` → **Credentials** → create a new credential, and set the fields as follows.

| Field                | Value for Azure AD                                                                                    | Notes                                                                                                                                                                                                                                              |
| -------------------- | ----------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Credential Name**  | A descriptive label, e.g. `AzureAD-CAPI`                                                              | Free text.                                                                                                                                                                                                                                         |
| **Credential Type**  | `JWT OAuth`                                                                                           | Reveals the fields below.                                                                                                                                                                                                                          |
| **Public Key value** | `https://login.microsoftonline.com/{tenant-id}/v2.0/.well-known/openid-configuration`                 | This field accepts a **WKE (well-known endpoint) URL** in place of a PEM string. Paste your tenant's OpenID Connect discovery URL. Moveworks auto-discovers the issuer and fetches the JWKS signing keys from it, so you never paste a static key. |
| **Audience**         | The **Application (client) ID** GUID you noted in step 1, e.g. `a1b2c3d4-e5f6-7890-abcd-ef1234567890` | Enter the exact `aud` value your tokens carry. If your tokens carry the Application ID URI, e.g. `api://<client-id>`, enter that instead.                                                                                                          |
| **Issuer**           | `https://login.microsoftonline.com/{tenant-id}/v2.0`                                                  | The `iss` claim your tokens carry. Together with Audience, it uniquely identifies this credential.                                                                                                                                                 |
| **Identifier Type**  | `IdP`                                                                                                 | Tells Moveworks to resolve the identifier claim through an identity integration rather than as a raw email or record ID.                                                                                                                           |
| **Integration ID**   | `conversations_rest_api`                                                                              | Required when Identifier Type is `IdP`. Moveworks matches the `oid` claim against the user's Conversations API channel entry on their profile.                                                                                                     |
| **Identifier Claim** | `oid`                                                                                                 | The claim Moveworks reads to identify the user. Azure's `sub` is a pairwise GUID and is not usable for identity resolution, so use `oid`, which matches the object ID synced from Entra.                                                           |

The **Audience** value must exactly match the `aud` claim your Azure tokens actually carry. That value depends on your app registration and token version:

* **v2.0 tokens with the default Application ID URI**: `aud` is typically the Application (client) ID GUID (the common case).
* **v1.0 tokens**: `aud` is typically the Application ID URI (`api://<client-id>`).
* **Custom Application ID URI**: `aud` matches whatever value you set.

Recommended: obtain a test token in step 3 first, decode it at [jwt.ms](https://jwt.ms/), and paste the exact `aud` value from the decoded token into this field before saving the credential. If the values don't match, the token exchange fails with `invalid_audience`.

Save the credential.

## Step 3: Request an access token from Azure AD

Your client application obtains an Azure access token via the OAuth 2.0 Authorization Code + PKCE flow. This is a two-hop flow: your app redirects the user's browser to Azure to sign in, then exchanges the returned authorization code for an access token.

### The Azure endpoints

Both endpoints are tenant-specific. Substitute your Entra tenant ID (a GUID).

| Purpose                                          | URL                                                                   |
| ------------------------------------------------ | --------------------------------------------------------------------- |
| **Authorization** (browser redirect for sign-in) | `https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/authorize` |
| **Token** (exchange the authorization code)      | `https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/token`     |

### Step 3.1: Redirect the user to sign in

Construct the authorization URL with the following query parameters and redirect the user's browser to it.

| Parameter               | Value                                                      |
| ----------------------- | ---------------------------------------------------------- |
| `client_id`             | Your Application (client) ID                               |
| `response_type`         | `code`                                                     |
| `redirect_uri`          | The redirect URI you configured on the app registration    |
| `response_mode`         | `query`                                                    |
| `scope`                 | `{client-id}/.default` (see note below)                    |
| `state`                 | A CSRF token you generate and validate on return           |
| `code_challenge`        | PKCE code challenge (base64url of SHA-256 of the verifier) |
| `code_challenge_method` | `S256`                                                     |

Example:

```
https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/authorize?
  client_id=a1b2c3d4-e5f6-7890-abcd-ef1234567890
  &response_type=code
  &redirect_uri=https%3A%2F%2Fapp.acme.com%2Fauth%2Fcallback
  &response_mode=query
  &scope=a1b2c3d4-e5f6-7890-abcd-ef1234567890%2F.default
  &state=<csrf-token>
  &code_challenge=<pkce-challenge>
  &code_challenge_method=S256
```

The `scope` parameter **must** be `{client-id}/.default` (using your Application (client) ID). This is what causes Azure to issue a token whose `aud` claim equals your client ID, matching the Audience registered on the Moveworks credential. Requesting a different scope (for example, `openid profile email` alone) causes Azure to issue a token intended for a different resource and Moveworks will reject the exchange with `invalid_audience`.

After the user signs in, Azure redirects the browser to your `redirect_uri` with `code` and `state` query parameters. Validate `state` matches what you sent, then use `code` in the next step.

### Step 3.2: Exchange the authorization code for an access token

POST to the token endpoint with the authorization code, PKCE verifier, and application details.

```bash
curl -X POST "https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  --data-urlencode "client_id=<APPLICATION_CLIENT_ID>" \
  --data-urlencode "scope=<APPLICATION_CLIENT_ID>/.default" \
  --data-urlencode "code=<AUTHORIZATION_CODE>" \
  --data-urlencode "redirect_uri=<REDIRECT_URI>" \
  --data-urlencode "grant_type=authorization_code" \
  --data-urlencode "code_verifier=<PKCE_VERIFIER>"
```

A successful response returns an `access_token` (an Azure-issued JWT). This is what you pass to Moveworks in step 4.

### Manual testing tips

For testing the setup without wiring up a full client:

* Use [OAuth Debugger](https://oauthdebugger.com) or Postman's OAuth 2.0 support to run the Authorization Code + PKCE flow interactively against your Entra app registration. Both let you paste in the endpoints, client ID, scope, and redirect URI, and hand back the access token.
* Alternatively, use the Microsoft Graph Explorer if you just want to verify a user can sign in and see what claims their token carries.

Decode the returned access token (for example, at [jwt.ms](https://jwt.ms/)) and confirm the claims before moving to step 4:

* `aud` equals the Application (client) ID you registered as the Audience.
* `iss` equals `https://login.microsoftonline.com/{your-tenant-id}/v2.0`.
* `oid` is present and matches the object ID Moveworks has for the test user.

## Step 4: Validate end to end

The examples below use `https://api.moveworks.ai`, which is only correct for orgs on the US Production data center. Replace it with the base URL for your data center from the [Base URLs table](/api-reference/overview#base-urls) (for example, `https://api.am-eu-central.moveworks.ai` for EU). Requests to the wrong host return 404 or `invalid_audience`.

### 4.1 Exchange for a Moveworks token

Pass the Azure-issued token to the Moveworks token endpoint:

```bash
curl -X POST "https://api.moveworks.ai/oauth/v1/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  --data-urlencode "grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer" \
  --data-urlencode "assertion=<AZURE_ACCESS_TOKEN>"
```

A successful response returns a Moveworks access token (valid \~1 hour).

### 4.2 Test the API

```bash
curl -X POST "https://api.moveworks.ai/assistant/v1/conversations" \
  -H "Content-Type: application/json" \
  -H "Assistant-Name: <YOUR_BOT_NAME>" \
  -H "Authorization: Bearer <MW_ACCESS_TOKEN>" \
  -d '{"title": "Test conversation"}'
```

A `201` with a `conversation_id` confirms the integration works end to end.

## Summary

| Credential field | Azure AD value                                                                                                    |
| ---------------- | ----------------------------------------------------------------------------------------------------------------- |
| Public Key value | WKE URL: `https://login.microsoftonline.com/{tenant-id}/v2.0/.well-known/openid-configuration`                    |
| Audience         | Application (client) ID GUID from the Entra app registration                                                      |
| Issuer           | `https://login.microsoftonline.com/{tenant-id}/v2.0`                                                              |
| Identifier Type  | `IdP`                                                                                                             |
| Integration ID   | `conversations_rest_api`                                                                                          |
| Identifier Claim | `oid`                                                                                                             |
| Entra side       | No claim configuration required. `oid` is a default claim in v2.0 access tokens, and no custom claims are needed. |

For interaction patterns (polling and streaming) and the full API contract, see the [Conversations API reference](/api-reference/beta-conversations-api).