Notix
Learn

A bounce is an answer. Handle it once, automatically.

A bounce is the receiving mail server telling you it will not deliver a message. Some of those answers are final and some are not, and the whole of bounce handling is telling the two apart, then never sending to a finally-rejected address again. Notix does the second part for you and sends your code the first part as an email.bounced webhook.

Hard bounces and soft bounces.

When a sending server hands a message to a receiving server, the receiver answers with a three-digit SMTP reply. A reply starting with 2 accepts the message. A reply starting with 5 is a permanent refusal: the address does not exist, the domain has no mail server, the receiver will not take mail from this sender. That is a hard bounce. A reply starting with 4 is a temporary refusal: the mailbox is full, the message is too large, the server is busy or is deliberately delaying unknown senders for a few minutes. That is a soft bounce. The sending server keeps retrying a 4xx for a while, usually several hours, before it gives up and reports a bounce.

The distinction matters because the right response differs. A hard bounce means the address is wrong and sending again only damages your reputation with the receiver. A soft bounce means nothing is wrong with the address, and the next message may well get through.

Bounce kinds, the SMTP replies behind them, common causes and what Notix does
KindSMTP replyCommon reasonsWhat Notix does
Hard bounce5xx permanent failure, for example 550 5.1.1The address does not exist, the domain has no mail server, the receiver rejected the sender outright.The address goes on the suppression list with reason HARD_BOUNCE. Later sends to it are not attempted.
Soft bounce4xx transient failure, for example 452 4.2.2Mailbox full, message too large, the receiving server is busy or greylisting, a temporary DNS problem.Reported as a bounce of type Transient. The address is not suppressed; the next send is attempted normally.
UndeterminedA reply the receiver did not classifyA non-standard rejection that could not be read as permanent or transient.Reported with type Undetermined. Not suppressed; treat a repeat as a hard bounce in your own logic.

What happens to the address automatically.

When a bounce comes back as permanent, Notix adds each recipient that actually bounced to your team’s suppression list with the reason HARD_BOUNCE and a pointer to the email that caused it. Only the recipients named in the bounce are added: if a message went to three people and one address was dead, the other two are untouched. Transient and undetermined bounces are reported but never suppress anything.

From then on a send to that address is refused before it reaches the queue. The API still accepts the request and returns an email id, but the message is recorded with the status SUPPRESSED, nothing goes out, and the send does not count against your allowance. Where a message has several recipients and only some are suppressed, those are dropped and the rest are delivered. The same list is checked for transactional mail and for campaigns and journeys, so an address that bounced on a receipt is not tried again by a newsletter next week. The suppression list page covers the other two reasons an address lands there.

The webhook

What email.bounced delivers.

Every bounce, permanent or not, is posted to each endpoint subscribed to the event. The envelope is the same for every Notix event; the bounce object is what this one adds.

An email.bounced call, permanent bounce
{
  "id": "call_01j9x2n4",
  "type": "email.bounced",
  "version": "2026-01-18",
  "createdAt": "2026-09-13T10:30:12.000Z",
  "teamId": 123,
  "attempt": 1,
  "data": {
    "id": "email_01j9x2mz",
    "status": "BOUNCED",
    "from": "receipts@acme.com",
    "to": ["someone@example.com"],
    "subject": "Your receipt for order 4821",
    "occurredAt": "2026-09-13T10:30:11Z",
    "campaignId": null,
    "contactId": null,
    "domainId": 42,
    "metadata": { "orderId": "4821" },
    "bounce": {
      "type": "Permanent",
      "subType": "NoEmail",
      "message": "550 5.1.1 The email account that you tried to reach does not exist."
    }
  }
}

data.id is the email you sent, so you can join it to whatever you stored when you called the API; metadata is returned exactly as you passed it. bounce.type is Permanent, Transient or Undetermined. bounce.subType narrows it: NoEmail for an address that does not exist, MailboxFull, MessageTooLarge, ContentRejected, AttachmentRejected, Suppressed or OnAccountSuppressionList when the underlying transport refused the address from its own history, and General otherwise. bounce.message carries the receiver’s reply text when there was one.

Each call is signed. The X-Notix-Signature header is an HMAC-SHA256 of the X-Notix-Timestamp value, a dot and the raw body, keyed with the endpoint’s secret; X-Notix-Event names the event and X-Notix-Retry is set on a retry. If your endpoint does not answer 2xx the call is retried with exponential backoff up to six attempts. The webhooks guide has the verification code for each SDK and the manual HMAC check.

