# Suppression List API V3

The Suppression List API lets you manage the phone numbers that must not be contacted by your outbound voice campaigns. Use it to add a number the moment a customer opts out, synchronise a do-not-call registry in bulk, and remove a number when consent is regained.

**V2 reference:** [Add Contact in Suppression List](https://dev.sprinklr.com/add-contact-in-suppression-list) · [Bulk Add Contacts in Suppression List](https://dev.sprinklr.com/bulk-add-contacts-in-suppression-list) · [Remove Contact from Suppression List](https://dev.sprinklr.com/remove-contact-from-suppression-list)

## Overview

A **suppression list** is a list of phone numbers that must not be contacted by outbound campaigns. It typically holds numbers of people who have asked to be placed on a "do not call" list, who have asked to be removed from a calling list, or who are ineligible for certain types of calls for regulatory or compliance reasons.

Maintaining an accurate suppression list is a compliance requirement, not a convenience. Use this API when suppression decisions originate in your own systems — a CRM opt-out flow, a DNC-registry sync, a consent-management platform, or any service that must suppress a number promptly after a customer requests it.

Suppression lists can also be managed directly in the Sprinklr UI under **Voice Care → Voice Settings → Suppression List**. Both paths act on the same underlying list.

### Suppression is time-bounded

Suppression is not permanent by default. A suppressed number will not receive calls **until its expiry time**, after which it becomes contactable again. Two fields on the create payload express this:

| Field | Meaning |
|  --- | --- |
| `expiryTime` | Absolute expiry instant, in epoch milliseconds |
| `expiryIntervalType` | Expiry interval type |


Set these deliberately. If a customer has made a permanent do-not-call request, confirm with your Sprinklr account team how your tenant expresses indefinite suppression before relying on a default.

### The four operations

| Operation | Method | Path |
|  --- | --- | --- |
| Add a single contact | `POST` | `/api/v3/suppressionList/contact` |
| Bulk add contacts (asynchronous) | `POST` | `/api/v3/suppressionList/contact/bulk` |
| Remove a contact | `DELETE` | `/api/v3/suppressionList/contact` |
| Poll bulk job status | `GET` | `/api/v3/suppressionList/bulk/status` |


Add and remove share a single path and are distinguished by HTTP method.

### Reading suppression state

The API provides write and job-status operations. It does not provide an endpoint to list suppression lists, enumerate the contacts on a list, or check whether an individual number is currently suppressed.

Plan for this in your integration:

- Obtain your `suppressionListId` from the Sprinklr UI — see [Find your suppression list ID](#find-your-suppression-list-id).
- Keep your own authoritative record of every number you have suppressed and removed, along with the API response for each call.
- Base reconciliation on your own records rather than on a query against Sprinklr.


## Base URL and environments

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

Replace `{env}` with the environment hosting your Sprinklr tenant:

`prod` · `prod0` · `prod2` · `prod3` · `prod4` · `prod5` · `prod6` · `prod8` · `prod11` · `prod12` · `prod15` · `prod16` · `prod17` · `prod18` · `prod19` · `prod21` · `prod25`

If you are unsure which environment applies, see the [APIs](https://dev.sprinklr.com/apis) page or ask your Sprinklr account team. Calling the wrong environment authenticates against a different tenant and the request will fail.

## Authentication

Every request requires both credentials:

| Header | Value |
|  --- | --- |
| `Authorization` | `Bearer {{accessToken}}` |
| `Key` | `{{apiKey}}` |


Requests that send a body also require:

| Header | Value |
|  --- | --- |
| `Content-Type` | `application/json` |


### Credential lifetimes

| Credential | Lifetime |
|  --- | --- |
| Authorization code | 10 minutes |
| API key | No expiry |
| Access token | 30 days |


Only **one token pair per API key** is valid at a time. Generating a new token pair invalidates the previous one — a common cause of a scheduled job that starts returning `401` without any change on your side.

For setup, see [API Key and Secret Generation](https://dev.sprinklr.com/api-key-and-secret-generation) and [Authorize](https://dev.sprinklr.com/authorize).

### Permissions

Authentication and authorization are checked separately. A valid token whose associated user lacks rights to modify the target suppression list returns `403`, not `401`. Confirm that the user behind your API credentials has suppression-list permissions in Sprinklr.

> **Credential hygiene.** Treat your access token and API key as secrets. Do not commit them to source control, do not write them to logs, and do not paste them into support requests or chat messages. Rotate immediately if a credential is exposed.


## Operations

### Add a contact

```
POST /api/v3/suppressionList/contact
```

Adds a single phone number to a suppression list. Send all fields in the JSON body, including `suppressionListId`.

|  |  |
|  --- | --- |
| Request body | `SuppressionListContactCreateRequestDTO` (required) |
| Success | `200` |
| Errors | `400`, `401`, `403`, `404` |


Send `suppressionListId` and `contactValue` on every request. The response returns the contact as stored.

### Bulk add contacts

```
POST /api/v3/suppressionList/contact/bulk
```

Adds many contacts in one call. **This operation is asynchronous by default**: a `200` confirms that the job was accepted, not that every contact was suppressed.

|  |  |
|  --- | --- |
| Request body | `BulkSuppressionListContactRequestDTO` (required) |
| Success | `200` |
| Errors | `400`, `401`, `403`, `404` |


To learn the outcome, choose one of three approaches:

| Approach | How | Best for |
|  --- | --- | --- |
| Poll | Take the process id from the response, then call the bulk status endpoint | Batch jobs where you control the schedule |
| Callback | Supply `callbackUrl` and let Sprinklr notify you on completion | Large jobs, event-driven pipelines |
| Synchronous | Set `syncProcessing: true` and read per-record results from the response | Small batches where a caller is waiting |


#### Choosing synchronous or asynchronous

The `syncProcessing` flag determines the shape of your integration:

| `syncProcessing` | Behaviour | What you need |
|  --- | --- | --- |
| `false` or omitted | The job is queued and processed in the background | A process id, plus polling or a callback |
| `true` | Records are processed during the request and per-record results are returned immediately | Nothing further — read the response |


Use `syncProcessing: true` for small, interactive batches where the caller needs to know at once which records failed. Use the asynchronous default for large batches and scheduled synchronisations, where holding an HTTP connection open is a liability.

Keep synchronous batches small. Confirm a safe batch size with your Sprinklr account team before relying on synchronous processing for a large volume.

### Poll bulk job status

```
GET /api/v3/suppressionList/bulk/status?processId={processId}
```

Returns the status of a bulk job submitted asynchronously.

| Parameter | In | Type | Description |
|  --- | --- | --- | --- |
| `processId` | query | string | Bulk process id returned by the bulk add call |


|  |  |
|  --- | --- |
| Success | `200` |
| Errors | `400`, `401`, `403`, `404` |


Always supply `processId`. Poll with a widening backoff — start at a few seconds and increase — rather than in a tight loop.

### Remove a contact

```
DELETE /api/v3/suppressionList/contact?suppressionListId={id}&contactValue={phoneNumber}
```

Removes a phone number from a suppression list, making it contactable again.

| Parameter | In | Type | Description |
|  --- | --- | --- | --- |
| `suppressionListId` | query | string | Suppression list id |
| `contactValue` | query | string | Phone number to remove |


|  |  |
|  --- | --- |
| Success | `200` |
| Errors | `400`, `401`, `403`, `404` |


Supply both parameters on every request.

> ### Encode the leading `+` as `%2B`
`contactValue` is a phone number, and E.164 numbers begin with `+`. Inside a URL query string a literal `+` is decoded as a **space**, not a plus sign.
Sending `?contactValue=+14155550142` transmits `" 14155550142"` — a space followed by digits — which will not match the stored contact.
Percent-encode the plus sign:

```
?contactValue=%2B14155550142
```
Use your HTTP library's URL-encoding function rather than building the query string by concatenation. This is the most common error on this API.


## Field reference

### `SuppressionListContactCreateRequestDTO`

Request body for adding a contact to a suppression list.

| Field | Type | Description |
|  --- | --- | --- |
| `contactValue` | string | The phone number to suppress. Send on every request |
| `suppressionListId` | string | The suppression list to add the contact to. Send on every request |
| `name` | string | Display name for the contact |
| `expiryTime` | integer (int64) | Expiry time in epoch milliseconds |
| `expiryIntervalType` | string | Expiry interval type |
| `id` | string | Identifier of the stored contact. Returned by the service; do not send on create |
| `createdTime` | integer (int64) | Creation time in epoch milliseconds. Returned by the service; do not send on create |


`expiryTime` is epoch **milliseconds**. A ten-digit value is seconds and will resolve to 1970 — multiply by 1000.

For the interval values your tenant accepts in `expiryIntervalType`, check with your Sprinklr account team.

### `BulkSuppressionListContactRequestDTO`

Bulk suppression list contact request.

| Field | Type | Description |
|  --- | --- | --- |
| `records` | array | The contacts to suppress. Each element carries the same fields as a single add request |
| `syncProcessing` | boolean | When `true`, process records synchronously in-request and return per-record results immediately |
| `callbackUrl` | string | HTTPS endpoint Sprinklr calls when an asynchronous job completes |
| `callbackUrlHeaders` | object (string → string) | Optional HTTP headers sent with the callback request |


#### Securing the callback

Use `callbackUrlHeaders` to authenticate the inbound call, so your receiver can verify the request came from Sprinklr:

```json
"callbackUrl": "https://your-host.example.com/hooks/suppression-complete",
"callbackUrlHeaders": {
  "X-Webhook-Secret": "{{yourWebhookSecret}}"
}
```

Build the receiver defensively: accept and log the full body before parsing, and do not assume any individual field is present.

## Worked examples

> Examples use placeholder credentials and the `prod2` environment. Identifiers and phone numbers are illustrative. Substitute your own values.


### Add a single contact

```bash
curl -X POST \
  'https://api3.sprinklr.com/prod2/api/v3/suppressionList/contact' \
  -H 'Authorization: Bearer {{accessToken}}' \
  -H 'Key: {{apiKey}}' \
  -H 'Content-Type: application/json' \
  -d '{
    "suppressionListId": "0000000000000000000000a1",
    "contactValue": "+14155550142",
    "name": "Jane Doe — DNC request",
    "expiryTime": 1790000000000
  }'
```

A phone number inside a **JSON body** does not need percent-encoding. The `%2B` rule applies only to the `DELETE` query string.

### Bulk add, asynchronous with a callback

```bash
curl -X POST \
  'https://api3.sprinklr.com/prod2/api/v3/suppressionList/contact/bulk' \
  -H 'Authorization: Bearer {{accessToken}}' \
  -H 'Key: {{apiKey}}' \
  -H 'Content-Type: application/json' \
  -d '{
    "syncProcessing": false,
    "callbackUrl": "https://your-host.example.com/hooks/suppression-complete",
    "callbackUrlHeaders": {
      "X-Webhook-Secret": "{{yourWebhookSecret}}"
    },
    "records": [
      {
        "suppressionListId": "0000000000000000000000a1",
        "contactValue": "+14155550142",
        "name": "Jane Doe — DNC request"
      },
      {
        "suppressionListId": "0000000000000000000000a1",
        "contactValue": "+14155550187",
        "name": "John Roe — opt-out"
      }
    ]
  }'
```

Capture the process id from the response and use it to poll for completion, or wait for the callback.

### Bulk add, synchronous

```bash
curl -X POST \
  'https://api3.sprinklr.com/prod2/api/v3/suppressionList/contact/bulk' \
  -H 'Authorization: Bearer {{accessToken}}' \
  -H 'Key: {{apiKey}}' \
  -H 'Content-Type: application/json' \
  -d '{
    "syncProcessing": true,
    "records": [
      {
        "suppressionListId": "0000000000000000000000a1",
        "contactValue": "+14155550142"
      }
    ]
  }'
```

With `syncProcessing: true` the response carries per-record results directly. There is no process id and no polling.

### Poll a bulk job

```bash
curl -X GET \
  'https://api3.sprinklr.com/prod2/api/v3/suppressionList/bulk/status?processId={{processId}}' \
  -H 'Authorization: Bearer {{accessToken}}' \
  -H 'Key: {{apiKey}}'
```

### Remove a contact

```bash
curl -X DELETE \
  'https://api3.sprinklr.com/prod2/api/v3/suppressionList/contact?suppressionListId=0000000000000000000000a1&contactValue=%2B14155550142' \
  -H 'Authorization: Bearer {{accessToken}}' \
  -H 'Key: {{apiKey}}'
```

Note `%2B` in place of the leading `+`.

In Python, let the library handle encoding:

```python
import requests

params = {
    "suppressionListId": "0000000000000000000000a1",
    "contactValue": "+14155550142",   # encoded correctly by requests
}

resp = requests.delete(
    "https://api3.sprinklr.com/prod2/api/v3/suppressionList/contact",
    params=params,
    headers={
        "Authorization": "Bearer {{accessToken}}",
        "Key": "{{apiKey}}",
    },
)
```

In JavaScript, use `URLSearchParams`:

```javascript
const params = new URLSearchParams({
  suppressionListId: "0000000000000000000000a1",
  contactValue: "+14155550142",   // encoded correctly by URLSearchParams
});

await fetch(
  `https://api3.sprinklr.com/prod2/api/v3/suppressionList/contact?${params}`,
  {
    method: "DELETE",
    headers: {
      Authorization: "Bearer {{accessToken}}",
      Key: "{{apiKey}}",
    },
  }
);
```

## Response format and status codes

### Response envelope

Bulk add, bulk status, and remove return the standard V3 envelope:

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

Each entry in `errors` carries:

| Field | Type |
|  --- | --- |
| `id` | string |
| `code` | string |
| `message` | string |


**Check `errors` even when the status is `200`.** A bulk job can succeed at the transport level while individual records fail — the HTTP status will not tell you that.

### Status codes

| Code | Meaning |
|  --- | --- |
| `200` | Success |
| `400` | Bad request — a required field or parameter is missing or malformed |
| `401` | Unauthorized — missing, invalid, or expired credentials |
| `403` | Forbidden — valid credentials without rights to modify this suppression list |
| `404` | Not found — unknown suppression list, or wrong environment |


Also handle `429` and `5xx` with retry and exponential backoff, as you would for any Sprinklr API. See [REST API Errors and Status Codes](https://dev.sprinklr.com/rest-api-error-and-status-codes).

### Troubleshooting

| Symptom | Likely cause | Fix |
|  --- | --- | --- |
| `DELETE` succeeds but the number is still suppressed | The leading `+` was sent literally and decoded as a space | Percent-encode as `%2B` |
| `400` on `DELETE` | `contactValue` or `suppressionListId` missing | Send both parameters |
| `400` on add | `suppressionListId` or `contactValue` missing from the body | Send both fields |
| `400` on bulk status | `processId` missing or unrecognised | Use the process id returned by the bulk add call |
| `401` on a job that worked previously | Access token expired after 30 days, or a new token pair was generated against the same API key | Re-authorize; remember one token pair per key |
| `403` with valid credentials | The user lacks rights to modify this suppression list | Grant suppression-list permissions in Sprinklr |
| `404` | Unknown `suppressionListId`, or the wrong `{env}` in the base URL | Verify the list ID in the UI and confirm your environment |
| A suppressed number is called anyway | The expiry time has passed | Suppression is time-bounded; review `expiryTime` |
| Bulk returns `200` but nothing is suppressed | The job was accepted, then failed during processing | Poll the bulk status endpoint or inspect the callback |
| Timestamps resolve to 1970 | `expiryTime` sent in seconds | Use epoch milliseconds |


## Migrating from V2

### Path mapping

| V2 | V3 |
|  --- | --- |
| `POST /api/v2/suppressionList/addContact` | `POST /api/v3/suppressionList/contact` |
| `POST /api/v2/suppressionList/addContacts` | `POST /api/v3/suppressionList/contact/bulk` |
| `POST /api/v2/suppressionList/removeContact/{suppressionListId}/{contactValue}` | `DELETE /api/v3/suppressionList/contact?suppressionListId={id}&contactValue={value}` |
| *(new in V3)* | `GET /api/v3/suppressionList/bulk/status?processId={id}` |


### What changed

**Verbs moved out of the path.** `/addContact` becomes `POST /contact`, `/addContacts` becomes `POST /contact/bulk`, and `/removeContact` becomes `DELETE /contact`. The HTTP method now carries the verb, so one path serves both add and remove.

**Identifiers moved to query parameters.** V2's path-segment form `/removeContact/{suppressionListId}/{contactValue}` becomes `DELETE /contact?suppressionListId={id}&contactValue={value}`.

> This is the change most likely to break existing code. In V2 the phone number was a **path segment**; in V3 it is a **query parameter**, where a literal `+` decodes to a space. Code that built the V2 URL by string concatenation will produce a silently incorrect request in V3 — the call returns success and the number stays suppressed. Switch to a proper URL encoder and verify that `+` becomes `%2B`.


**Bulk add became a sub-resource.** Plural `/addContacts` becomes `/contact/bulk`. The array of contacts is now wrapped in an object as the `records` field, alongside `syncProcessing`, `callbackUrl`, and `callbackUrlHeaders`, rather than being posted as a bare array.

**Remove is now `DELETE`.** V2 used `POST /removeContact`. V3 uses `DELETE` with query parameters, matching REST conventions. Confirm that your HTTP client handles a `DELETE` with no request body — some libraries treat this awkwardly.

### Business behaviour is unchanged

V3 is an interface redesign. Adding and removing a contact produce the same outcome as the equivalent V2 calls.

### Migration checklist

- [ ] Change the remove call from `POST` to `DELETE`.
- [ ] Move `suppressionListId` and `contactValue` from path segments to query parameters on the remove call.
- [ ] Replace manual URL string-building with a URL encoder, and verify `+` becomes `%2B`.
- [ ] Wrap the bulk array in `{ "records": [...] }` instead of posting a bare array.
- [ ] Decide `syncProcessing` per call site — `true` for small interactive batches, `false` for scheduled synchronisations.
- [ ] For asynchronous bulk, implement polling or a callback receiver. Do not treat `200` as completion.
- [ ] If you use a callback, set `callbackUrlHeaders` with a shared secret so you can authenticate the inbound call.
- [ ] Confirm the accepted `expiryIntervalType` values for your tenant.
- [ ] Update base URLs to `https://api3.sprinklr.com/{env}/api/v3/...`.
- [ ] Add the `Key` header alongside `Authorization` on every request.
- [ ] Test the `403` path — permissions are checked separately from authentication.
- [ ] Confirm `expiryTime` values are epoch milliseconds.
- [ ] Keep your own record of suppressions, since V3 has no read endpoint to reconcile against.


## Common tasks

### Find your suppression list ID

There is no API to list suppression lists. Read the ID from the Sprinklr UI:

1. Open **Voice Care** within the **Sprinklr Service** module.
2. In the **Voice Settings** menu on the far left, select **Suppression List**.
3. Click the three dots beside the suppression list you want to use.
4. Select **View** from the drop-down menu.
5. The suppression list ID is the ID appended in the page URL.


Store the ID in your configuration. It is stable, and every add and remove call needs it.

### Suppress a number when a customer opts out

Call `POST /suppressionList/contact` from your opt-out handler with `contactValue` set to the customer's number. Because this write is compliance-critical, do not fire and forget: check the response, and queue a retry if the call fails.

Set `expiryTime` only when the opt-out is genuinely temporary.

### Synchronise a do-not-call registry nightly

Use asynchronous bulk. Post the batch with `syncProcessing: false` and a `callbackUrl`, store the process id, and let the callback report completion. Poll the bulk status endpoint as a fallback if the callback has not arrived within your expected window.

Because there is no read endpoint, compute the nightly delta against your own record of what you last sent.

### Remove a number when consent is regained

Call `DELETE /suppressionList/contact` with both query parameters, correctly encoded. Record the removal in your own system — you cannot query the list to confirm it later.

## Best practices

**Always URL-encode the phone number on `DELETE`.** The leading `+` must become `%2B`. Use your library's encoder rather than string concatenation.

**Send `suppressionListId` and `contactValue` on every call.** Both are required in practice for add and remove.

**Treat a bulk `200` as acceptance, not completion.** Confirm the outcome by polling the status endpoint, handling the callback, or using `syncProcessing: true`.

**Inspect the `errors` array even on `200`.** Bulk jobs can partially succeed, with some records rejected as invalid or already present.

**Deduplicate and validate before you send.** Remove duplicate numbers from a batch and reject empty arrays client-side rather than depending on server-side handling.

**Normalise phone numbers to E.164 before sending.** Consistent formatting makes later removal reliable, since the value you send on `DELETE` must match what was stored.

**Use epoch milliseconds for `expiryTime`.** Seconds-based timestamps resolve to 1970.

**Maintain your own suppression record.** V3 has no read endpoint. Log every request and response so you can prove what was suppressed, when, and why — valuable for compliance audits.

**Authenticate your callback receiver.** Set a shared secret in `callbackUrlHeaders` and verify it on every inbound call.

**Implement retry with exponential backoff** for `429` and `5xx`, and poll bulk status with a widening interval rather than in a tight loop.

**Choose `syncProcessing` deliberately.** Synchronous for small interactive batches; asynchronous for volume and scheduled jobs.

## Related articles

- [API Overview](https://dev.sprinklr.com/api-overview)
- [API Key and Secret Generation](https://dev.sprinklr.com/api-key-and-secret-generation)
- [Authorize](https://dev.sprinklr.com/authorize)
- [APIs by environment](https://dev.sprinklr.com/apis)
- [REST API Errors and Status Codes](https://dev.sprinklr.com/rest-api-error-and-status-codes)
- V2 reference: [Add Contact in Suppression List](https://dev.sprinklr.com/add-contact-in-suppression-list) · [Bulk Add Contacts in Suppression List](https://dev.sprinklr.com/bulk-add-contacts-in-suppression-list) · [Remove Contact from Suppression List](https://dev.sprinklr.com/remove-contact-from-suppression-list)
- [Suppression Lists — Sprinklr Help Center](https://www.sprinklr.com/help/articles/compliance-management-with-suppression-list/suppression-lists/643417d9b7f3625d288e6adb)
- [Suppression List — Voice Use Cases](https://www.sprinklr.com/help/articles/voice-use-cases/suppression-list/67e54be279ba2163f6319eff)
- [Adding Customers to Suppression Lists](https://www.sprinklr.com/help/articles/adding-customers-to-suppression-lists/63f3145a9b334f7283b4caba)
- [Blacklist Audience Lead Records via Data Pipeline](https://www.sprinklr.com/help/articles/blacklist-audience-lead-records-via-data-pipeline/653f941d94ea62237ed0a3ae)