Guides
Bounces and suppressions
Mail to an address that does not exist bounces, and some recipients mark mail as spam. Sending to them again hurts your domain's reputation, so tsmartSend keeps a suppression list per company and skips the addresses on it.
How bounces and delays are reported
After our mail server accepts an email (email.sent), it delivers it to each recipient's mail server. The
result for every recipient becomes an event, usually within a minute:
| Result | Event | What happens next |
|---|---|---|
| Accepted by the receiving server | email.delivered | Nothing; the email is delivered. |
Temporary failure (a 4.x.x status, for example a full mailbox or greylisting) | email.delivery_delayed | Our server keeps retrying. A later attempt produces email.delivered or email.bounced. |
Permanent failure (a 5.x.x status, for example an unknown user) | email.bounced | The address is added to the suppression list. |
| Delivery given up after retrying for too long | email.bounced | The address is not suppressed, because the failure was temporary. |
The webhook payload of email.bounced and email.delivery_delayed contains a
bounce object: type is Permanent or Temporary,
subType is General, and message is the receiving server's reply. The email's
event history (GET /emails/{id}/events) has the same events with the status code in
dsn and the reply in message.
{
"object": "email_event",
"id": "18352",
"type": "email.bounced",
"created_at": "2026-10-12 09:41:08.999000+00",
"recipient": "no-such-user@example.com",
"dsn": "5.1.1",
"message": "550 5.1.1 <no-such-user@example.com>: Recipient address rejected: User unknown"
} Each recipient of an email gets its own result, so one email can be delivered to some recipients and bounce for others. Results are always reported for the addresses you sent to. If one of them is a group or forwarding address hosted on tsmart.ai, results are reported for that address without the server reply, its members or forwarding targets are never shown, and their bounces do not suppress the address.
Automatic suppression
A permanent bounce (status 5.x.x) of an address you sent to directly adds that address to your company's
suppression list with the reason bounce and a link to the email that bounced
(email_id). Temporary failures never add an address. A spam complaint adds the recipient who complained
with the reason complaint (see Spam complaints).
What suppression does
-
The list belongs to the company: it applies to every API key and every sending domain, and to
to,ccandbccalike. Addresses are compared in lower case. -
A suppressed recipient is left out when the email is sent, and the email gets an
email.suppressedevent for it. The request itself succeeds, so check the events or webhooks. -
If every recipient of an email is suppressed, nothing is sent, the email's
last_eventissuppressed, and it does not count toward your monthly allowance. - For a scheduled email the list is checked when it goes out, not when it is scheduled.
{
"type": "email.suppressed",
"created_at": "2026-10-12T11:02:31.004Z",
"data": {
"created_at": "2026-10-12T11:02:30.977Z",
"email_id": "9c1d2e3f-4a5b-4c6d-8e7f-0a1b2c3d4e5f",
"from": "Acme <orders@acme.com>",
"to": ["no-such-user@example.com"],
"subject": "Order 1043 confirmed",
"tags": {},
"suppressed": {
"message": "The recipient is on the suppression list",
"type": "bounce"
}
}
} Manage the list
In the console open Apps → Core services → tsmartSend → Suppressions to see every address with its reason and the email that caused it, add an address (Suppress an address) or remove one. With a full-access key:
| Endpoint | Does |
|---|---|
GET /suppressions | Lists suppressed addresses, newest first. |
POST /suppressions | Adds an address ({"email": "…"}) with the reason manual and returns its id. Adding one that is already listed returns the existing entry's id. |
GET /suppressions/{id or email} | One entry, looked up by its id or by the address. |
DELETE /suppressions/{id or email} | Removes the address; the next email to it is sent again. |
# Stop sending to an address, for example after an unsubscribe request
curl -X POST https://send.tsmart.ai/suppressions \
-H "Authorization: Bearer $TSMART_API_KEY" \
-H "Content-Type: application/json" \
-d '{"email": "jane@example.com"}'
# Look it up or remove it by address (URL-encode the @)
curl https://send.tsmart.ai/suppressions/jane%40example.com \
-H "Authorization: Bearer $TSMART_API_KEY"
curl -X DELETE https://send.tsmart.ai/suppressions/jane%40example.com \
-H "Authorization: Bearer $TSMART_API_KEY" {
"object": "suppression",
"id": "6f1e2d3c-4b5a-4968-8776-655443322110",
"email": "jane@example.com",
"reason": "manual",
"email_id": null,
"created_at": "2026-10-12 11:15:42.081377+00"
} await tsmart.suppressions.create({ email: "jane@example.com" });
const { data } = await tsmart.suppressions.get("jane@example.com");
await tsmart.suppressions.remove("jane@example.com"); client.suppressions.create({"email": "jane@example.com"})
entry = client.suppressions.get("jane@example.com")
client.suppressions.remove("jane@example.com") tsmart suppressions add jane@example.com
tsmart suppressions list
tsmart suppressions remove jane@example.com reason is bounce (added automatically after a permanent bounce), complaint
(added automatically after a spam complaint) or manual (added by you). email_id names the
email that bounced or was reported as spam, or is null for manual entries.
Spam complaints
When a recipient marks your email as spam (or as phishing), some mailbox providers tell the sending platform through a feedback loop: they send a complaint report about that message. tsmartSend receives these reports for the mail it sends, matches each one to your email by the Message-ID tsmartSend gave it (a report without it is not used), and then:
-
records an
email.complainedevent on the email, which appears in its event history and is sent to webhooks subscribed to it; -
adds the recipient, when it is known, to your company's suppression list with the reason
complaintand the email's id, so they get no further mail from any of your keys or domains.
Reports are read about once an hour, so the event usually arrives within an hour or two of the complaint. A report that was received twice is counted once, and each recipient can complain about an email only once.
{
"type": "email.complained",
"created_at": "2026-10-12T10:15:00.000Z",
"data": {
"created_at": "2026-10-12T09:41:05.120Z",
"email_id": "4ef9a417-02e9-4d39-ad75-9611e0fcc33c",
"from": "Acme <orders@acme.com>",
"to": ["jane@example.com"],
"subject": "Order 1042 confirmed",
"tags": { "category": "order_confirmation" },
"message_id": "<4ef9a417-02e9-4d39-ad75-9611e0fcc33c@acme.com>"
}
} {
"object": "email_event",
"id": "18377",
"type": "email.complained",
"created_at": "2026-10-12 10:15:00+00",
"recipient": "jane@example.com",
"message": "Marked as spam by the recipient"
} When the provider hides the recipient
Many providers remove the complaining recipient's address from the report. If the email had a single recipient
(counting to, cc and bcc), the complaint is still attributed to that address.
If it had several, the event is recorded without a recipient: recipient is null in the
event history, data.to is an empty list in the webhook, and nobody is suppressed, because there is no
way to tell who complained. Such a complaint is recorded at most once per email, however many reports arrive. Send
each recipient their own email (for example with the batch endpoint) if you want every complaint to name its
recipient.
Which complaints are reported
Complaint reports come only from mailbox providers that run a feedback loop and with which the platform is
registered. Some providers send them, others (including some of the largest) only publish aggregate spam rates and
never say which message was reported. So a missing email.complained event does not mean nobody
complained. Complaints about mail sent from a mailbox (webmail or a mail app) rather than through the API do not
appear in tsmartSend.
Keep complaints well below 0.1% of the mail you send; above about 0.3% providers start filtering or refusing a
domain's mail. Send only to people who asked for your mail, honour unsubscribe requests (for example with a
List-Unsubscribe header, see Custom headers) and add those
addresses with POST /suppressions. The complained metric of
GET /emails/metrics counts the complaints received.