Suppressions and test mode
The suppression list
Every project has a list of addresses Postilio no longer sends to. Mail to them would bounce again or be marked as spam again, and that hurts the delivery of all your other mail.
An address gets on the list:
| Reason | How |
|---|---|
hard_bounce | by itself, when a receiving server refuses it for good (a 5xx answer) because the mailbox or domain does not exist or is no longer in use. A block for spam or policy reasons (5.7.x) does not count: that is about reputation, not about the address. |
complaint | by itself, when the recipient marks a message as spam |
manual | when you add it, in the portal or with the API |
Test messages never put an address on the list. Addresses are compared without regard to case.
Sending to a suppressed address
Postilio accepts the message but does not send it. The 202 answer lists the address in suppressed, and its message gets the status suppressed with an event that says why. An address that gets on the list after a message to it was accepted, but before it was sent, is not sent to either.
Manage the list
With the suppressions:manage scope:
curl "https://api.postilio.eu/v1/suppressions?reason=hard_bounce&limit=20" \
-H "Authorization: Bearer $POSTILIO_KEY"
GET /v1/suppressions lists the newest first. Filter with q (part of an address) and reason; for the next page pass the answer's next as before.
Add an address by hand with POST /v1/suppressions; it gets the reason manual. An address that is on the list already answers 409 with {"error": "address_already_suppressed"}.
curl https://api.postilio.eu/v1/suppressions \
-H "Authorization: Bearer $POSTILIO_KEY" \
-H "Content-Type: application/json" \
-d '{ "address": "former.customer@example.org" }'
Remove one with DELETE /v1/suppressions/{id}, and Postilio sends to it again. A complaint is only removed with a reason of 10 to 500 characters, which Postilio keeps; without one the answer is 422 with {"error": "reason_required"}. Remove a complaint only when the recipient asked for your mail again.
curl -X DELETE https://api.postilio.eu/v1/suppressions/$SUPPRESSION_ID \
-H "Authorization: Bearer $POSTILIO_KEY" \
-H "Content-Type: application/json" \
-d '{ "reason": "The customer subscribed again on 5 October." }'
Test mode
A message sent with a test key (pk_test_…) goes through every check a live one does (the sender's domain, the key's restrictions, the suppression list) and is stored, but it is never sent. Instead it gets simulated events at once. The recipient decides the outcome:
| Recipient | Outcome |
|---|---|
delivered@simulator.postilio.eu | delivered |
bounced@simulator.postilio.eu | bounced: 550 5.1.1, the mailbox does not exist |
deferred@simulator.postilio.eu | deferred: 451 4.7.1, greylisted |
complained@simulator.postilio.eu | delivered, then complained |
suppressed@simulator.postilio.eu | suppressed, as if the address were on the suppression list |
| any other address | delivered |
A tag after a plus keeps test runs apart and changes nothing: bounced+run-42@simulator.postilio.eu bounces too.
Test messages:
- have
"test": trueand are found only with a test key; - reach only webhook endpoints in test mode, with the same events a live message would get;
- never count as usage or against the sandbox, and never put an address on the suppression list.
Use a test key in your own tests and in CI. Switching to live is swapping the key.