postkit
Guides

Suppressions

Manage email suppressions and unsubscribes

Suppressions prevent Postkit from sending to email addresses that have bounced, complained, or been manually blocked. Managing suppressions protects your sender reputation and ensures you only send to valid, willing recipients.

Prerequisites: An API key is required for all suppression management. See API Keys.

How suppressions work

When an email hard-bounces or a recipient marks your email as spam, Postkit automatically adds a suppression. Future sends to that address are blocked before they reach the SMTP engine, protecting your domain reputation.

There are three suppression reasons:

  • hard_bounce -- The recipient's mail server permanently rejected delivery. This typically means the address does not exist or the domain is unreachable.
  • complaint -- The recipient marked your email as spam via a feedback loop (e.g., clicking "Report Spam" in their inbox).
  • manual -- You manually suppressed the address via the API. Common use case: a user requests to stop receiving emails from your application.

When you attempt to send an email to a suppressed address, the API returns an error and the email is not queued for delivery.

List suppressions

Retrieve your suppression list with optional filtering by reason. The response uses cursor-based pagination.

# List all suppressions
curl https://api.postkit.eu/v1/suppressions \
  -H "Authorization: Bearer pk_live_abc123..."

# Filter by reason
curl "https://api.postkit.eu/v1/suppressions?reason=hard_bounce" \
  -H "Authorization: Bearer pk_live_abc123..."
// List all suppressions
const response = await fetch('https://api.postkit.eu/v1/suppressions', {
  headers: { 'Authorization': 'Bearer pk_live_abc123...' },
});
const { data, has_more } = await response.json();

// Filter by reason
const bounces = await fetch(
  'https://api.postkit.eu/v1/suppressions?reason=hard_bounce',
  { headers: { 'Authorization': 'Bearer pk_live_abc123...' } }
).then(r => r.json());
import requests

headers = {'Authorization': 'Bearer pk_live_abc123...'}

# List all suppressions
response = requests.get(
    'https://api.postkit.eu/v1/suppressions',
    headers=headers,
)
result = response.json()
# result['data'] is the array, result['has_more'] for pagination

# Filter by reason
bounces = requests.get(
    'https://api.postkit.eu/v1/suppressions',
    headers=headers,
    params={'reason': 'hard_bounce'},
).json()

The response includes a data array of suppression objects and a has_more boolean. Each suppression contains:

FieldTypeDescription
idstringSuppression ID (prefixed with sup_)
emailstringThe suppressed email address
reasonstringOne of hard_bounce, complaint, or manual
detailstring or nullBounce message or complaint feedback (if available)
created_atstringWhen the suppression was created (ISO 8601)

Check if an address is suppressed

Query the suppression list for a specific email address to check whether it is currently suppressed:

curl "https://api.postkit.eu/v1/suppressions?email=user@example.com" \
  -H "Authorization: Bearer pk_live_abc123..."
const response = await fetch(
  'https://api.postkit.eu/v1/suppressions?email=user@example.com',
  { headers: { 'Authorization': 'Bearer pk_live_abc123...' } }
);
const { data } = await response.json();

if (data.length > 0) {
  console.log(`Suppressed: ${data[0].reason} (${data[0].created_at})`);
} else {
  console.log('Not suppressed');
}
response = requests.get(
    'https://api.postkit.eu/v1/suppressions',
    headers=headers,
    params={'email': 'user@example.com'},
)
data = response.json()['data']

if data:
    print(f"Suppressed: {data[0]['reason']} ({data[0]['created_at']})")
else:
    print('Not suppressed')

Add a manual suppression

Add an email address to the suppression list manually. This is useful when a user requests to stop receiving emails from your application.

curl -X POST https://api.postkit.eu/v1/suppressions \
  -H "Authorization: Bearer pk_live_abc123..." \
  -H "Content-Type: application/json" \
  -d '{
    "email": "user@example.com",
    "reason": "manual"
  }'
const response = await fetch('https://api.postkit.eu/v1/suppressions', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer pk_live_abc123...',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    email: 'user@example.com',
    reason: 'manual',
  }),
});
const suppression = await response.json();
// suppression.id = "sup_abc123..."
response = requests.post(
    'https://api.postkit.eu/v1/suppressions',
    headers={'Authorization': 'Bearer pk_live_abc123...'},
    json={
        'email': 'user@example.com',
        'reason': 'manual',
    },
)
suppression = response.json()
# suppression['id'] = "sup_abc123..."

Only manual is accepted as the reason when creating suppressions via the API. The hard_bounce and complaint reasons are set automatically by Postkit when bounces or complaints are detected.

Remove a suppression

Delete a suppression to allow future sends to that address:

curl -X DELETE https://api.postkit.eu/v1/suppressions/sup_abc123... \
  -H "Authorization: Bearer pk_live_abc123..."
const response = await fetch(
  'https://api.postkit.eu/v1/suppressions/sup_abc123...',
  {
    method: 'DELETE',
    headers: { 'Authorization': 'Bearer pk_live_abc123...' },
  }
);
const result = await response.json();
// result = { id: "sup_abc123...", deleted: true }
response = requests.delete(
    'https://api.postkit.eu/v1/suppressions/sup_abc123...',
    headers={'Authorization': 'Bearer pk_live_abc123...'},
)
result = response.json()
# result = {'id': 'sup_abc123...', 'deleted': True}

Removing a hard_bounce suppression and resending to an invalid address will result in another bounce, which harms your domain reputation. Only remove suppressions if you have confirmed the address is now valid (e.g., the recipient fixed their mailbox).

Automatic suppressions from webhooks

When Postkit detects a hard bounce or spam complaint, it automatically creates a suppression and sends a webhook event:

  • email.bounced with bounce_type: "hard" -- Postkit has created a hard_bounce suppression
  • email.complained -- Postkit has created a complaint suppression

Your application does not need to manually create these suppressions -- they are handled automatically. You can use these webhook events to update your own records (e.g., flag a user's email as invalid in your database).

See the Webhooks guide for setting up webhook endpoints and verifying signatures.

What's next?

  • Webhooks -- receive bounce and complaint events in real time
  • Error Handling -- understand API error responses