PushMail.dev

Suppressions

Manage global and per-org suppression lists for bounces, complaints, and manual suppressions. Suppressed emails are automatically blocked from sending.

Overview

PushMail automatically maintains a suppression list for each organization. When an email hard-bounces, receives a spam complaint, or is manually suppressed, it's added to the suppression list. Any future send attempts to suppressed addresses are blocked before they reach your email provider, protecting your sender reputation.

Suppressions are checked:

  • At the API level when you call POST /v1/send
  • In the queue consumer before dispatching campaign and sequence emails
  • Using a fast KV cache layer for minimal latency

The suppression object

{
  "id": 1,
  "orgId": 5,
  "siteId": null,
  "email": "bounced@example.com",
  "reason": "hard_bounce",
  "source": "automatic",
  "originalSendId": 42,
  "createdAt": "2025-03-01T10:00:00.000Z",
  "updatedAt": "2025-03-01T10:00:00.000Z"
}

Reason values

ReasonDescription
hard_bouncePermanent delivery failure (invalid address, domain doesn't exist)
soft_bounceTemporary delivery failure that was promoted to suppression
spam_complaintRecipient marked the email as spam
manualManually added via API or dashboard
list_unsubscribeRecipient used the List-Unsubscribe header

Source values

SourceDescription
automaticAdded automatically from webhook events (bounces, complaints)
manualAdded manually via API or dashboard
importBulk imported from CSV

List suppressions

GET /v1/suppressions

ParameterTypeDescription
qstringSearch by email (partial match)
reasonstringFilter by reason: hard_bounce, soft_bounce, spam_complaint, manual, list_unsubscribe
pageintegerPage number (default: 1)
limitintegerResults per page, 1-100 (default: 50)
Request
curl "https://pushmail.dev/api/v1/suppressions?reason=hard_bounce&page=1&limit=20" \
  -H "Authorization: Bearer pm_live_YOUR_KEY"
Response
{
  "data": {
    "suppressions": [
      {
        "id": 1,
        "email": "bounced@example.com",
        "reason": "hard_bounce",
        "source": "automatic",
        "createdAt": "2025-03-01T10:00:00.000Z"
      }
    ],
    "pagination": {
      "page": 1,
      "limit": 20,
      "total": 1,
      "totalPages": 1
    }
  }
}

Add a suppression

POST /v1/suppressions

ParameterTypeDescription
emailrequiredstringEmail address to suppress
reasonstringSuppression reason. Defaults to "manual"
siteIdintegerScope to a specific site (omit for org-wide)
Request
curl -X POST https://pushmail.dev/api/v1/suppressions \
  -H "Authorization: Bearer pm_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "user@example.com",
    "reason": "manual"
  }'

Returns 201 on success. If the email is already suppressed, the existing record is updated.

Check suppression status

GET /v1/suppressions/check/:email

Request
curl "https://pushmail.dev/api/v1/suppressions/check/user@example.com" \
  -H "Authorization: Bearer pm_live_YOUR_KEY"
Response (suppressed)
{
  "data": {
    "email": "user@example.com",
    "suppressed": true,
    "reason": "hard_bounce"
  }
}
Response (not suppressed)
{
  "data": {
    "email": "user@example.com",
    "suppressed": false,
    "reason": null
  }
}

Remove a suppression

DELETE /v1/suppressions/:email

Removes the email from the suppression list, allowing future sends to this address.

Request
curl -X DELETE "https://pushmail.dev/api/v1/suppressions/user%40example.com" \
  -H "Authorization: Bearer pm_live_YOUR_KEY"

Returns 200 on success, 404 if the email was not suppressed.

Note: The email address in the URL must be URL-encoded (@ becomes %40).

Bulk import

POST /v1/suppressions/import

Import multiple suppressions at once. Accepts either JSON or CSV format.

JSON format

Request (JSON)
curl -X POST https://pushmail.dev/api/v1/suppressions/import \
  -H "Authorization: Bearer pm_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "suppressions": [
      { "email": "bad1@example.com", "reason": "hard_bounce" },
      { "email": "bad2@example.com", "reason": "spam_complaint" },
      { "email": "bad3@example.com" }
    ]
  }'

CSV format

Request (CSV)
curl -X POST https://pushmail.dev/api/v1/suppressions/import \
  -H "Authorization: Bearer pm_live_YOUR_KEY" \
  -H "Content-Type: text/csv" \
  -d 'email,reason
bad1@example.com,hard_bounce
bad2@example.com,spam_complaint
bad3@example.com,manual'
Response
{
  "data": {
    "imported": 3,
    "skipped": 0,
    "total": 3
  }
}

Maximum 10,000 suppressions per import request.

Export

GET /v1/suppressions/export

ParameterTypeDescription
formatstringExport format: "csv" or "json" (default: "json")
Request (CSV)
curl "https://pushmail.dev/api/v1/suppressions/export?format=csv" \
  -H "Authorization: Bearer pm_live_YOUR_KEY" \
  -o suppressions.csv
Request (JSON)
curl "https://pushmail.dev/api/v1/suppressions/export?format=json" \
  -H "Authorization: Bearer pm_live_YOUR_KEY"
Response (JSON)
[
  {
    "id": 1,
    "orgId": 5,
    "email": "bounced@example.com",
    "reason": "hard_bounce",
    "source": "automatic",
    "createdAt": "2025-03-01T10:00:00.000Z"
  }
]

The CSV export returns a file with columns: email, reason, source, created_at.

Automatic suppression

PushMail automatically adds emails to the suppression list when:

  1. Hard bounce -- SendGrid reports a permanent delivery failure
  2. Spam complaint -- A recipient marks your email as spam
  3. List unsubscribe -- A recipient uses the List-Unsubscribe header
  4. Dropped -- SendGrid drops the email due to previous bounces or spam reports

These automatic suppressions protect your sender reputation and deliverability. You can remove automatic suppressions via the API or dashboard, but exercise caution -- re-sending to previously bounced addresses can harm your reputation.

Send pipeline integration

When you send an email (transactional, campaign, or sequence), PushMail checks the suppression list at two points:

  1. API entry point (POST /v1/send): Returns 422 with a clear error message if the recipient is suppressed
  2. Queue consumer: Rejects the email with status rejected if the recipient was suppressed between queueing and processing

Suppressed sends do not consume credits.

Next steps

  • Sending -- Send transactional emails via the API
  • Webhooks -- Configure webhook endpoints for delivery events
  • Contacts -- Manage your contact list

On this page