Documentation menu

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:

ResultEventWhat 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.

Bounce in the event history
{
  "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, cc and bcc alike. Addresses are compared in lower case.
  • A suppressed recipient is left out when the email is sent, and the email gets an email.suppressed event 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_event is suppressed, 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.
email.suppressed webhook
{
  "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:

EndpointDoes
GET /suppressionsLists suppressed addresses, newest first.
POST /suppressionsAdds 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.
cURL
# 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"
Response of GET
{
  "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"
}
Node.js
await tsmart.suppressions.create({ email: "jane@example.com" });
const { data } = await tsmart.suppressions.get("jane@example.com");
await tsmart.suppressions.remove("jane@example.com");
Python
client.suppressions.create({"email": "jane@example.com"})
entry = client.suppressions.get("jane@example.com")
client.suppressions.remove("jane@example.com")
CLI
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.complained event 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 complaint and 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.

email.complained webhook
{
  "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>"
  }
}
Complaint in the event history
{
  "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.