---
title: Suppressions
path: concepts/suppressions
status: published
---

# Suppressions

A suppression is a "don't send to this address" marker. ScaiSend maintains three kinds automatically — bounces, spam reports, unsubscribes — and also supports **suppression groups** for per-topic opt-outs. This page describes what each kind means, how they're populated, and what happens when a send hits one.

## The three kinds

| Kind | When added | Effect on future sends |
|------|------------|-----------------------|
| **Bounce** | Recipient MX returned a permanent failure, or an async DSN arrived | Skipped unless `mail_settings.bypass_bounce_management` or `mail_settings.bypass_list_management` is set |
| **Spam report** | A feedback-loop report arrived from an ISP (Gmail, Yahoo, etc.) | Skipped unless `mail_settings.bypass_spam_management` or `mail_settings.bypass_list_management` is set |
| **Unsubscribe** | Recipient clicked the unsubscribe link or used RFC 8058 one-click | Skipped unless `mail_settings.bypass_unsubscribe_management` or `mail_settings.bypass_list_management` is set |

All three lists are tenant-scoped. If address `X` bounces on tenant A, it's suppressed for A only.

## Automatic vs manual

The primary flow is automatic. ScaiSend watches SMTP responses, DSN bounces, and FBL reports, and adds addresses to the right list without any action on your part.

Manual adds exist for edge cases:

- **Migration from another provider.** When you move from SendGrid/Mailgun/Postmark, import their suppression lists so you don't re-send to addresses that bounced there.
- **External reports.** A user called support to opt out. Record it via API so your next send skips them.
- **Pre-emptive blocks.** You have a list of known bad addresses from a list-cleaning service.

```bash
# Manual bounce add
curl -X POST https://scaisend.scailabs.ai/v3/suppression/bounces \
  -H "Authorization: Bearer $SCAISEND_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"email": "invalid@example.com", "reason": "Imported from SendGrid, hard bounce 2026-03-01"}'
```

## Suppression groups (ASM)

A suppression group lets recipients unsubscribe from a specific **kind** of email instead of all email. Typical groups:

- Marketing (promotions, announcements)
- Product Updates (release notes, feature launches)
- Newsletter (weekly digest)
- Transactional (receipts, password resets — almost never want this opt-outable)

Each group has a numeric ID. When you send, specify the group:

```json
{
  "personalizations": [{"to": [{"email": "user@example.com"}]}],
  "from": {"email": "hello@mail.example.com"},
  "subject": "Newsletter #42",
  "content": [{"type": "text/html", "value": "<html>..."}],
  "asm": {
    "group_id": 123,
    "groups_to_display": [123, 456, 789]
  }
}
```

The recipient sees a preference page (not just a blunt "unsubscribe") listing the groups in `groups_to_display`. They can opt out of `Newsletter` while staying subscribed to `Receipts`.

### Creating a group

```bash
curl -X POST https://scaisend.scailabs.ai/v3/asm/groups \
  -H "Authorization: Bearer $SCAISEND_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "Newsletter", "description": "Our weekly digest", "is_default": false}'
```

Response includes the numeric `id` — that's what goes in `asm.group_id` on future sends.

### Group unsubscribe vs global unsubscribe

The unsubscribe link in a grouped send defaults to group-scoped. The recipient stays subscribed to other groups. There's also a "Unsubscribe from all" link that adds them to the **global** unsubscribe list, blocking every future send from this tenant.

Events emitted:

| Event | When |
|-------|------|
| `group_unsubscribe` | Recipient unsubscribed from a specific group |
| `group_resubscribe` | Recipient re-subscribed to a group (via a preference page) |
| `unsubscribe` | Recipient hit "Unsubscribe from all" or the `List-Unsubscribe` header |

## Bypass flags

Some sends must go through regardless of suppression state — password resets, account security alerts, legal notices. Use the bypass flags in `mail_settings`:

