Suppressions

Manage SMS opt-out lists and suppressed phone numbers.

Overview

Suppressions are phone numbers that should not receive SMS messages. This includes users who have opted out, invalid numbers, and manually blocked numbers.

How Suppressions Work

When you send an SMS:

  1. We check if the recipient is on your suppression list
  2. If suppressed, the message is blocked and not sent
  3. API returns error code 1006 (recipient opted out)
const result = await client.sms.send({
  to: '+14155551234',
  templateAlias: 'otp-verification',
  templateModel: { code: '123456' },
});
 
if (result.errorCode === 1006) {
  console.log('Recipient has opted out');
}

Suppression Reasons

ReasonDescription
OPT_OUTUser replied STOP or similar keyword
MANUALManually added by your team
CARRIER_BLOCKCarrier rejected the number
INVALID_NUMBERNumber is not a valid mobile

Automatic Opt-Out Handling

We automatically process opt-out keywords and add numbers to your suppression list:

Opt-Out Keywords

  • STOP
  • UNSUBSCRIBE
  • CANCEL
  • END
  • QUIT

Opt-In Keywords

  • START
  • YES
  • UNSTOP

When a user opts out:

  1. They're added to your suppression list
  2. They receive a confirmation message
  3. Future messages are blocked

When a user opts back in:

  1. They're removed from your suppression list
  2. They receive a confirmation message
  3. They can receive messages again

Managing Suppressions

List Suppressions

TypeScript:

const suppressions = await client.sms.suppressions.list({
  count: 100,
  offset: 0,
});
 
for (const suppression of suppressions) {
  console.log(`${suppression.phoneNumber}: ${suppression.reason}`);
}

Python:

suppressions = client.sms.suppressions.list(count=100, offset=0)
 
for suppression in suppressions.suppressions:
    print(f"{suppression.phone_number}: {suppression.reason}")

Check If Suppressed

TypeScript:

const result = await client.sms.suppressions.check('+14155551234');
if (result.suppressed) {
  console.log('This number is suppressed');
}

Python:

result = client.sms.suppressions.check("+14155551234")
if result.suppressed:
    print("This number is suppressed")

Add Suppression Manually

TypeScript:

await client.sms.suppressions.add({
  phoneNumber: '+14155551234',
  reason: 'MANUAL',
  notes: 'User requested via email',
});

Python:

client.sms.suppressions.add(
    phone_number="+14155551234",
    reason="MANUAL",
    notes="User requested via email",
)

Remove Suppression

TypeScript:

await client.sms.suppressions.remove('+14155551234');

Python:

client.sms.suppressions.remove("+14155551234")

Note: Only remove suppressions if you have valid re-consent from the user.

Dashboard Management

Viewing Suppressions

  1. Go to SMS > Opt-Outs in your dashboard
  2. View all suppressed numbers
  3. Filter by reason or date

Bulk Import

Import suppressions from CSV:

  1. Go to SMS > Opt-Outs
  2. Click Import
  3. Upload CSV with phone numbers
  4. Select suppression reason
  5. Import

CSV format:

phone_number,reason,notes
+14155551234,MANUAL,User requested removal
+14155555678,MANUAL,Do not contact

Bulk Export

Export your suppression list:

  1. Go to SMS > Opt-Outs
  2. Click Export
  3. Download CSV file

API Reference

List Suppressions

GET /sms/suppressions

Query parameters:

  • count - Number to return (default: 100, max: 500)
  • offset - Pagination offset
  • reason - Filter by reason
  • fromDate - Filter by date range
  • toDate - Filter by date range

Response:

{
  "totalCount": 150,
  "suppressions": [
    {
      "phoneNumber": "+14155551234",
      "phoneNumberHash": "abc123...",
      "reason": "OPT_OUT",
      "optedOutAt": "2024-01-01T12:00:00.000Z",
      "notes": null
    }
  ]
}

Add Suppression

POST /sms/suppressions

Body:

{
  "phoneNumber": "+14155551234",
  "reason": "MANUAL",
  "notes": "User requested via support ticket"
}

Remove Suppression

DELETE /sms/suppressions/{phoneNumber}

Check Suppression

GET /sms/suppressions/check/{phoneNumber}

Response:

{
  "suppressed": true,
  "reason": "OPT_OUT",
  "optedOutAt": "2024-01-01T12:00:00.000Z"
}

Webhook Events

Receive notifications when suppressions change:

Opt-Out Event

{
  "event": "sms.opt_out",
  "timestamp": "2024-01-01T12:00:00.000Z",
  "data": {
    "phoneNumber": "+14155551234",
    "keyword": "STOP",
    "optedOutAt": "2024-01-01T12:00:00.000Z"
  }
}

Opt-In Event

{
  "event": "sms.opt_in",
  "timestamp": "2024-01-01T12:00:00.000Z",
  "data": {
    "phoneNumber": "+14155551234",
    "keyword": "START",
    "optedInAt": "2024-01-01T12:00:00.000Z"
  }
}

Privacy Considerations

Phone Number Hashing

Phone numbers in suppressions are stored as SHA-256 hashes:

  • Original number: +14155551234
  • Stored hash: abc123def456...

This protects user privacy while allowing lookup and deduplication.

Data Retention

Suppressions are retained indefinitely to ensure compliance. Contact support if you need to delete suppression data under privacy regulations.

Best Practices

1. Check Before Sending

Always check suppressions before important sends:

async function sendSmsIfAllowed(phone: string, template: string, model: object) {
  const isSuppressed = await client.sms.suppressions.check(phone);
 
  if (isSuppressed) {
    console.log(`Skipping ${phone} - suppressed`);
    return;
  }
 
  return client.sms.send({
    to: phone,
    templateAlias: template,
    templateModel: model,
  });
}

2. Honor Opt-Outs Immediately

Never send to opted-out users, even for transactional messages.

3. Sync with Your Systems

Keep your internal suppression lists in sync:

// Listen for opt-out webhooks
app.post('/webhooks/sms', (req, res) => {
  const { event, data } = req.body;
 
  if (event === 'sms.opt_out') {
    // Update your database
    db.users.update({
      where: { phone: data.phoneNumber },
      data: { smsOptedOut: true },
    });
  }
 
  res.status(200).send('OK');
});

4. Import Existing Lists

If you have existing suppression lists from other providers, import them:

const existingSuppressions = [
  { phone: '+14155551234', reason: 'MANUAL' },
  { phone: '+14155555678', reason: 'OPT_OUT' },
];
 
for (const s of existingSuppressions) {
  await client.sms.suppressions.add({
    phoneNumber: s.phone,
    reason: s.reason,
  });
}

Troubleshooting

Message Blocked Unexpectedly

If a message was blocked but shouldn't have been:

  1. Check if number is on suppression list
  2. Verify the suppression reason
  3. If manually added in error, remove it
  4. If user opted out, get re-consent before removing

User Says They Didn't Opt Out

This can happen if:

  • Someone else replied STOP from a shared phone
  • Carrier-level opt-out
  • Number reassignment

Resolution:

  1. Verify user identity
  2. Have them text START to opt back in
  3. Document the re-consent

Next Steps