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 · Bulk Add Contacts in Suppression List · Remove Contact from Suppression List
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 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.
| 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.
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
suppressionListIdfrom the Sprinklr UI — see 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.
https://api3.sprinklr.com/{env}/api/v3/suppressionList/contactReplace {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 page or ask your Sprinklr account team. Calling the wrong environment authenticates against a different tenant and the request will fail.
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 | 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 and Authorize.
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.
POST /api/v3/suppressionList/contactAdds 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.
POST /api/v3/suppressionList/contact/bulkAdds 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 |
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.
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.
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.
contactValueis 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=+14155550142transmits" 14155550142"— a space followed by digits — which will not match the stored contact.Percent-encode the plus sign:
?contactValue=%2B14155550142Use your HTTP library's URL-encoding function rather than building the query string by concatenation. This is the most common error on this API.
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.
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 |
Use callbackUrlHeaders to authenticate the inbound call, so your receiver can verify the request came from Sprinklr:
"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.
Examples use placeholder credentials and the
prod2environment. Identifiers and phone numbers are illustrative. Substitute your own values.
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.
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.
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.
curl -X GET \
'https://api3.sprinklr.com/prod2/api/v3/suppressionList/bulk/status?processId={{processId}}' \
-H 'Authorization: Bearer {{accessToken}}' \
-H 'Key: {{apiKey}}'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:
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:
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}}",
},
}
);Bulk add, bulk status, and remove return the standard V3 envelope:
{
"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.
| 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.
| 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 |
| 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} |
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.
V3 is an interface redesign. Adding and removing a contact produce the same outcome as the equivalent V2 calls.
- Change the remove call from
POSTtoDELETE. - Move
suppressionListIdandcontactValuefrom 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
syncProcessingper call site —truefor small interactive batches,falsefor scheduled synchronisations. - For asynchronous bulk, implement polling or a callback receiver. Do not treat
200as completion. - If you use a callback, set
callbackUrlHeaderswith a shared secret so you can authenticate the inbound call. - Confirm the accepted
expiryIntervalTypevalues for your tenant. - Update base URLs to
https://api3.sprinklr.com/{env}/api/v3/.... - Add the
Keyheader alongsideAuthorizationon every request. - Test the
403path — permissions are checked separately from authentication. - Confirm
expiryTimevalues are epoch milliseconds. - Keep your own record of suppressions, since V3 has no read endpoint to reconcile against.
There is no API to list suppression lists. Read the ID from the Sprinklr UI:
- Open Voice Care within the Sprinklr Service module.
- In the Voice Settings menu on the far left, select Suppression List.
- Click the three dots beside the suppression list you want to use.
- Select View from the drop-down menu.
- 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.
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.
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.
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.
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.
- API Overview
- API Key and Secret Generation
- Authorize
- APIs by environment
- REST API Errors and Status Codes
- V2 reference: Add Contact in Suppression List · Bulk Add Contacts in Suppression List · Remove Contact from Suppression List
- Suppression Lists — Sprinklr Help Center
- Suppression List — Voice Use Cases
- Adding Customers to Suppression Lists
- Blacklist Audience Lead Records via Data Pipeline