Mark the address invalid on a permanent bounce
// app/api/notix/route.ts
import { Notix } from "notix-js";
import { markEmailInvalid } from "@/lib/users";

const webhooks = new Notix(process.env.NOTIX_API_KEY!).webhooks(process.env.NOTIX_WEBHOOK_SECRET!);

export async function POST(request: Request) {
  const rawBody = await request.text();
  const event = webhooks.constructEvent(rawBody, { headers: request.headers }); // throws on a bad signature

  if (event.type === "email.bounced" && event.data.bounce.type === "Permanent") {
    for (const address of event.data.to) {
      await markEmailInvalid(address, event.data.bounce.subType);
    }
  }
  return new Response("ok");
}

What to do in your product.

Notix stops the sending. Your product still has a user whose address does not work, and only your product can fix that. On a permanent bounce, mark the address invalid on the account and show it the next time the user signs in: a banner asking for a working address, with the old one prefilled so a typo is easy to spot. Do not retry the send; the suppression list will refuse it, and a receipt that was not delivered is better replaced by an in-app copy than by a second bounce. If the address was for a one-time code or a password reset, the sign-in flow should offer another channel rather than another email.

On a transient bounce, do nothing visible. The receiving server has already spent hours retrying before reporting it, and most of these resolve on the next send. Log it, and if the same address is transient three times in a row treat it as invalid in your own data. Undetermined bounces are rare; handle them like transient ones and watch for repeats.

Bounce rate and reputation.

Receivers count how much of your mail they refuse. A domain that keeps sending to dead addresses looks like a domain that bought or scraped its list, and the receiver’s response is to slow down or spam-folder the mail it would otherwise have accepted. Automatic suppression is the single largest lever here, because it turns every bounce into exactly one bounce. The rest is not sending to addresses nobody typed in: verify a signup address with a code before it goes on a list, and treat an imported list as unverified until it has been sent to once with the bounces removed. How receivers weigh bounces alongside complaints and authentication is on the deliverability page.

FAQ

Questions, answered.

What is the difference between a hard bounce and a soft bounce?
A hard bounce is a permanent failure: the receiving server answered with a 5xx reply saying the address or domain does not exist, or that it will not accept mail from you. A soft bounce is a temporary failure, a 4xx reply: the mailbox is full, the message is too large, the server is busy. A hard bounce means stop sending to that address; a soft bounce means the address is probably fine and the next send may get through.
How do I handle email bounces automatically?
With Notix you do not have to do anything for the sending side: a permanent bounce puts the address on your team's suppression list, and any later send to it is recorded as SUPPRESSED instead of being attempted. For your own product, subscribe to the email.bounced webhook, check that bounce.type is Permanent, and mark the user's address invalid so your UI can ask for a new one. Soft bounces need no action.
What happens if I send to an address that already hard-bounced?
The API accepts the request and returns an email id, but the message is recorded with the status SUPPRESSED and nothing leaves the queue. The send does not count against your plan's allowance. When some recipients are suppressed and others are not, the suppressed ones are dropped and the rest are delivered. The dashboard shows the SUPPRESSED status and the suppression list shows the address with the reason and the email that caused it.
Does a soft bounce ever become a hard bounce?
Not automatically. Notix reports each transient bounce as it happens and never suppresses on one. Mailbox providers usually keep retrying a transient failure for a while before giving up, and if they give up the final reply is a permanent bounce, which does suppress. If your own data shows the same address soft-bouncing for weeks, treating it as invalid in your product is reasonable; the suppression list also accepts addresses you add by hand.
Can I remove an address from the suppression list?
Yes, from the suppression page in the dashboard, which is where the list is managed today; there is no public suppression endpoint in the API yet. Do it when you know the address is good again, for example the user corrected a typo that pointed at a real but wrong mailbox, or a domain's mail server was down for a day. Removing an address that bounced because it does not exist only earns you a second bounce and the reputation cost that comes with it.
What bounce rate is acceptable?
Mailbox providers do not publish a single number for bounces the way Google does for complaints, but a hard bounce rate above about 2 percent on a send is a sign that the list is stale or was never verified, and sustained rates like that lower the reputation of your domain. Transactional mail to addresses people typed themselves should bounce well under 1 percent. Suppression keeps a bounced address from bouncing twice, which is most of what keeps the rate down.

Bounces handled before you write a line.

Suppression is on for every team, and the bounce webhook is a subscription away.