# CRM User Mapping API V3 — Developer Guide

- **Applies to:** Sprinklr CRM User Mapping API V3 (`/api/v3/crm-user-mapping`)
- **V2 API reference:** [CRM User Mapping | Sprinklr Developer Portal](https://dev.sprinklr.com/crm-user-mapping)


## 1. Overview

Once you have configured the CRM Connector in Marketplace, every user who needs access to the Sprinklr console within CRM must be mapped between the two platforms. This **one-to-one mapping** associates the CRM user with the Sprinklr user, thereby ensuring data governance, compliance, and tracking. The CRM User Mapping APIs facilitate automating creation, fetching, and updating the one-to-one user mapping between CRM and Sprinklr users.

User mapping ensures that users can access the Sprinklr Care Console iFrame within CRM. All features within the iFrame, controlled by permissions, are consistent with the associated Sprinklr user.

**Key benefits:**

- **Data Governance** — ensures that user actions are tracked and audited across both systems.
- **Compliance** — maintains consistency in user roles and permissions.
- **Streamlined Operations** — simplifies user management by keeping a synchronized record of users.


### 1.1 Operations

| Operation | Method | Path | Request body | `200` schema |
|  --- | --- | --- | --- | --- |
| Fetch CRM user mappings | `GET` | `/api/v3/crm-user-mapping?installedAppId={installedAppId}` | — | `APIResponse` |
| Create a CRM user mapping (no upsert) | `POST` | `/api/v3/crm-user-mapping` | `CrmUserMappingCreateRequestDTO` | `APIResponse` |
| Delete CRM user mappings | `DELETE` | `/api/v3/crm-user-mapping?installedAppId={installedAppId}` | See [§5.3](#53-delete-crm-user-mappings) | `APIResponse` |
| Create or update a CRM user mapping | `POST` | `/api/v3/crm-user-mapping/upsert` | `CrmUserMappingCreateRequestDTO` | `APIResponse` |
| Export CRM user mappings as CSV | `GET` | `/api/v3/crm-user-mapping/export?installedAppId={installedAppId}` | — | `string` |
| Import CRM user mappings from CSV (upsert) | `PUT` | `/api/v3/crm-user-mapping/import?installedAppId={installedAppId}` | `multipart/form-data` file | `string` |


### 1.2 Prerequisites

1. **CRM Connector configuration.** The CRM Connector must be configured in the Sprinklr Marketplace. Access to CRM Connector configuration may be permission controlled — ensure you have the necessary permissions to proceed.
2. **Valid Sprinklr user account.** A Sprinklr user can be mapped using **User ID or Email**.
3. **CRM User ID.** The CRM User ID is required to map with a Sprinklr User ID.


### 1.3 The three identifiers

Every call in this API turns on three ids.

**`installedAppId`** — the unique identifier for the CRM installed app.

**Steps to extract the Installed App Id from the UI:**

1. Click on the **+** tab on Sprinklr's homepage.
2. Search and navigate to **All Marketplace**.
3. Search for the CRM app you want the user mapping for.
4. Hover and click on the **edit** button.
5. The app installed Id will be appended in the suffix of the browser URL.


**`sprUserId`** — the unique identifier for the Sprinklr user.

**Steps to obtain the Sprinklr User ID:**

1. Click the **New Tab** icon in Sprinklr. Under **Platform Modules**, select **Users** within the **Collaborate** section.
2. Hover over the user's options icon and select **Details**. The Sprinklr User ID can be found within the URL string in your browser.


**`crmUserId`** — the unique identifier for the CRM user, for example a CRM user id such as `005gL000006r0fxQAA`.

## 2. Base URLs and environments

**All API calls are sent to the production endpoint:**

```
https://api3.sprinklr.com/{env}/api/v3
```

So the CRM user mapping resource is:

```
https://api3.sprinklr.com/{env}/api/v3/crm-user-mapping?installedAppId={installedAppId}
```

Replace `{env}` with your assigned environment identifier (`prod0`, `prod2`, `prod11`, and so on — see [APIs | Sprinklr Developer Portal](https://dev.sprinklr.com/apis) for the environment list).

## 3. Authentication and common headers

API headers include the mandatory information you send along with the request URL and body. This information helps provide insights into request context and authorization credentials that, in turn, allow access to protected resources.

| Key | Value | Description |
|  --- | --- | --- |
| `Authorization` | `******` | Credential used by the API to authenticate a user with the server. For generating an authorization token, refer to the Authorize section on the developer portal. |
| `Key` | `api-key` | API key helps authenticate the application with the server. For generating an API key, refer to the Getting Started guide. |
| `Content-Type` | `application/json` | Representation header that determines the type of data (media/resource) present in the request body |
| `Accept` | `application/json` | Determines the acceptable response type from the server |


**Two operations use a different `Accept` value.** Export and import deal in CSV:

| Operation | `Accept` | `Content-Type` |
|  --- | --- | --- |
| Export CRM user mappings | `text/csv` | — |
| Import CRM user mappings | `text/csv` | `multipart/form-data` (set by the client when attaching the file) |


## 4. Read operations

### 4.1 Fetch CRM user mappings

**`GET /api/v3/crm-user-mapping?installedAppId={installedAppId}`**

Fetches the CRM user mappings for the given installed App Id.

#### Query parameters

| Parameter | Required/Optional | Description | Type |
|  --- | --- | --- | --- |
| `installedAppId` | **Required** | Refers to the unique identifier for the CRM installed app. See [§1.3](#13-the-three-identifiers) for the steps to extract it. | String |
| `pageNumber` | Optional | Zero-based page index of the result set. The collection sends `pageNumber=0` for the first page. | Integer |
| `crmUserId` | Optional | Filter the mappings by one or more CRM user ids, comma-separated. | String |
| `sprUserId` | Optional | Filter the mappings by one or more Sprinklr user ids, comma-separated. | String |


`pageNumber`, `crmUserId`, and `sprUserId` are taken from the supplied Postman collection — `crmUserId` and `sprUserId` are present but **disabled** in the saved request, demonstrating the multi-id comma-separated form (`005gL000006r0fxQAA,005gL000006cPWbQAM` and `66007805,66016584`).

#### Request

```bash
curl -X GET \
  'https://api3.sprinklr.com/{env}/api/v3/crm-user-mapping?installedAppId=69b2a3a6c28fba5e0bf50c2e&pageNumber=0' \
  -H 'Authorization: ****** {Enter your Access Token}' \
  -H 'Key: {Enter your API KEY}' \
  -H 'accept: application/json'
```

Filtering by specific users:

```bash
curl -X GET \
  'https://api3.sprinklr.com/{env}/api/v3/crm-user-mapping?installedAppId=69b2a3a6c28fba5e0bf50c2e&crmUserId=005gL000006r0fxQAA,005gL000006cPWbQAM' \
  -H 'Authorization: ****** {Enter your Access Token}' \
  -H 'Key: {Enter your API KEY}' \
  -H 'accept: application/json'
```

#### Response

`200 Success`, body `APIResponse`.

```json
{
    "data": [
        {
            "installedAppId": "6685495f74c1b74c1659ad74",
            "crmUserId": "0051S00000AtoLjQAJ",
            "sprUserId": 66000027,
            "createdBy": 66000101,
            "lastModifiedBy": 66000101,
            "createdTime": 1720101365700,
            "lastModifiedTime": 1720512381309
        }
    ],
    "errors": []
}
```

#### Response parameters

| Parameter | Definition | Type |
|  --- | --- | --- |
| `installedAppId` | Refers to the unique identifier for the CRM installed app | String |
| `crmUserId` | Refers to the unique identifier for the CRM user | String |
| `sprUserId` | Refers to the unique identifier for the Sprinklr user | Integer |
| `createdBy` | Refers to the unique identifier for the user who created the app | Integer |
| `lastModifiedBy` | Refers to the unique identifier for the user who last modified the app | Integer |
| `createdTime` | Refers to the time at which the CRM app was created | Epoch (Milliseconds) |
| `lastModifiedTime` | Refers to the time at which the CRM app was last modified | Epoch (Milliseconds) |


### 4.2 Export CRM user mappings as CSV

**`GET /api/v3/crm-user-mapping/export?installedAppId={installedAppId}`**

Exports the existing mappings for an installed app as a CSV file.

#### Query parameters

| Parameter | Required/Optional | Description | Type |
|  --- | --- | --- | --- |
| `installedAppId` | **Required** | Refers to the unique identifier for the CRM installed app | String |


#### Headers

Send `Accept: text/csv` on this call — not `application/json`.

#### Request

```bash
curl -X GET \
  'https://api3.sprinklr.com/{env}/api/v3/crm-user-mapping/export?installedAppId=6914257a9d2a401d32f09d95' \
  -H 'Authorization: ****** {Enter your Access Token}' \
  -H 'Key: {Enter your API KEY}' \
  -H 'accept: text/csv'
```

#### Response

`200 Success`. The specification declares the `200` body as a bare `string` under `application/json`, while the collection requests `text/csv`. In practice this call yields the CSV export of the mappings.

The exported file is the intended input to [§5.4 Import](#54-import-crm-user-mappings-from-csv) — export, edit, re-import is the supported bulk-update round trip.

## 5. Write operations

### 5.1 Create a CRM user mapping

**`POST /api/v3/crm-user-mapping`**

Creates a CRM user mapping for the given CRM and SPR user Id. **This operation does not upsert** — the specification summary is explicitly *"Create a CRM user mapping (no upsert)"*. If either side of the pair is already mapped, the call fails; use [§5.2](#52-create-or-update-a-crm-user-mapping-upsert) instead.

#### Request body — `CrmUserMappingCreateRequestDTO`

| Parameter | Required/Optional | Description | Type |
|  --- | --- | --- | --- |
| `installedAppId` | **Required** | Refers to the unique identifier for the CRM installed app | String |
| `crmUserId` | **Required** | Refers to the unique identifier for the CRM user | String |
| `sprUserId` | **Required** | Sprinklr user id **or email** | String |


All three fields are listed under `required` in the schema.

Note that `installedAppId` travels in the **body** for create and upsert, and in the **query string** for fetch, delete, export, and import.

#### Request

```bash
curl -X POST \
  'https://api3.sprinklr.com/{env}/api/v3/crm-user-mapping' \
  -H 'Authorization: ****** {Enter your Access Token}' \
  -H 'Key: {Enter your API KEY}' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
  "installedAppId": "69b2a3a6c28fba5e0bf50c2e",
  "crmUserId": "005gL00000JrMx7QAF",
  "sprUserId": "66005588"
}'
```

#### Response

`200 Success`, body `APIResponse`.

### 5.2 Create or update a CRM user mapping (upsert)

**`POST /api/v3/crm-user-mapping/upsert`**

Creates the mapping if it does not exist, or updates it if it does. Use this for idempotent synchronization jobs where you cannot know in advance whether the pair is already mapped.

#### Request body — `CrmUserMappingCreateRequestDTO`

Identical to [§5.1](#51-create-a-crm-user-mapping): `installedAppId`, `crmUserId`, `sprUserId` — all required.

#### Request

```bash
curl -X POST \
  'https://api3.sprinklr.com/{env}/api/v3/crm-user-mapping/upsert' \
  -H 'Authorization: ****** {Enter your Access Token}' \
  -H 'Key: {Enter your API KEY}' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
  "installedAppId": "69b2a3a6c28fba5e0bf50c2e",
  "crmUserId": "005gL000006r0fxQAA",
  "sprUserId": "66016584"
}'
```

#### Response

`200 Success`, body `APIResponse`.

> The upsert path does **not** relax the one-to-one constraint. Conflict checks still apply — you cannot map a Sprinklr user already associated with one CRM user to a different CRM user. See [§10](#10-caveats-and-best-practices).


### 5.3 Delete CRM user mappings

**`DELETE /api/v3/crm-user-mapping?installedAppId={installedAppId}`**

Deletes CRM user mapping for the given installed app Id.

#### Query parameters

| Parameter | Required/Optional | Description | Type |
|  --- | --- | --- | --- |
| `installedAppId` | **Required** | Refers to the unique identifier for the CRM installed app | String |


#### Request body

The supplied collection sends a JSON body narrowing the delete to a single pair:

| Parameter | Required/Optional | Description | Type |
|  --- | --- | --- | --- |
| `crmUserId` | Optional | Refers to the unique identifier for the CRM user | String |
| `sprUserId` | Optional | Refers to the unique identifier for the Sprinklr user | String |


#### ⚠️ Scope of the delete

> **Dev Note:** if the SPR user Id or CRM user Id are not passed, the API will **delete all the CRM user mappings for the given installed app Id**. Also, if one of the SPR user Id or CRM user Id is passed, it deletes all the mapping records that match the given Id.


Omitting both selectors is a full wipe of the app's mappings, not a no-op. Always send at least one selector unless a full wipe is genuinely what you intend.

#### Request

```bash
curl -X DELETE \
  'https://api3.sprinklr.com/{env}/api/v3/crm-user-mapping?installedAppId=69b2a3a6c28fba5e0bf50c2e' \
  -H 'Authorization: ****** {Enter your Access Token}' \
  -H 'Key: {Enter your API KEY}' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
  "crmUserId": "005gL00000JrMx7QAF",
  "sprUserId": "66005588"
}'
```

#### Response

`200 Success`.

### 5.4 Import CRM user mappings from CSV

**`PUT /api/v3/crm-user-mapping/import?installedAppId={installedAppId}`**

Performs a bulk operation to create or update CRM user mapping from a CSV file. The specification summary is *"Import CRM user mappings from CSV (upsert)"* — existing mappings in the file are updated, new ones created.

#### Query parameters

| Parameter | Required/Optional | Description | Type |
|  --- | --- | --- | --- |
| `installedAppId` | **Required** | Refers to the unique identifier for the CRM installed app | String |


#### Request body — `multipart/form-data`

| Form field | Required/Optional | Description | Type |
|  --- | --- | --- | --- |
| `file` | **Required** | The CSV file containing the mappings to create or update | File |


#### CSV file requirements

| Rule | Requirement |
|  --- | --- |
| **Header validation** | The CSV file must include **"Sprinklr User Id/Email"** and **"CRM User ID"** in the first row as headers. |
| **File format** | Only **CSV** files are accepted for upload. |
| **Duplicate CRM User IDs** | There must be no duplicate CRM User IDs within the bulk upload file. |
| **Duplicate Sprinklr User IDs/Email** | The bulk file must not contain duplicate Sprinklr User IDs/Email. |
| **All-or-none principle** | Bulk operations follow an all-or-none principle — if any record in the bulk upload fails validation, **the entire operation is aborted**. |


Populate each row with the **Sprinklr User ID/Email** (the ID or email associated with the Sprinklr account) and the corresponding **CRM User ID**.

#### Headers

Send `Accept: text/csv` on this call. The collection names the uploaded file after the app and date, for example `69b2a3a6c28fba5e0bf50c2e_27Jul2026.csv`.

#### Request

```bash
curl -X PUT \
  'https://api3.sprinklr.com/{env}/api/v3/crm-user-mapping/import?installedAppId=69b2a3a6c28fba5e0bf50c2e' \
  -H 'Authorization: ****** {Enter your Access Token}' \
  -H 'key: {Enter your API KEY}' \
  -H 'Accept: text/csv' \
  -F 'file=@69b2a3a6c28fba5e0bf50c2e_27Jul2026.csv'
```

#### Response

`200 Success`. The specification declares the `200` body as a bare `string` under `application/json`, while the collection requests `text/csv`.

### 5.5 Operation comparison — which write to use

|  | `POST /crm-user-mapping` | `POST /crm-user-mapping/upsert` | `PUT /crm-user-mapping/import` | `DELETE /crm-user-mapping` |
|  --- | --- | --- | --- | --- |
| Purpose | Create one mapping | Create or update one mapping | Bulk create/update from CSV | Remove mappings |
| Volume | Single | Single | Many | Single or all |
| Existing mapping | **Fails** | Updated | Updated | — |
| `installedAppId` location | Body | Body | Query | Query |
| Body | JSON DTO | JSON DTO | `multipart/form-data` | JSON selectors |
| Partial success | N/A | N/A | **No — all-or-none** | Not documented |
| Response schema | `APIResponse` | `APIResponse` | `string` | `APIResponse` |


## 6. Response format and status codes

### 6.1 Response bodies

| Operation | Declared `200` schema |
|  --- | --- |
| `GET /crm-user-mapping` | `APIResponse` (`data`, `errors`) |
| `POST /crm-user-mapping` | `APIResponse` |
| `DELETE /crm-user-mapping` | `APIResponse` |
| `POST /crm-user-mapping/upsert` | `APIResponse` |
| `GET /crm-user-mapping/export` | `string` |
| `PUT /crm-user-mapping/import` | `string` |


The four JSON operations use the standard V3 `APIResponse` envelope: `data` carries the payload and `errors` is an array that is empty on success. **Check `errors` even on a `200`** — an empty array is the success signal.

### 6.2 Response codes

Declared on all six operations:

| HTTP Code | Scenario | Description |
|  --- | --- | --- |
| `200 OK` | Success | Operation completed successfully |
| `400 Bad Request` | Validation failure | Invalid CRM or Sprinklr user id, duplicate mapping, conflicting mapping, malformed CSV, or missing `installedAppId` |
| `401 Unauthorized` | Authentication failed | Invalid or missing `Authorization` token |
| `403 Forbidden` | Insufficient permissions | The Sprinklr user does not have access to the installed app, or the caller lacks CRM Connector permissions |
| `404 Not Found` | Not found | The installed app or mapping does not exist |


`500 Internal Server Error` is not declared on these operations; handle it defensively regardless.

### 6.3 Validation rules behind a `400`

Ensure the following validation rules are checked before adding or updating a user mapping:

1. **CRM User ID Validation** — the CRM User ID provided must be valid for the specified installed app.
2. **Sprinklr User ID Validation** — the Sprinklr User ID must be valid for the designated partner organization.
3. **Email Validation** — if a Sprinklr email is used via API2, it must belong to the specified partner.
4. **Access Validation** — the Sprinklr user must have access to the installed app. This means the app should be shared with the user.
5. **Installed App Integrity** — the installed app should not be deleted when using the API2 method.
6. **Duplicate Mapping Check for Addition** — when adding a mapping, ensure that neither the Sprinklr User ID nor the CRM User ID is already mapped.
7. **Conflict Check for Updating Mappings** — when updating a mapping, there should be no conflicting mappings, such as attempting to map a Sprinklr user already associated with one CRM user to another CRM user.


For bulk operations, the additional checks in [§5.4](#54-import-crm-user-mappings-from-csv) apply on top.

## 7. V2 → V3 migration

### 7.1 Endpoint mapping

| V2 | V3 |
|  --- | --- |
| [Fetch CRM User Mapping](https://dev.sprinklr.com/fetch-crm-user-mapping) — `GET /api/v2/crm-user-mapping/{InstalledAppId}` | `GET /api/v3/crm-user-mapping?installedAppId={installedAppId}` |
| Fetch and Download CRM User Mapping | `GET /api/v3/crm-user-mapping/export?installedAppId={installedAppId}` |
| Fetch CRM User Mapping Using CRM User Ids | `GET /api/v3/crm-user-mapping?installedAppId=…&crmUserId=…` |
| Fetch CRM User Mapping Using SPR User Ids | `GET /api/v3/crm-user-mapping?installedAppId=…&sprUserId=…` |
| [Add CRM User Mapping](https://dev.sprinklr.com/add-crm-user-mapping) — `POST` | `POST /api/v3/crm-user-mapping` |
| Create/Update CRM User Mappings | `POST /api/v3/crm-user-mapping/upsert` |
| Create/Update User Mapping - Bulk | Not present as a separate V3 path — use `/upsert` per mapping or `/import` for a file |
| [Create/Update User Mapping - From File](https://dev.sprinklr.com/create-update-user-mapping-from-file) — `PUT` | `PUT /api/v3/crm-user-mapping/import?installedAppId={installedAppId}` |
| [Delete CRM User Mapping](https://dev.sprinklr.com/delete-crm-user-mapping) — `DELETE` | `DELETE /api/v3/crm-user-mapping?installedAppId={installedAppId}` |


### 7.2 What actually changes

| Aspect | API V2 | API V3 | Impact |
|  --- | --- | --- | --- |
| Base path | `/api/v2/crm-user-mapping` | `/api/v3/crm-user-mapping` | Change the version segment |
| `installedAppId` on fetch | **Path** parameter — `/crm-user-mapping/{InstalledAppId}` | **Query** parameter — `?installedAppId=` | **Breaking.** URL construction must change. |
| Casing | `{InstalledAppId}` in the V2 path | `installedAppId` in the V3 query string | Lower-case initial letter |
| Endpoint count | Nine separately documented pages | **Six** operations across four paths | Fetch-by-CRM-id and fetch-by-SPR-id collapse into query filters on the single `GET` |
| Filtering | Distinct endpoints per id type | `crmUserId` / `sprUserId` query parameters on one `GET` | Consolidation — fewer URLs, same capability |
| Download / export | "Fetch and Download" | `/export` | Renamed |
| From-file import | "From File" | `/import` | Renamed; still `PUT`, still CSV |
| Bulk create/update | Separate bulk endpoint | No separate path | Use `/upsert` or `/import` |
| Response envelope | `{"data": [...], "errors": []}` | `APIResponse` — same shape | None observed |
| `sprUserId` type | Integer in the V2 response | String in the V3 request DTO | Verify before serializing |


**The one genuinely breaking change is the move of `installedAppId` from the path to the query string.** Everything else is renaming or consolidation.

### 7.3 Migration steps

1. **Update the base URL.** Change `/api/v2/crm-user-mapping` to `/api/v3/crm-user-mapping`.
2. **Move `installedAppId` out of the path.** Rewrite `/crm-user-mapping/{InstalledAppId}` as `/crm-user-mapping?installedAppId={installedAppId}`. Note the lower-case `i`.
3. **Collapse your fetch endpoints.** Replace the separate fetch-by-CRM-id and fetch-by-SPR-id calls with `crmUserId` / `sprUserId` query filters on the single `GET`.
4. **Repoint download and file-import.** `/export` and `/import` respectively; keep `Accept: text/csv` on both.
5. **Choose create versus upsert deliberately.** V3 separates them into two paths — `POST /crm-user-mapping` explicitly does **not** upsert.
6. **Re-verify your delete calls.** Confirm whether `crmUserId` / `sprUserId` selectors belong in the body or the query string in V3, and re-test that omitting both still means "delete everything for this app."
7. **Serialize `sprUserId` as a string** in V3 request bodies, per the DTO — while continuing to parse it as an integer in fetch responses.


## 8. Supported enums and reference tables

There are no enumerated value sets in this API. The reference tables that matter are the CSV contract and the identifier formats.

### 8.1 CSV import contract

| Item | Value |
|  --- | --- |
| Accepted file format | CSV only |
| Required header row | `Sprinklr User Id/Email`, `CRM User ID` |
| Header position | First row |
| Duplicate CRM User IDs | Not permitted |
| Duplicate Sprinklr User IDs/Email | Not permitted |
| Failure behavior | All-or-none — one bad record aborts the whole operation |


### 8.2 Identifier formats

| Identifier | Example | Type in request | Type in response |
|  --- | --- | --- | --- |
| `installedAppId` | `69b2a3a6c28fba5e0bf50c2e` | String (24-char hex) | String |
| `crmUserId` | `005gL000006r0fxQAA` | String (CRM 18-char id) | String |
| `sprUserId` | `66016584` or a Sprinklr email | String | Integer |
| `createdBy` / `lastModifiedBy` | `66000101` | — | Integer |
| `createdTime` / `lastModifiedTime` | `1720101365700` | — | Epoch (Milliseconds) |


### 8.3 `Accept` header by operation

| Operation | `Accept` |
|  --- | --- |
| Fetch, Create, Upsert, Delete | `application/json` |
| Export, Import | `text/csv` |


## 9. Use cases

### 9.1 Onboard a single agent into the CRM console

**Scenario:** a new agent needs access to the Sprinklr Care Console iFrame inside CRM.

1. Confirm the Sprinklr user has access to the installed app — the app must be shared with them, or the call fails Access Validation.
2. `POST /api/v3/crm-user-mapping` with `installedAppId`, `crmUserId`, and `sprUserId`.
3. Verify with `GET /api/v3/crm-user-mapping?installedAppId=…&sprUserId=…`.


### 9.2 Idempotent identity synchronization

**Scenario:** a nightly job reconciles CRM users against Sprinklr users and cannot know which pairs already exist.

Use `POST /api/v3/crm-user-mapping/upsert` per pair rather than `POST /api/v3/crm-user-mapping`. The plain create path fails on an existing mapping; upsert does not. Conflict checks still apply — a Sprinklr user already bound to a *different* CRM user will still be rejected.

### 9.3 Map by email instead of user id

`sprUserId` accepts a **Sprinklr user id or email**, so you can map directly from your CRM's email field without a lookup:

```json
{
  "installedAppId": "69b2a3a6c28fba5e0bf50c2e",
  "crmUserId": "005gL000006r0fxQAA",
  "sprUserId": "agent.name@example.com"
}
```

The email must belong to the specified partner, or the call fails Email Validation.

### 9.4 Bulk-onboard at go-live

**Scenario:** several hundred mappings at connector rollout.

Build a CSV with `Sprinklr User Id/Email` and `CRM User ID` as the first-row headers, one row per pair, then:

```bash
curl -X PUT \
  'https://api3.sprinklr.com/{env}/api/v3/crm-user-mapping/import?installedAppId={installedAppId}' \
  -H 'Authorization: ******' -H 'key: {Enter your API KEY}' \
  -H 'Accept: text/csv' \
  -F 'file=@mappings.csv'
```

De-duplicate the file before uploading. A single duplicate id — on either side — aborts the entire import under the all-or-none principle.

### 9.5 Bulk-update existing mappings — the export/edit/import round trip

This is the supported way to change many mappings at once:

1. `GET /api/v3/crm-user-mapping/export?installedAppId={installedAppId}` with `Accept: text/csv`.
2. Open the exported file in a spreadsheet editor and update the `Sprinklr User Id/Email` or `CRM User ID` columns.
3. `PUT /api/v3/crm-user-mapping/import?installedAppId={installedAppId}` with the edited file.


Exporting first guarantees the header row is correct and that you are editing the live state rather than a stale snapshot.

### 9.6 Audit who changed a mapping and when

The fetch response carries provenance on every record: `createdBy`, `lastModifiedBy`, `createdTime`, and `lastModifiedTime` (epoch milliseconds). Page through with `pageNumber` and diff against your own records to detect out-of-band changes made through the Marketplace UI.

### 9.7 Offboard a single user

Send the `DELETE` with exactly one selector — whichever id you hold:

```json
{ "crmUserId": "005gL00000JrMx7QAF" }
```

This deletes all mapping records matching that id for the app. **Never send an empty body** on `DELETE` unless you intend to remove every mapping for the installed app.

### 9.8 Decommission a connector

To clear all mappings for an installed app, call `DELETE /api/v3/crm-user-mapping?installedAppId={installedAppId}` with **no** `crmUserId` and **no** `sprUserId`. Export first if you need a rollback artifact — there is no undo.

### 9.9 Page through a large mapping set

```javascript
let pageNumber = 0;
let all = [];

while (true) {
  const res = await get(
    `/api/v3/crm-user-mapping?installedAppId=${appId}&pageNumber=${pageNumber}`
  );

  if (res.errors.length) throw new Error(JSON.stringify(res.errors));
  if (!res.data.length) break;          // empty page ends the walk

  all = all.concat(res.data);
  pageNumber += 1;                      // pageNumber is zero-based
}
```

`pageNumber` is zero-based, and no page-size parameter is documented — the walk terminates on an empty `data` array rather than on a declared total. For large sets, `/export` is the more reliable bulk read.

### 9.10 Look up several users in one call

Both `crmUserId` and `sprUserId` accept comma-separated lists:

```
GET /api/v3/crm-user-mapping?installedAppId=69b2a3a6c28fba5e0bf50c2e&sprUserId=66007805,66016584
```

Cheaper than one round trip per user.

## 10. Caveats and best practices

**The one-to-one rule**

- Mapping is a strict **1-to-1 association** between a CRM user and a Sprinklr user. Neither side can participate in two mappings.
- `POST /crm-user-mapping` **does not upsert** — it fails if either id is already mapped. Use `/upsert` when the pair may already exist.
- Upsert does not bypass the conflict check: re-pointing a Sprinklr user who is already bound to a different CRM user is rejected.


**Delete is dangerously broad**

- Omitting both `crmUserId` and `sprUserId` deletes **all mappings for the installed app**. This is documented behavior, not an error.
- Passing one selector deletes **all records matching that id** — not necessarily a single row.
- Export before any bulk delete; there is no undo.


**Bulk import**

- **All-or-none.** One failed record aborts the entire operation — there is no partial application to reconcile.
- De-duplicate on both the CRM and Sprinklr columns before uploading.
- Headers must be exactly `Sprinklr User Id/Email` and `CRM User ID`, in the first row.
- CSV only. No XLSX.
- Prefer export → edit → import over hand-building a file, so the header row is guaranteed correct.


**Parameter placement**

- `installedAppId` sits in the **body** for create and upsert, and in the **query string** for fetch, delete, export, and import. This asymmetry is easy to get wrong.
- The V2 fetch took `InstalledAppId` as a **path** parameter; V3 moved it to the query string and lower-cased the initial letter.
- The `crmUserId` / `sprUserId` selectors on `DELETE` are sent in the **body** by the supplied collection, but are **query** parameters on `GET`.


**Types**

- `sprUserId` is a **String** in the request DTO (it may hold an id or an email) but comes back as an **Integer** in the fetch response. Do not assume symmetric types.
- Timestamps are epoch **milliseconds**, not seconds.


**Validation before you call**

- The CRM User ID must be valid for the specified installed app.
- The Sprinklr User ID must be valid for the designated partner organization.
- A Sprinklr email must belong to the specified partner.
- The Sprinklr user must have access to the installed app — the app must be shared with them.
- The installed app must not have been deleted.


**Response parsing**

- `errors` is present on every `APIResponse` and is `[]` on success. Inspect it even on `200`.
- `/export` and `/import` return a `string`, not an `APIResponse` envelope.