Docs
guides · suppressions

Suppressions

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.

01 How addresses get suppressed

There are two sources:

AutomaticA 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.
ManualYou can add an address yourself — an unsubscribe from your own UI, a support request, a known-bad address — with reason manual.
ReasonsThe reason field is one of hard_bounce, complaint, unsubscribe, or manual. Automatic entries set it for you; manual additions accept only manual.

02 List suppressed addresses

GET/v1/suppressions

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

curl https://api.drin.run/v1/suppressions \
  -H "Authorization: Bearer $DRIN_API_KEY"
Response
{
  "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
}

03 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.

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" }'

04 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.

curl -X DELETE "https://api.drin.run/v1/suppressions?email=do-not-email%40example.com" \
  -H "Authorization: Bearer $DRIN_API_KEY"
Removing a bounce won't fix the addressLifting 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.

05 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:

409 suppressed
{
  "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
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;
  }
}
Not the same as a partial dropA 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.

06 Related