Reducing email bounces and maintaining account reputation using webhooks

Setup bounce notification webhooks for your account to reduce email bounces

Each time your account receives a bounce or complaint email it is recorded against your sender score. If your account reputation has too many bounces you could be suspended from sending. To avoid this it is important to keep bounces low and take action when bounces occur. See the email event webhook documentation for more information on setting up webhooks.

Getting notified of account bounces

You can see your bounces in the reputation bounce section of the MailSlurp dashboard. You can also receive notification of each bounce by creating an account webhook.

bounce back notification

Javascript code example

Create account webhooks in code or using the dashboard. You can use the @mailslurp/test-webhooks package to create dummy server endpoints if your server is not yet ready. Use the mailslurp-client NPM package

Create a bounce webhook

To create a webhook use the webhookController in the main package.

mailslurp.webhookController.createWebhook(null, {
  url: "https://my-server.com/webhooks/bounce",
  eventName: "BOUNCE"
})

You can then have MailSlurp send a test payload to your account using the sendTestData method.

const sent = await mailslurp.webhookController.sendTestData({webhookId: webhook.id})

Testing webhooks to receive bounce event

You can use the free @mailslurp/test-webhooks to spin up a test server to receive webhooks if your server is not reachable.

import fetchApi from 'isomorphic-fetch';
import {
  CreateWebhookOptionsEventNameEnum,
  MailSlurp,
  WebhookBouncePayload, WebhookBounceRecipientPayload,
} from 'mailslurp-client';
import {
  Configuration as TestWebhookConfiguration,
  CreateRulesetOptionsStrategyEnum,
  DefaultApi as TestWebhookApi,
} from '@mailslurp/test-webhooks';

import { jest } from '@jest/globals';
jest.setTimeout(60000);
const apiKey = process.env.apiKey || 'your-api-key-here';
const mailslurp = new MailSlurp({ apiKey, fetchApi });
const testWebhooks = new TestWebhookApi(
  new TestWebhookConfiguration({ fetchApi })
);

describe('BOUNCE webhooks', () => {
  test('can create BOUNCE webhook and receive successfully using send test data', async () => {
    // create an inbox, webhook, and a test endpoint
    const testEndpoint = await testWebhooks.createEndpoint({});
    const webhook = await mailslurp.webhookController.createAccountWebhook({
      createWebhookOptions: {
        eventName: CreateWebhookOptionsEventNameEnum.BOUNCE,
        url: testEndpoint.url!!,
      },
    });
    // can see that endpoint has not received an event
    const endpointHistory = await testWebhooks.getEndpointHistory({
      endpointId: testEndpoint.id!,
    });
    expect(endpointHistory.items?.length).toEqual(0);

    // can send a test webhook paylaod
    const sent = await mailslurp.webhookController.sendTestData({webhookId: webhook.id})
    expect(sent.response.statusCode).toEqual(200)

    // endpoint receives the payload (note the expected length to wait for)
    const endpointHistory2 = await testWebhooks.getEndpointHistory({
      endpointId: testEndpoint.id!,
      expectedLength: 1,
    });
    expect(endpointHistory2.items?.length).toEqual(1);

    // assert correct payload was sent to endpoint
    const payload: WebhookBouncePayload = JSON.parse(endpointHistory2.items?.[0]?.request?.body!);
    expect(payload.webhookId).toEqual(webhook.id);
    expect(payload.eventName).toEqual('BOUNCE');

    await mailslurp.webhookController.deleteWebhookById({
      webhookId: webhook.id!
    });
  });
  test('can create BOUNCE RECIPIENT webhook and receive successfully using send test data', async () => {
    // create an webhook, and a test endpoint
    const testEndpoint = await testWebhooks.createEndpoint({});
    const webhook = await mailslurp.webhookController.createAccountWebhook({
      createWebhookOptions: {
        eventName: CreateWebhookOptionsEventNameEnum.BOUNCE_RECIPIENT,
        url: testEndpoint.url!!,
      },
    });
    // can see that endpoint has not received an event
    const endpointHistory = await testWebhooks.getEndpointHistory({
      endpointId: testEndpoint.id!,
    });
    expect(endpointHistory.items?.length).toEqual(0);

    // can send a test webhook paylaod
    const sent = await mailslurp.webhookController.sendTestData({webhookId: webhook.id})
    expect(sent.response.statusCode).toEqual(200)

    // endpoint receives the payload (note the expected length to wait for)
    const endpointHistory2 = await testWebhooks.getEndpointHistory({
      endpointId: testEndpoint.id!,
      expectedLength: 1,
    });
    expect(endpointHistory2.items?.length).toEqual(1);

    // assert correct payload was sent to endpoint
    const payload: WebhookBounceRecipientPayload = JSON.parse(endpointHistory2.items?.[0]?.request?.body!);
    expect(payload.webhookId).toEqual(webhook.id);
    expect(payload.eventName).toEqual('BOUNCE_RECIPIENT');

    await mailslurp.webhookController.deleteWebhookById({
      webhookId: webhook.id!
    });
  });

  test('can create NEW_EMAIL webhook and see failed results when endpoint fails to accept payload', async () => {
    // create a test endpoint that always returns a 401 error
    const testEndpoint = await testWebhooks.createEndpoint({});
    await testWebhooks.addEndpointRuleset({
      endpointId: testEndpoint.id!,
      createRulesetOptions: {
        strategy: CreateRulesetOptionsStrategyEnum.SINGULAR,
        responses: [
          {
            statusCode: 401,
          },
        ],
      },
    });

    // create inbox and webhook
    const inbox = await mailslurp.createInbox();
    const webhook = await mailslurp.webhookController.createWebhook({
      inboxId: inbox.id!,
      createWebhookOptions: {
        eventName: CreateWebhookOptionsEventNameEnum.NEW_EMAIL,
        url: testEndpoint.url!!,
      },
    });

    // send email to inbox
    await mailslurp.sendEmail(inbox.id!, {
      to: [inbox.emailAddress!],
      subject: 'email2',
    });

    // wait for endpoint to receive payload
    const endpointHistory = await testWebhooks.getEndpointHistory({
      endpointId: testEndpoint.id!,
      expectedLength: 1,
    });
    expect(endpointHistory.items?.length).toEqual(1);

    // can see webhook results via mailslurp
    const results = await mailslurp.webhookController.getWebhookResults({
      webhookId: webhook.id!,
    });
    expect(results.totalElements).toEqual(1);
    const result = await mailslurp.webhookController.getWebhookResult({
      webhookResultId: results.content?.[0]?.id!,
    });
    expect(result.resultType).toEqual('BAD_RESPONSE');
    expect(result.responseStatus).toEqual(401);

    await mailslurp.webhookController.deleteWebhook({
      inboxId: inbox.id!,
      webhookId: webhook.id!,
    });
  });
});

