# External Authentication Credentials API V3 — Developer Guide

- **Applies to:** Sprinklr External Authentication Credentials API V3 (`/api/v3/connector-cred`)
- **V2 API reference:** [External Authentication Credential APIs | Sprinklr Developer Portal](https://dev.sprinklr.com/external-authentication-credential-apis)


## 1. Overview

The External Authentication Credentials APIs let you create and delete **external API credentials** inside Sprinklr, so Sprinklr can authenticate outbound calls to third-party applications on your behalf. Once a credential exists, Sprinklr modules — Bots, Guided Workflows, Agent Consoles, Care Consoles, Automation, and External REST API connectors — reference it by ID instead of storing secrets in each configuration.

The stored object is a **connector credential**: a named record holding an `authType` plus whatever fields that auth flow needs (client ID and secret, refresh token, JWT signing secret, API key, and so on). Sprinklr performs the token exchange at call time.

V3 exposes the credential lifecycle on a **single resource path** — `/api/v3/connector-cred` — differentiated by HTTP method:

| Operation | Method | Path |
|  --- | --- | --- |
| Create credential (all auth types) | `POST` | `/api/v3/connector-cred` |
| Delete credential | `DELETE` | `/api/v3/connector-cred?id=` |
| Read / list credentials | — | Not exposed in V3. See [§5](#5-read-operations). |
| Update credential | — | Not exposed in V3. Delete and recreate — see [§5](#5-read-operations). |


This is the central design change from V2, which documented **eight separate create pages** all posting to the same `POST /api/v2/connector-cred/create` endpoint, differentiated only by the `authType` value in the body.

For more details refer to [Connector Credential API Reference](/apis/sprinklr-v3/connector-credential-v3).

### 1.1 The credential data model

A connector credential has three conceptual layers:

| Layer | Fields | What it holds |
|  --- | --- | --- |
| Identity | `name`, `authType` | The two **required** fields on every request. `name` must be unique within the workspace. |
| Flow-specific secrets | `oauthClientId`, `oauthClientSecret`, `oauthRefreshToken`, `oauthAccessToken`, `oauthAccessTokenSecret`, `userId`, `password`, `apiKey`, `jwtSecret` | Only the subset relevant to the chosen `authType` |
| Transport and extension | `tokenEndpoint`, `grantType`, `scope`, `httpMethod`, `contentTypeHeader`, `customHeader`, `customTokenParams`, `customTokenHeaders`, `addAuthDataTo`, `requestViaProxy`, `jwtClaims`, `jwtHeaders`, `signatureAlgorithm`, `preRequestScript`, `responseAdapter`, `customClaimsAdapter`, `customTokenHeadersAdapter`, `key`, `label` | How Sprinklr builds and sends the token request |


Which fields you populate is driven entirely by `authType`. [§4.1](#41-post-apiv3connector-cred--create-a-credential) gives the per-flow field tables.

### 1.2 Addressing a credential

A credential is addressed by the **credential ID** returned when it was created — a 24-character hex ObjectId, for example `679228515882ec48d9c99e62`.

> ⚠️ **Capture the ID at creation time.** The create call returns the credential ID and it is the *only* handle you get. There is no read or list endpoint in either V2 or V3, so an ID you fail to persist cannot be recovered through the API — you would have to look the credential up in the Sprinklr UI. See [§5](#5-read-operations).


## 2. Base URLs and environments

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

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

So the connector credential resource in production is:

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

Replace `{env}` with your assigned environment identifier. See [APIs | Sprinklr Developer Portal](https://dev.sprinklr.com/apis) for the current environment list.

## 3. Authentication and common headers

All External Authentication Credentials API calls are authenticated with OAuth 2.0. See [Developer Tools in Sprinklr](https://www.sprinklr.com/help/articles/developer-tools/developer-tools-in-sprinklr/692e8b39f0afa271d18a5929) for API key and secret generation, and the Authorize flow.

| Header | Value | Purpose | Required on |
|  --- | --- | --- | --- |
| `Authorization` | `Bearer {{accessToken}}` | Authenticates the user with the server | All requests |
| `Key` | `{{apiKey}}` | Authenticates the application with the server | All requests |
| `Content-Type` | `application/json` | Declares the request body media type | `POST` |
| `Accept` | `application/json` | Declares the acceptable response type | All requests |


**Permissions.** Access to this API is more restricted than most Sprinklr APIs:

- Only **Global Admins** and **Global Users** can add external APIs in Sprinklr.
- The calling user needs the **External Authentication Credentials** permission and the **External API** permission.
- To grant access, an administrator creates a **custom role** with the required permissions and assigns it to the appropriate user or user group.
- Your organization must be on the Sprinklr **Enterprise** edition.


Missing permission returns `403 Forbidden`.

**Do not confuse the two credential layers.** The `Authorization` and `Key` headers authenticate *you to Sprinklr*. The body of the create call contains the credentials Sprinklr will use to authenticate *itself to a third party*. Both are secrets; neither should ever be logged or committed.

## 4. Write operations

### 4.1 `POST /api/v3/connector-cred` — Create a credential

**`operationId`:** `ConnectorCredentialApiV3_createConnectorCredential`
**Summary:** Create connector credentials.

The `authType` in the body already determines the flow, so separate endpoints added no value.

#### Request body — `ConnectorCredentialsDTO`

**Required on every request:** `name`, `authType`.

| Parameter | Type | Description |
|  --- | --- | --- |
| `name` | String | **Required.** Name of Credential. |
| `authType` | Object | **Required.** Credential Auth Type. See [§8.1](#81-authtype). |
| `tokenEndpoint` | String | Token Endpoint Url |
| `oauthClientId` | String | Client Id |
| `oauthClientSecret` | String | Client Secret |
| `oauthRefreshToken` | String | Refresh Token |
| `oauthAccessToken` | String | Access Token |
| `oauthAccessTokenSecret` | String | Access Token Secret |
| `requestViaProxy` | Boolean | flag for proxy |
| `userId` | String | User Id |
| `password` | String | Password |
| `customHeader` | String | Header Name if token is to be passed in separate header apart from Authorization |
| `grantType` | String | grantType |
| `contentTypeHeader` | String | Details about content-type |
| `customTokenParams` | Map[String → String] | custom Token params |
| `customTokenHeaders` | Map[String → String] | custom token headers |
| `jwtClaims` | Map[String → Object] | Details of jwt claims |
| `jwtHeaders` | Map[String → Object] | Details of jwt headers. |
| `jwtSecret` | String | jwtSecret |
| `signatureAlgorithm` | String | Algorithm type |
| `preRequestScript` | String | groovy script to build custom request |
| `responseAdapter` | String | groovy script to adapt custom response |
| `customClaimsAdapter` | String | groovy script to build custom claims |
| `customTokenHeadersAdapter` | String | groovy script to build custom token headers |
| `httpMethod` | String | Method Type for token call |
| `scope` | String | Scope of token |
| `apiKey` | String | Api Key info |
| `addAuthDataTo` | String | Add OAuth Data to header or request. |
| `key` | String | *(no description in the specification)* |
| `label` | String | *(no description in the specification)* |


#### Required fields by auth type

The following per-flow requirements are carried forward from the V2 reference pages. V3 keeps the same field names.

| authType | Required fields | Optional fields commonly used |
|  --- | --- | --- |
| `STD_BASIC_AUTH` | `name`, `authType`, `userId`, `password` | `grantType`, `requestViaProxy` |
| `BASIC` (OAuth 2.0 ROPC) | `name`, `authType`, `userId`, `password` | `grantType`, `requestViaProxy`, `tokenEndpoint`, `customHeader` |
| `OAUTH_REFRESH` | `name`, `authType`, `tokenEndpoint`, `oauthClientId`, `oauthClientSecret`, `oauthRefreshToken` | — |
| `OAUTH_PASSWORD` | `name`, `authType`, `tokenEndpoint`, `userId`, `password` | `oauthClientId`, `oauthClientSecret` |
| `OAUTH_TOKEN` (client credentials) | `name`, `authType`, `tokenEndpoint`, `oauthClientId`, `oauthClientSecret` | `scope`, `customHeader`, `requestViaProxy` |
| `OAUTH_1` | `name`, `authType`, `tokenEndpoint`, `oauthClientId`, `oauthClientSecret`, `addAuthDataTo` | `oauthAccessToken`, `oauthAccessTokenSecret` |
| `JWT` | `name`, `authType`, `signatureAlgorithm`, `jwtSecret`, `jwtClaims`, `jwtHeaders` | `responseAdapter`, `customClaimsAdapter`, `preRequestScript`, `requestViaProxy` |
| `CUSTOM_AUTH` | `name`, `authType` | `tokenEndpoint`, `httpMethod`, `contentTypeHeader`, `customTokenHeaders`, `customTokenParams`, `customHeader`, `preRequestScript`, `responseAdapter`, `requestViaProxy` |
| `API_KEY` | `name`, `authType`, `apiKey`, `password` | — |


#### Request — Standard Basic Auth

```bash
curl --location 'https://api3.sprinklr.com/{env}/api/v3/connector-cred' \
--header 'Authorization: Bearer {{accessToken}}' \
--header 'Key: {{apiKey}}' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--data '{
     "name": "Shivangi Test Basic", 
    "authType": "BASIC", 
    "grantType": "client_credentials", 
    "userId": "shivangi.singh+test@sprinklr.com", 
    "password": "1234@", 
    "requestViaProxy": true 
}'
```

#### Request — OAuth 2.0 ROPC (`BASIC`)

```bash
curl --location 'https://api3.sprinklr.com/{env}/api/v3/connector-cred' \
--header 'Authorization: Bearer {{accessToken}}' \
--header 'Key: {{apiKey}}' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--data '{
   "name": "Shivangi Test Basic", 
    "authType": "STD_BASIC_AUTH", 
    "grantType": "client_credentials", 
    "userId": "shivangi.singh+test@sprinklr.com", 
    "password": "1234@", 
    "requestViaProxy": true 
}'
```

#### Request — OAuth Refresh Token

```bash
curl --location 'https://api3.sprinklr.com/{env}/api/v3/connector-cred' \
--header 'Authorization: Bearer {{accessToken}}' \
--header 'Key: {{apiKey}}' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--data '{
    "name": "Avinash Test OAuth 1", 
    "authType": "OAUTH_REFRESH", 
    "tokenEndpoint": "https://www.google.com", 
    "oauthClientId": "shivangi+test@sprinklr.com", 
    "oauthClientSecret": "56789abcdefgh", 
    "oauthRefreshToken": " E13zqlMcOuEMobsKSLvdqCU4A6e1jddL8oM36tx8oQE1MTQzYmY2Ni0wM2U2LTM2NmUtOWFkMy01MGI5ZTE1ZjIwYTA=" 
}'
```

#### Request — Password Auth

```bash
curl --location 'https://api3.sprinklr.com/{env}/api/v3/connector-cred' \
--header 'Authorization: Bearer {{accessToken}}' \
--header 'Key: {{apiKey}}' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--data '{
   "name": "Shivangi Test Password", 
    "authType": "OAUTH_PASSWORD", 
    "tokenEndpoint": "https://www.google.com", 
    "userId": " shivangi.singh+test@sprinklr.com ", 
    "password": "1234@" 
}'
```

#### Request — Client Credentials (`OAUTH_TOKEN`)

```bash
curl --location 'https://api3.sprinklr.com/{env}/api/v3/connector-cred' \
--header 'Authorization: Bearer {{accessToken}}' \
--header 'Key: {{apiKey}}' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--data '{
 "name": "Shivangi Test OAuth Client", 
    "authType": "OAUTH_TOKEN", 
    "tokenEndpoint": "https://www.google.com", 
    "oauthClientId": "shivangi+test@sprinklr.com", 
    "oauthClientSecret": "56789abcdefgh " 
}'
```

#### Request — OAuth 1.0

```bash
curl --location 'https://api3.sprinklr.com/{env}/api/v3/connector-cred' \
--header 'Authorization: Bearer {{accessToken}}' \
--header 'Key: {{apiKey}}' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--data '{
    "name": "Shivangi Test OAuth", 
    "authType": "OAUTH_1", 
    "tokenEndpoint": "https://www.google.com", 
    "oauthClientId": "shivangi+test@sprinklr.com", 
    "oauthClientSecret": "56789abcdefgh ", 
    "addAuthDataTo": "request", 
    "oauthAccessToken": "E13zqlMcOuEMobsKSLvdqCU4A6e1jddL8oM36tx8", 
    "oauthAccessTokenSecret": "MeE6EjoFfmsMa9Wy6RMSiYXxKpF3VSpSHx" 
}'
```

#### Request — JWT

```bash
curl --location 'https://api3.sprinklr.com/{env}/api/v3/connector-cred' \
--header 'Authorization: Bearer {{accessToken}}' \
--header 'Key: {{apiKey}}' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--data '{
    "name": "Shivangi Test JWT",  
    "authType": "JWT",  
    "signatureAlgorithm": "HS256",  
    "jwtSecret": "asd",  
    "jwtClaims": { 
                    "userRole": "admin", 
                    "organizationId": "org-12345", 
                    "isVerified": true 
    },  
    "jwtHeaders": {  
                     "x-client-id": "12345",  
                     "x-request-id": "abcde-12345"  
                       }  
    } 
}'
```

#### Request — API Key

```bash
curl --location 'https://api3.sprinklr.com/{env}/api/v3/connector-cred' \
--header 'Authorization: Bearer {{accessToken}}' \
--header 'Key: {{apiKey}}' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--data '{
    "name": "Acme API Key",
    "authType": "API_KEY",
    "apiKey": "acmeapikey@1234",
    "password": "acme@1234"
}'
```

#### Response

```json
{
    "data": "67990812259f4a0ca47468ab",
    "errors": []
}
```

| Parameter | Type | Description |
|  --- | --- | --- |
| `data` | String | A unique identifier associated with the newly created authentication credential. **This ID is what you pass to the delete call.** |
| `errors` | Array | Array of errors, if any. Empty when there are none. |


> **Duplicate names are rejected.** *"When attempting to create connector credentials, the API will return a 400 Bad Request error response if the provided credential name already exists."* Name your credentials deterministically and handle `400` as "already exists", not as a malformed payload.


### 4.2 `DELETE /api/v3/connector-cred` — Delete a credential

**`operationId`:** `ConnectorCredentialApiV3_deleteCredentials`
**Summary:** Delete connector credentials by id.

Replaces `DELETE /api/v2/connector-cred/{credentialId}`. The credential ID moves from a **path segment** to a **query parameter**, consistent with the V3 pattern.

#### Query parameters

| Parameter | Type | Required | Description | Example |
|  --- | --- | --- | --- | --- |
| `id` | String | Required | Connector credential id | `679228515882ec48d9c99e62` |


#### Request

```bash
curl --location --request DELETE \
  'https://api3.sprinklr.com/{env}/api/v3/connector-cred?id=679228515882ec48d9c99e62' \
  --header 'Authorization: Bearer {{accessToken}}' \
  --header 'Key: {{apiKey}}' \
  --header 'Accept: application/json'
```

#### Response

*(illustrative example, in the V2 envelope shape)*

```json
{
    "data": true,
    "errors": []
}
```

| Parameter | Type | Description |
|  --- | --- | --- |
| `data` | Boolean | Indicates the success of the operation. `true` signifies that the deletion was successful; `false` indicates that the specified credential ID had already been deleted. |
| `errors` | Array | Array of errors, if any. Empty when there are none. |


## 5. Read operations

**V3 exposes no read, list, or search operation for connector credentials.** Neither does V2.

This has three practical consequences:

1. **Persist the credential ID at creation time.** The `POST` response is the only place the ID is returned programmatically. Store it alongside your own record of which integration uses it.
2. **There is no API to verify a credential exists** before deleting it or before pointing a module at it. Attempting the delete is the only programmatic probe, and that is destructive.
3. **There is no update operation.** To rotate a secret — a new client secret, a new refresh token, a new API key — you must **create a new credential and delete the old one**, then repoint any module that referenced it. Plan rotation as a create-repoint-delete sequence, not an in-place edit.


To view or edit existing credentials interactively, use **Connections Manager** in the Sprinklr UI (Launchpad → Platform Modules → Listen → Settings → All Settings → **APIs & Integrations** → **Connections Manager**, or on the old settings page, **Manage Customer** → Connections Manager). Connections Manager supports Edit and Delete per connection, plus filtering by connection type, authentication type, platform type, and creator. See [§10](#10-caveats-and-best-practices).

## 6. Response format and status codes

### 6.1 The response envelope

Every V2 External Authentication Credentials example returns the standard Sprinklr envelope:

```json
{
    "data": "67990812259f4a0ca47468ab",
    "errors": []
}
```

| Field | Type | Description |
|  --- | --- | --- |
| `data` | String (create) / Boolean (delete) | The credential ID on create; the success flag on delete |
| `errors` | Array[Error] | Array of error objects (empty if no errors) |


### 6.2 Response codes

The OpenAPI document declares the same response set on both operations:

| HTTP Code | Scenario | Description |
|  --- | --- | --- |
| `200 OK` | Success | Credential created (returns the ID) or deleted (returns `true`) |
| `400 Bad Request` | Invalid request | Missing `name` or `authType`; unsupported `authType`; missing auth-type-specific required fields; malformed URLs, tokens, or keys; empty payload; **or a credential name that already exists** |
| `401 Unauthorized` | Authentication failed | Invalid or missing `Authorization` token |
| `403 Forbidden` | Insufficient permissions | The user lacks the External Authentication Credentials or External API permission |
| `404 Not Found` | Credential not found | The supplied credential ID does not exist, or has already been deleted |


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

## 7. V2 → V3 migration

### 7.1 Endpoint mapping

| What you want to do | V2 | V3 |
|  --- | --- | --- |
| Create Basic Auth credentials | `POST /api/v2/connector-cred/create` — `authType: STD_BASIC_AUTH` | `POST /api/v3/connector-cred` — `authType: STD_BASIC_AUTH` |
| Create OAuth 2.0 ROPC credentials | `POST /api/v2/connector-cred/create` — `authType: BASIC` | `POST /api/v3/connector-cred` — `authType: BASIC` |
| Create OAuth Refresh Token credentials | `POST /api/v2/connector-cred/create` — `authType: OAUTH_REFRESH` | `POST /api/v3/connector-cred` — `authType: OAUTH_REFRESH` |
| Create Password Auth credentials | `POST /api/v2/connector-cred/create` — `authType: OAUTH_PASSWORD` | `POST /api/v3/connector-cred` — `authType: OAUTH_PASSWORD` |
| Create Client Credentials | `POST /api/v2/connector-cred/create` — `authType: OAUTH_TOKEN` | `POST /api/v3/connector-cred` — `authType: OAUTH_TOKEN` |
| Create OAuth 1.0 credentials | `POST /api/v2/connector-cred/create` — `authType: OAUTH_1` | `POST /api/v3/connector-cred` — `authType: OAUTH_1` |
| Create JWT credentials | `POST /api/v2/connector-cred/create` — `authType: JWT` | `POST /api/v3/connector-cred` — `authType: JWT` |
| Create Custom Auth credentials | `POST /api/v2/connector-cred/create` — `authType: CUSTOM_AUTH` | `POST /api/v3/connector-cred` — `authType: CUSTOM_AUTH` |
| Create API Key credentials | `POST /api/v2/connector-cred/create` — `authType: API_KEY` | `POST /api/v3/connector-cred` — `authType: API_KEY` |
| Delete a credential | `DELETE /api/v2/connector-cred/{credentialId}` | `DELETE /api/v3/connector-cred?id={id}` |


### 7.2 Key structural changes

- **Verb removed from path** — `/connector-cred/create` → `/connector-cred`; POST already implies creation.
- **credentialId moves to query param** — `/connector-cred/{credentialId}` → `/connector-cred?credentialId={id}`, consistent with v3 pattern.


### 7.3 What actually changes in your client

|  | V2 | V3 |
|  --- | --- | --- |
| Create URL | `/api/v2/connector-cred/create` | `/api/v3/connector-cred` |
| Create method | `POST` | `POST` (unchanged) |
| Create body | `ConnectorCredentialsDTO` | **Unchanged** — same field names |
| Delete URL | `/api/v2/connector-cred/{credentialId}` | `/api/v3/connector-cred?id={id}` |
| Delete method | `DELETE` | `DELETE` (unchanged) |
| ID location | Path segment | **Query parameter** |
| ID parameter name | `credentialId` | **`id`** per the specification (`credentialId` per the story — see [§1.2](#12-addressing-a-credential)) |


## 8. Supported enums

### 8.1 `authType`

The nine supported values, with the V2 descriptions:

| Authentication Name | AuthType Value | Description |
|  --- | --- | --- |
| Basic Auth | `STD_BASIC_AUTH` | Username and password based authentication, encoded in Base64 and passed in the Authorization header. |
| OAUTH 2.0 ROPC | `BASIC` | OAuth 2.0 Resource Owner Password Credentials flow that uses a Base64-encoded username and password as a Basic Auth header to obtain an access token from the Token URL. |
| OAuth Refresh Token | `OAUTH_REFRESH` | OAuth 2.0 authentication using a Client ID, Client Secret, Token URL, and Refresh Token to generate a new access token. |
| Password Auth | `OAUTH_PASSWORD` | OAuth 2.0 authorization using the 'password' grant type with a username and password to generate an access token from the Token URL. |
| Client Credential Auth | `OAUTH_TOKEN` | OAuth 2.0 authorization using the 'client_credentials' grant type with a Client ID and Client Secret to obtain an access token from the Token URL. |
| OAuth 1.0 | `OAUTH_1` | Authentication where each request is securely signed with a shared secret and token credentials. |
| JWT Auth | `JWT` | Authentication using a digitally signed JSON Web Token (JWT) to verify identity and grant API access. |
| Custom Auth | `CUSTOM_AUTH` | A custom authentication mechanism tailored to the specific needs of the application. |
| API Key | `API_KEY` | Uses an API Key and Password to authorize access. |


## 9. Use cases

### 9.1 Provision a Salesforce-style client-credentials connection from a deployment pipeline

**Scenario:** your infrastructure-as-code pipeline stands up a new Sprinklr workspace and must register the CRM connection automatically.

```
POST /api/v3/connector-cred
{ "name": "acme-crm-prod", "authType": "OAUTH_TOKEN", "tokenEndpoint": "...",
  "oauthClientId": "...", "oauthClientSecret": "...", "scope": "..." }
```

Capture `data` from the response and write it to your own configuration store. It is the only handle to the credential.

### 9.2 Make credential provisioning idempotent

**Scenario:** your pipeline re-runs and must not fail on an already-provisioned credential.

There is no "get by name" endpoint, so idempotency must be built on the documented `400`-on-duplicate-name behavior:

```
POST /api/v3/connector-cred  →  400  ⇒  credential with this name already exists
```

Use deterministic names (`acme-crm-prod`, not `acme-crm-2026-08-30`) and treat `400` on create as a benign "already exists" **only** when your payload is known-valid. Because `400` also covers genuine validation failures, inspect `errors[].message` to distinguish the two rather than branching on the status code alone.

### 9.3 Rotate a leaked client secret

**Scenario:** a third-party client secret has been rotated upstream and must be updated in Sprinklr.

There is no update endpoint, so rotation is a three-step sequence:

1. `POST /api/v3/connector-cred` with a **new name** and the new secret → capture the new ID.
2. Repoint the consuming module(s) — External REST API connector, Bot, Guided Workflow — at the new credential.
3. `DELETE /api/v3/connector-cred?id={oldId}`.


Do not delete first. Between step 1 and step 2 both credentials exist, which is what keeps the integration up during rotation.

### 9.4 Register a JWT-signed integration

**Scenario:** a partner API requires an HS256-signed JWT with custom claims and headers on every call.

```
POST /api/v3/connector-cred
{ "name": "partner-jwt", "authType": "JWT", "signatureAlgorithm": "HS256",
  "jwtSecret": "{{externalJwtSecret}}",
  "jwtClaims": { "userRole": "admin", "organizationId": "org-12345" },
  "jwtHeaders": { "x-client-id": "12345" } }
```

`jwtClaims` values are typed as `Object` in the specification, so booleans and numbers are valid claim values, not just strings — the V2 example includes `"isVerified": true`.

### 9.5 Handle a non-standard token endpoint with Custom Auth

**Scenario:** the third-party token endpoint expects form-encoded parameters and a non-standard response body.

Use `authType: CUSTOM_AUTH` with `httpMethod`, `contentTypeHeader`, `customTokenParams`, and `customTokenHeaders`, and supply Groovy scripts via `preRequestScript` (build the request) and `responseAdapter` (parse the response). These script fields execute inside Sprinklr — treat them as production code and review them accordingly.

### 9.6 Decommission an integration cleanly

**Scenario:** a third-party system is being retired.

1. Confirm no Sprinklr module still references the credential — **this must be checked in Connections Manager in the UI**, since no API lists credential usage.
2. `DELETE /api/v3/connector-cred?id={id}`.
3. Treat both `data: true` and a `404` as success; treat `data: false` as "already gone".


### 9.7 Provision one credential per auth type in a test harness

The IN-12945 QA plan exercises exactly this: create a credential for each of the nine `authType` values, verify each is created successfully, then verify the negative paths — missing `authType`, invalid `authType`, missing per-flow required fields such as `clientId` for OAuth, malformed URLs and tokens, and an empty payload returning `400 Bad Request`. Mirroring that matrix is a reasonable acceptance suite for your own migration.

## 10. Caveats and best practices

**Secret handling — read this first**

- This API transports **third-party secrets in request bodies**. Never log request bodies, never commit them, and never echo them into CI output.
- The API returns only an ID. It does not return stored secrets — do not build any flow that assumes you can read a secret back.
- Use placeholders in all shared examples and runbooks (`{{externalClientSecret}}`, `{{externalPassword}}`).
- The `Authorization`/`Key` headers and the body credentials are two different secrets serving two different trust boundaries. Rotate them on independent schedules.
- `preRequestScript`, `responseAdapter`, `customClaimsAdapter`, and `customTokenHeadersAdapter` accept **Groovy scripts that Sprinklr executes**. Subject them to the same review as any deployed code.


**No read, no update**

- Persist the credential ID returned by `POST`. It is unrecoverable through the API.
- Rotation is create → repoint → delete. There is no in-place edit.
- You cannot programmatically check whether a credential exists without attempting a destructive delete.


**Naming**

- Credential names must be unique; a duplicate returns `400 Bad Request`, not a conflict-specific status.
- Use deterministic, environment-scoped names so re-runs are predictable.


**Access control**

- Only Global Admins and Global Users can add external APIs.
- The caller needs both the **External Authentication Credentials** and **External API** permissions, granted via a custom role.
- Enterprise edition is required.


**Static authentication**

- For static (key-based) authentication, there is **no need to add external authentication credentials in Sprinklr** at all. Check whether your integration needs a stored credential before creating one.


*All JSON payloads in this guide are illustrative examples derived from the supplied specifications and published references. They are not real customer data and are not guaranteed production responses. All credentials are placeholders (`{{accessToken}}`, `{{apiKey}}`, `{{externalClientSecret}}`, and similar) and must never be committed or logged.*