| Flag | What it bypasses |
|------|------------------|
| `bypass_list_management` | All of the below combined |
| `bypass_bounce_management` | Bounce list |
| `bypass_spam_management` | Spam-report list |
| `bypass_unsubscribe_management` | Unsubscribe list (global and group) |

```json
{
  "personalizations": [{"to": [{"email": "user@example.com"}]}],
  "from": {"email": "security@mail.example.com"},
  "subject": "Password reset",
  "content": [{"type": "text/plain", "value": "Reset link: ..."}],
  "mail_settings": {
    "bypass_list_management": {"enable": true}
  }
}
```

**Use sparingly.** Bypassing ignores real deliverability signals. A hard-bounced address won't magically accept your password reset because you bypassed the check. The correct use is for *legitimate transactional mail where the recipient must receive it* (regulatory, security), not as a hack around user preferences.

You still can't send to an invalid address. SMTP will reject it; the message will end in `bounced` state.

## Managing suppressions via API

Full CRUD is available. The endpoints track SendGrid's:

### Bounces

```bash
# List
curl "https://scaisend.scailabs.ai/v3/suppression/bounces?limit=50" \
  -H "Authorization: Bearer $SCAISEND_API_KEY"

# Delete one (allow future sends to this address)
curl -X DELETE https://scaisend.scailabs.ai/v3/suppression/bounces/user@example.com \
  -H "Authorization: Bearer $SCAISEND_API_KEY"

# Bulk import from CSV
curl -X POST https://scaisend.scailabs.ai/v3/suppression/bounces/import \
  -H "Authorization: Bearer $SCAISEND_API_KEY" \
  -H "Content-Type: text/csv" \
  --data-binary @old-bounces.csv
```

### Spam reports

```bash
curl https://scaisend.scailabs.ai/v3/suppression/spam_reports \
  -H "Authorization: Bearer $SCAISEND_API_KEY"

curl -X DELETE https://scaisend.scailabs.ai/v3/suppression/spam_reports/user@example.com \
  -H "Authorization: Bearer $SCAISEND_API_KEY"
```

### Global unsubscribes

```bash
curl https://scaisend.scailabs.ai/v3/asm/suppressions/global \
  -H "Authorization: Bearer $SCAISEND_API_KEY"

# Is a specific address globally unsubscribed?
curl https://scaisend.scailabs.ai/v3/asm/suppressions/global/user@example.com \
  -H "Authorization: Bearer $SCAISEND_API_KEY"
```

### Group suppressions

```bash
# List suppressions in a group
curl https://scaisend.scailabs.ai/v3/asm/groups/123/suppressions \
  -H "Authorization: Bearer $SCAISEND_API_KEY"

# Add (manual opt-out)
curl -X POST https://scaisend.scailabs.ai/v3/asm/groups/123/suppressions \
  -H "Authorization: Bearer $SCAISEND_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"recipient_emails": ["opt-out@example.com"]}'

# Remove (resubscribe)
curl -X DELETE https://scaisend.scailabs.ai/v3/asm/groups/123/suppressions/opt-out@example.com \
  -H "Authorization: Bearer $SCAISEND_API_KEY"
```

Full endpoint list in [Suppressions Reference](../reference/suppressions).

## What gets recorded when a send is suppressed

A send that hits a suppression:

1. Does **not** get handed to SMTP — no network I/O, no recipient contact.
2. Is recorded as a message with status `FAILED` (or skipped, depending on version; confirm with `GET /v3/messages/{id}`).
3. Emits a `dropped` event with reason `suppressed`.
4. Increments the `dropped` counter in daily stats.

So you can audit suppression activity via the event stream and stats — you won't be silently dropping mail without any record.

## What's next

- [Tracking](tracking) — how subscription tracking populates unsubscribes.
- [Bounce Handling](bounce-handling) — how DSNs become bounce suppressions.
- [Feedback Loops](feedback-loops) — how ARF reports become spam-report suppressions.
