Webhook integrations fail most often because teams only test the happy path. This guide focuses on reliability checks you should run before production.

## MailSlurp webhook behavior summary

- MailSlurp sends events to your endpoint via HTTP `POST`
- `200`/`201` within timeout window is treated as success
- non-success responses and timeouts are retried
- failed deliveries are queued for retry until processed or exhausted

## Test scenarios you should always run

| Scenario                            | Expected result         | Why it matters                      |
| ----------------------------------- | ----------------------- | ----------------------------------- |
| Endpoint returns `200` quickly      | Event marked delivered  | Confirms baseline integration       |
| Endpoint returns `500`              | Event is retried        | Validates retry path                |
| Endpoint times out                  | Event is retried        | Validates latency/error handling    |
| Endpoint returns malformed response | Event handled as failed | Confirms parser/contract resilience |

## Manual testing option

For early local testing:

- expose local service with ngrok or equivalent
- send a controlled event
- verify payload handling and response code

Useful during initial development, but not sufficient as a release gate.

## Automated webhook testing option

Use the MailSlurp test webhook helpers to generate deterministic endpoints and simulate known response patterns.

Success-path example:



```typescript
import { CreateWebhookOptionsEventNameEnum, MailSlurp } from "mailslurp-client";
import {
  Configuration as TestWebhookConfiguration,
  CreateRulesetOptionsStrategyEnum,
  EndpointControllerApi,
} from "@mailslurp/test-webhooks";

const apiKey = process.env.API_KEY!!;
const mailslurp = new MailSlurp({ apiKey });
const devhooksEndpointController = new EndpointControllerApi(
  new TestWebhookConfiguration({
    basePath: "http://api.infrahooks.com",
  }),
);

describe.skip("NEW_EMAIL webhooks", () => {
  test("can create NEW_EMAIL webhook and receive successfully", async () => {
    // create an inbox, webhook, and a test endpoint
    const testEndpoint = await devhooksEndpointController.createEndpoint({});
    const inbox = await mailslurp.createInbox();
    const webhook = await mailslurp.webhookController.createWebhook({
      createWebhookOptions: {
        eventName: CreateWebhookOptionsEventNameEnum.NEW_EMAIL,
        url: testEndpoint.url!!,
      },
      inboxId: inbox.id!,
    });
    // can see that endpoint has not received an event
    const endpointHistory = await devhooksEndpointController.getEndpointHistory({
      endpointId: testEndpoint.id!,
    });
    expect(endpointHistory.items?.length).toEqual(0);
    // send email to inbox
    await mailslurp.sendEmail(inbox.id!, {
      to: [inbox.emailAddress!],
      subject: "email1",
    });
    // can fetch the email directly
    const email = await mailslurp.waitForLatestEmail(inbox.id!, 60000, true);
    expect(email.subject).toEqual("email1");
    // endpoint receives the payload (note the expected length to wait for)
    const endpointHistory2 = await devhooksEndpointController.getEndpointHistory({
      endpointId: testEndpoint.id!,
      expectedLength: 1,
    });
    expect(endpointHistory2.items?.length).toEqual(1);

    // assert correct payload was sent to endpoint
    const payload = JSON.parse(endpointHistory2.items?.[0]?.request?.body!);
    expect(payload.webhookId).toEqual(webhook.id);
    expect(payload.eventName).toEqual("NEW_EMAIL");
    expect(payload.inboxId).toEqual(inbox.id);
    expect(payload.emailId).toEqual(email.id);
    expect(payload.to).toEqual([inbox.emailAddress]);
    expect(payload.from).toEqual(inbox.emailAddress);
    expect(payload.subject).toEqual("email1");

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

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



Failure-path example:



```typescript
test.skip("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 devhooksEndpointController.createEndpoint({});
  await devhooksEndpointController.createEndpointRuleset({
    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 devhooksEndpointController.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.numberOfElements).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!,
  });
});
```



## Production hardening checklist

- enforce idempotency for repeated event deliveries
- log request IDs and event IDs for traceability
- keep response time low (do heavy work asynchronously)
- add alerting for retry spikes and sustained failures
- run webhook contract tests in CI

## Recommended endpoint pattern

1. Validate payload structure and signature (if enabled).
2. Persist the event quickly.
3. Return `200`.
4. Process downstream asynchronously.

This pattern avoids accidental retries caused by slow business logic.

## Related guides

- [Email webhooks architecture](/guides/email-webhooks/)
- [Receive emails in code](/about/receive-emails-in-code/)
- [Email parser API](/automations/email-parser-api/)
