Import suppression lists before migrating email providers
Why bounces, complaints, unsubscribes, and manual blocks should move before your first send through a new email provider.
The risky part of an email provider migration is not usually the send API. You can wrap a new endpoint, test a staging message, and ship a credentials swap in a day.
The risky part is state.
Your old provider knows who hard-bounced, who complained, who unsubscribed, and which recipients your team blocked manually. Your new provider does not know any of that until you import it.
If you skip this step, the first production send through the new provider can contact recipients who were already suppressed in the old system.
What suppression lists protect
A suppression list protects three things at once:
- Recipient expectations.
- Sender reputation.
- Your own support and compliance posture.
Hard bounces tell you an address should not receive more mail. Spam complaints tell you a recipient or mailbox provider considered the message unwanted. Unsubscribes are a direct request to stop a class of mail.
Those are not just dashboard counters. They are operational rules that must survive a provider migration.
Import before the first real send
The safest order is:
- Add and verify the new provider's DNS records.
- Configure webhooks.
- Export suppressions from the old provider.
- Import suppressions into the new provider.
- Send staging and low-risk production traffic.
- Ramp gradually.
Importing after cutover is too late. By then, the new provider may already have sent to addresses that should have been blocked.
What to export
Export suppression data by reason whenever the provider supports it.
| Provider | Export |
|---|---|
| SendGrid | Bounces, spam reports, global unsubscribes, group unsubscribes, blocks or invalids |
| Mailgun | Bounces, complaints, unsubscribes |
| Postmark | Suppression dump per message stream |
| Resend | Suppression list rows for hard bounces and spam complaints |
| Amazon SES | Account-level suppressed destinations |
Do not treat soft bounces as suppressions by default. A soft bounce is a temporary delivery failure. Keep it in your historical analytics, but do not turn it into a permanent block unless your old provider explicitly promoted it to a hard bounce or manual suppression.
Preview first, then commit
Postscale's suppression import flow is deliberately two-step.
In the dashboard, open /dashboard/suppressions, choose a provider under
Import Suppressions, upload the CSV, and click Preview. The preview
shows valid rows, invalid rows, and duplicates before anything is written.
The API import flow also expects CSV text. If the old provider gives you a JSON
array, convert it to CSV first. Do not post hard-bounce or complaint rows one by
one to POST /v1/suppressions; that direct endpoint is only for manual blocks
and scoped unsubscribes.
Through the API, create a preview:
curl -X POST https://api.postscale.io/v1/imports/suppressions/preview \
-H "Authorization: Bearer ps_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"provider": "mailgun",
"source_filename": "mailgun-complaints.csv",
"default_reason": "complaint",
"content": "email,reason\nabuse@example.com,complaint\n"
}'
Then commit the previewed job:
curl -X POST https://api.postscale.io/v1/imports/suppressions/jobs/IMPORT_JOB_ID/commit \
-H "Authorization: Bearer ps_live_your_api_key"
Committed rows are idempotent. Existing suppressions are skipped, not duplicated,
and new rows are tagged with source values such as import:mailgun or
import:sendgrid.
Keep reason data
The worst CSV export is one that only says:
email
user@example.com
That can still be useful, but it forces you to choose a default reason for every row. A better export keeps the provider's reason:
email,reason
user@example.com,hard_bounce
spam@example.com,complaint
old@example.com,unsubscribe
Reason data matters later. Support can tell whether a recipient bounced, complained, or opted out. Deliverability review can separate list hygiene from abuse signals. Compliance review can distinguish a normal unsubscribe from a manual block.
Scoped unsubscribes
Hard bounces and complaints should stay org-wide. If a mailbox permanently rejects mail or reports abuse, do not keep sending from another stream.
Unsubscribes are more nuanced. A recipient may unsubscribe from product updates but still need password resets, invoices, and security notices.
Postscale supports scoped unsubscribes with scope_type and scope_id:
email,reason,scope_type,scope_id
user@example.com,unsubscribe,list,product-updates
Use scoped imports only when the old provider export carries reliable stream, list, template, or domain context. If you are unsure, import unsubscribes org-wide. It is blunt, but safer.
Re-export the delta
Keep the old provider active for a short overlap period. Even after traffic moves, delayed bounces and webhook retries can arrive there.
After the final cutover, export suppressions one more time and import the delta into Postscale. The import is idempotent, so already-imported rows are skipped.
That final pass closes the gap between "we stopped sending through the old provider" and "the old provider has no more useful state."
For the full operational checklist, use the Suppression List Migration Guide. For API details, see the Suppressions API.