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.

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.

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.

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());

Ready to dive in?Start building email applications today.