Suppressions Live
The suppression list is the workspace's do-not-send roster. Addresses on this list will not receive outbound email, regardless of the source. Hard bounces are added automatically; complaints from feedback loops are added automatically; agents can add or remove entries manually.
Reasons
| Reason | Source | Behaviour |
|---|---|---|
hard_bounce | Outbound webhook | Added immediately on permanent bounce. |
soft_bounce_exceeded | Outbound webhook | Added after three transient failures. |
complaint | Outbound webhook (feedback loop) | Added immediately on spam complaint. |
manual | Agent or API | Explicit add (e.g., customer asked to be removed). |
List suppressions
Cursor-paginated. Filters: reason, email (prefix match).
Code samples
curl 'https://app.message.com/api/v1/suppressions' \
-H 'Authorization: Bearer YOUR_WORKSPACE_JWT'const res = await fetch('https://app.message.com/api/v1/suppressions', {
headers: { Authorization: 'Bearer ' + token }
});
const { suppressions } = await res.json();import requests
r = requests.get(
"https://app.message.com/api/v1/suppressions",
headers={"Authorization": f"Bearer {token}"},
)
suppressions = r.json()["suppressions"]require "net/http"
require "json"
uri = URI("https://app.message.com/api/v1/suppressions")
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
req = Net::HTTP::Get.new(uri)
req["Authorization"] = "Bearer #{token}"
http.request(req)
end
suppressions = JSON.parse(res.body)["suppressions"]<?php
$ctx = stream_context_create([
"http" => ["method" => "GET", "header" => "Authorization: Bearer $token"]
]);
$suppressions = json_decode(file_get_contents("https://app.message.com/api/v1/suppressions", false, $ctx), true)["suppressions"];Add to suppressions
Adds an email to the list. If the email is already suppressed with the same reason, returns the existing record (idempotent). Note text is optional and admin-visible only.
| Field | Type | Description |
|---|---|---|
| emailrequired | string | Address to suppress. Normalised to lowercase. |
| reasonoptional | enum | One of the four reasons. Defaults to manual. |
| noteoptional | string | Free-form note. Audit trail only. |
{
"email": "[email protected]",
"reason": "manual",
"note": "Customer asked to be removed via phone"
}{
"id": "uuid",
"email": "[email protected]",
"reason": "hard_bounce",
"note": "550-5.1.1 The email account that you tried to reach does not exist",
"createdAt": "2026-05-13T15:30:00Z"
}Remove from suppressions
Removes an entry. The address is eligible to receive outbound mail again on the next attempt. Use sparingly: a removed entry that immediately re-bounces hurts your sender reputation.
Removing a complaint entry is risky. Spam complaints come from the recipient explicitly marking your message as spam. Re-mailing them often triggers further complaints and an ESP-side block.
Outbound behaviour with suppressions
When you send via /tickets/:id/reply or /conversations/:id/messages and the visitor email is on the suppression list:
- If
sendEmailis the default (truefor tickets,falsefor chat): the message is recorded withdeliveryStatus: "suppressed"and not delivered. - The API returns
200, not an error. We surface the suppression status in the response body. - The dashboard shows a red banner on the conversation explaining why the message did not go out.
Errors
| Code | When |
|---|---|
400 invalid_body | Email format is not valid. |
403 forbidden | Agent role does not allow suppression edits. |
404 not_found | Suppression record not found in this workspace. |
Common pitfalls
- Importing a suppression list from another vendor. Bulk import is admin-only and rate-limited. POST one at a time or contact us for a bulk-load.
- Confusing suppression with deletion. Suppressing an email does not delete the visitor record or their conversation history. It only blocks future outbound mail.
- Manually adding a customer who replied "stop". Add them with
reason: manualand a note. Auto-removal does not happen.