> ## Documentation Index
> Fetch the complete documentation index at: https://docs.drin.run/llms.txt
> Use this file to discover all available pages before exploring further.

# Suppressions

> The per-project suppression list: automatic suppression on hard bounce and complaint, manual add/remove, and the suppressed 409.

A suppression list protects your sending reputation. Any address on it is silently skipped at send time — so a hard bounce or spam complaint can never be re-sent to by accident.

Each **project** keeps its own suppression list. When you send, Drin checks every recipient (To, Cc, and Bcc) against it before the message is queued. Suppressed addresses are quietly dropped; the rest are delivered normally.

## How addresses get suppressed

There are two sources:

<CardGroup cols={2}>
  <Card title="Automatic" icon="shield">
    A **hard bounce** or a **spam complaint** adds the recipient to the list automatically, with `reason` `hard_bounce` or `complaint`. This is the important one — it's how Drin keeps you from emailing addresses that hurt your reputation.
  </Card>

  <Card title="Manual" icon="key">
    You can add an address yourself — an unsubscribe from your own UI, a support request, a known-bad address — with `reason` `manual`.
  </Card>
</CardGroup>

<Info>
  **Reasons.** The `reason` field is one of `hard_bounce`, `complaint`, `unsubscribe`, or `manual`. Automatic entries set it for you; manual additions accept only `manual`.
</Info>

## List suppressed addresses

**`GET /v1/suppressions`**

A cursor-paged list of suppressed addresses, newest first. Each entry is `{ email, reason, createdAt }`.

<CodeGroup>
  ```bash cURL theme={null}
  curl https://api.drin.run/v1/suppressions \
    -H "Authorization: Bearer $DRIN_API_KEY"
  ```

  ```typescript Node.js theme={null}
  for await (const s of drin.suppressions.paginate()) {
    console.log(s.email, s.reason, s.createdAt);
  }
  ```
</CodeGroup>

```json Response theme={null}
{
  "data": [
    { "email": "bounced@example.com", "reason": "hard_bounce", "createdAt": "2026-06-01T10:04:00Z" },
    { "email": "angry@example.com",   "reason": "complaint",   "createdAt": "2026-05-30T18:22:00Z" }
  ],
  "nextCursor": null
}
```

## Add an address

**`POST /v1/suppressions`**

Add an address by hand. Only `email` is required; `reason` defaults to `manual`. Adding the same address twice is idempotent — it stays on the list once.

<CodeGroup>
  ```bash cURL theme={null}
  curl https://api.drin.run/v1/suppressions \
    -H "Authorization: Bearer $DRIN_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{ "email": "do-not-email@example.com", "reason": "manual" }'
  ```

  ```typescript Node.js theme={null}
  await drin.suppressions.create({
    email: "do-not-email@example.com",
    reason: "manual",
  });
  ```
</CodeGroup>

## Remove an address

**`DELETE /v1/suppressions`**

Remove an address with the `email` query parameter (URL-encode the `@`). After removal you can send to it again.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X DELETE "https://api.drin.run/v1/suppressions?email=do-not-email%40example.com" \
    -H "Authorization: Bearer $DRIN_API_KEY"
  ```

  ```typescript Node.js theme={null}
  await drin.suppressions.delete("do-not-email@example.com");
  ```
</CodeGroup>

<Warning>
  **Removing a bounce won't fix the address.** Lifting an automatic `hard_bounce` suppression only tells Drin to *try* again. If the mailbox still doesn't exist it will bounce — and re-suppress — immediately. Repeated sends to dead addresses are exactly what damages deliverability. Only remove an address you have a concrete reason to believe is now valid.
</Warning>

## The `suppressed` response

Suppression is applied per recipient. If *some* recipients are suppressed, they're dropped and the send proceeds to the rest. If **every** recipient is suppressed there's nothing left to send, so the request fails with `409` and `type: "suppressed"` — and no message is created:

```json 409 suppressed theme={null}
{
  "error": {
    "type": "suppressed",
    "message": "Every recipient is on this project's suppression list; nothing was sent."
  }
}
```

In the SDK this surfaces as a typed error you can catch:

```typescript theme={null}
import { DrinSuppressedError } from "@drin00/sdk";

try {
  await drin.emails.send({ /* … */ });
} catch (err) {
  if (err instanceof DrinSuppressedError) {
    // 409 — all recipients suppressed, no message was created
  } else {
    throw err;
  }
}
```

<Note>
  **Not the same as a partial drop.** A `suppressed` 409 means *nothing* went out. When only some recipients are suppressed you get a normal `202` for the survivors — there's no error and no signal in the response that an address was skipped. Check the list if you need to know which addresses are blocked.
</Note>

## Related

<CardGroup cols={2}>
  <Card title="Contacts" icon="inbox" href="/contacts">
    The address book and its own subscribe/unsubscribe state, separate from suppressions.
  </Card>

  <Card title="Deliverability" icon="chart-line" href="/deliverability">
    Why bounces and complaints matter, and how to keep both low.
  </Card>

  <Card title="Errors" icon="bolt" href="/errors">
    The full error envelope and every `type`, including `suppressed`.
  </Card>

  <Card title="Suppressions API" icon="code" href="/api-reference/suppressions/list">
    Parameters and response schemas for each endpoint.
  </Card>
</CardGroup>