Actions to take when bounces occur

If you receive a bounce or bounce_recipient webhook notification on your server you should save the recipient email address to a ban list and check the user on the ban list before sending emails again. This will mean you will only bounce once on a recipient.

Additional bounce methods

You can also avoid bounces by verifying that email addresses exist using the email verification methods available in the dashboard and API.

Here is an example of filtering out invalid email addresses during sending using the Java client:


// send an email from inbox1 to inbox2 and wait for confirmation
SendEmailOptions sendOptions = new SendEmailOptions()
        .validateEmailAddresses(SendEmailOptions.ValidateEmailAddressesEnum.VALIDATE_FILTER_REMOVE_INVALID)
        .addToItem(inbox2.getEmailAddress())
        .addToItem(notExistingEmailAddress)
        .subject("Hello inbox 2")
        .body("Send me a reply");
SentEmailDto confirmation = inboxController.sendEmailAndConfirm(inbox1.getId(), sendOptions);
assertEquals(confirmation.getTo(), Collections.singletonList(inbox2.getEmailAddress()));
assertEquals(confirmation.getReplyTo(), inbox1.getEmailAddress());

Validate email address lists

To reduce bounces consider validating email address to filter out non-existing emails:

const mailslurp = new MailSlurp(config);
const res = await mailslurp.emailVerificationController.validateEmailAddressList({
    validateEmailAddressListOptions: {
        emailAddressList: ['contact@mailslurp.dev', 'bad@mailslurp.dev'],
    },
});
expect(res.resultMapEmailAddressIsValid['contact@mailslurp.dev']).toEqual(
    true
);
expect(res.resultMapEmailAddressIsValid['bad@mailslurp.dev']).toEqual(
    false
);

You can view validated email addresses in the online dashboard

view bounced

Related content

Bounce email: verify address lists to reduce spam

Check email addresses exist before sending emails to reduce bounce-back returns and avoid spam bans.

Email Bounces: What to Do About Them?

Email Bounces: What to Do About Them?

Keeping your email marketing list clean - avoiding bounces and complaints

Keeping your email marketing list clean - avoiding bounces and complaints

Email address verification

Check email addresses are real and exist using MailSlurp email verification.

Email verification: checking a list of email addresses to reduce bounces

Avoid bounces and deliver emails to your customers with email verification and validation.

Reducing email bounces and maintaining account reputation using webhooks

Setup bounce notification webhooks for your account to reduce email bounces

Ready to dive in?Start building email applications today.