MailSlurp logo

Reliable email and SMS webhook processing

Documentation navigation
Search documentation

Verify incoming events, queue work durably, prevent duplicate actions and recover failed webhook deliveries.

View MarkdownAgent setup

Use webhooks to start processing when an email, SMS, attachment or AI result arrives. MailSlurp delivers events at least once, so a successful receiver needs durable processing and duplicate handling in addition to a reachable URL.

Receive, verify and queue

  1. Parse the supported event type and retain its messageId and source resource IDs.
  2. Verify x-signature with x-message-id using MailSlurp's signature verification endpoint.
  3. Atomically insert the event into a durable queue or inbox table with a unique constraint on messageId.
  4. Return a 2xx response after the insert succeeds. An already-recorded duplicate can also return 2xx.
  5. Let a worker fetch the source message or result, process it and record completion.
POST /webhooks/verify

Verify a webhook payload signature

Verify a webhook payload using the messageId and signature. This allows you to be sure that MailSlurp sent the payload and not another server.

Request, parameters, and responses

Request body (required)

FieldTypeRequiredDescription
messageIdstringYes
signaturestringYes
Request example
{
  "messageId": "00000000-0000-4000-8000-000000000000",
  "signature": "value"
}

Responses

StatusSchemaDescription
200VerifyWebhookSignatureResultsOK
HTTP and SDK snippets

HTTP

HTTP
POST /webhooks/verify HTTP/1.1
Host: api.mailslurp.com
x-api-key: YOUR_API_KEY
Accept: application/json
Content-Type: application/json

{
  "messageId": "00000000-0000-4000-8000-000000000000",
  "signature": "value"
}

cURL

cURL
curl -X POST "https://api.mailslurp.com/webhooks/verify" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  --data '{"messageId":"00000000-0000-4000-8000-000000000000","signature":"value"}'

JavaScript SDK

JavaScript SDK
import { Configuration, WebhookControllerApi } from "mailslurp-client";

const config = new Configuration({ apiKey: "YOUR_API_KEY" });
const webhookController = new WebhookControllerApi(config);
const request = {
  "verifyWebhookSignatureOptions": {
    "messageId": "00000000-0000-4000-8000-000000000000",
    "signature": "value"
  }
};

const result = await webhookController.verifyWebhookSignature(request);

Python SDK

Python SDK
import mailslurp_client
from mailslurp_client.api.webhook_controller_api import WebhookControllerApi

configuration = mailslurp_client.Configuration()
configuration.api_key["x-api-key"] = "YOUR_API_KEY"

with mailslurp_client.ApiClient(configuration) as api_client:
    webhookController = WebhookControllerApi(api_client)
    verify_webhook_signature_options = {
      "messageId": "00000000-0000-4000-8000-000000000000",
      "signature": "value"
    }
    result = webhookController.verify_webhook_signature(verify_webhook_signature_options)

Do not acknowledge before durable storage: a crash after acknowledgement can lose work. Do not use an in-memory set as the only duplicate store because it disappears on restart and is not shared between workers.

Make downstream actions repeatable

A worker can crash after writing to a CRM or sending a reply but before recording success. Use a destination idempotency key or transaction keyed to the logical action, not just a local boolean. Keep the source event, extracted result and destination record associated for recovery.

For AI extraction results, validate the value before writing it. For agent replies, use claims, freshness guards and the completion endpoint's idempotency key. Apply the same principle to SMS processing.

Inspect failures and redrive

MailSlurp retries unsuccessful webhook deliveries with backoff for up to 24 hours. Inspect the delivery result and HTTP response to distinguish an unreachable endpoint, authentication failure and application error. Fix the receiver, then redrive the affected result.

GET /webhooks/{webhookId}/results

Get a webhook results for a webhook

Request, parameters, and responses

Path parameters

NameTypeRequiredDescription
webhookIdstring:uuidYesID of webhook to get results for

Query parameters

NameTypeRequiredDescription
pageinteger:int32NoOptional page index in list pagination
sizeinteger:int32NoOptional page size in list pagination
sortenum: ASC | DESCNoOptional createdAt sort direction ASC or DESCValues: ASC, DESC
searchFilterstringNoOptional search filter
sincestring:date-timeNoFilter by created at after the given timestamp
beforestring:date-timeNoFilter by created at before the given timestamp
unseenOnlybooleanNoFilter for unseen exceptions only
resultTypeenum: BAD_RESPONSE | EXCEPTION | EXHAUSTED | SUCCESS | REDRIVENNoFilter by result typeValues: BAD_RESPONSE, EXCEPTION, EXHAUSTED, SUCCESS, REDRIVEN
eventNameenum: EMAIL_RECEIVED | NEW_AI_TRANSFORM_RESULT | NEW_EMAIL | NEW_CONTACT | NEW_ATTACHMENT | EMAIL_OPENEDNoFilter by event nameValues: EMAIL_RECEIVED, NEW_AI_TRANSFORM_RESULT, NEW_EMAIL, NEW_CONTACT, NEW_ATTACHMENT, EMAIL_OPENED, EMAIL_READ, DELIVERY_STATUS, BOUNCE, BOUNCE_RECIPIENT, NEW_SMS, NEW_GUEST_USER
minStatusCodeinteger:int32NoMinimum response status
maxStatusCodeinteger:int32NoMaximum response status
inboxIdstring:uuidNoInbox ID
smsIdstring:uuidNoSms ID
attachmentIdstring:uuidNoAttachment ID
emailIdstring:uuidNoEmail ID
phoneIdstring:uuidNoPhone ID
aiTransformerIdstring:uuidNoAI Transformer ID

Responses

StatusSchemaDescription
200PageWebhookResultOK
HTTP and SDK snippets

HTTP

HTTP
GET /webhooks/00000000-0000-4000-8000-000000000000/results?page=value&size=value&sort=ASC HTTP/1.1
Host: api.mailslurp.com
x-api-key: YOUR_API_KEY
Accept: application/json

cURL

cURL
curl -X GET "https://api.mailslurp.com/webhooks/00000000-0000-4000-8000-000000000000/results?page=value&size=value&sort=ASC" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Accept: application/json"

JavaScript SDK

JavaScript SDK
import { Configuration, WebhookControllerApi } from "mailslurp-client";

const config = new Configuration({ apiKey: "YOUR_API_KEY" });
const webhookController = new WebhookControllerApi(config);
const request = {
  "webhookId": "00000000-0000-4000-8000-000000000000",
  "page": null,
  "size": null,
  "sort": "ASC"
};

const result = await webhookController.getWebhookResults(request);

Python SDK

Python SDK
import mailslurp_client
from mailslurp_client.api.webhook_controller_api import WebhookControllerApi

configuration = mailslurp_client.Configuration()
configuration.api_key["x-api-key"] = "YOUR_API_KEY"

with mailslurp_client.ApiClient(configuration) as api_client:
    webhookController = WebhookControllerApi(api_client)
    result = webhookController.get_webhook_results("00000000-0000-4000-8000-000000000000", page=NaN, size=NaN, sort="ASC")
POST /webhooks/results/{webhookResultId}/redrive

Get a webhook result and try to resend the original webhook payload

Allows you to resend a webhook payload that was already sent. Webhooks that fail are retried automatically for 24 hours and then put in a dead letter queue. You can retry results manually using this method.

Request, parameters, and responses

Path parameters

NameTypeRequiredDescription
webhookResultIdstring:uuidYesWebhook Result ID

Responses

StatusSchemaDescription
200WebhookRedriveResultOK
HTTP and SDK snippets

HTTP

HTTP
POST /webhooks/results/00000000-0000-4000-8000-000000000000/redrive HTTP/1.1
Host: api.mailslurp.com
x-api-key: YOUR_API_KEY
Accept: application/json

cURL

cURL
curl -X POST "https://api.mailslurp.com/webhooks/results/00000000-0000-4000-8000-000000000000/redrive" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Accept: application/json"

JavaScript SDK

JavaScript SDK
import { Configuration, WebhookControllerApi } from "mailslurp-client";

const config = new Configuration({ apiKey: "YOUR_API_KEY" });
const webhookController = new WebhookControllerApi(config);
const request = {
  "webhookResultId": "00000000-0000-4000-8000-000000000000"
};

const result = await webhookController.redriveWebhookResult(request);

Python SDK

Python SDK
import mailslurp_client
from mailslurp_client.api.webhook_controller_api import WebhookControllerApi

configuration = mailslurp_client.Configuration()
configuration.api_key["x-api-key"] = "YOUR_API_KEY"

with mailslurp_client.ApiClient(configuration) as api_client:
    webhookController = WebhookControllerApi(api_client)
    result = webhookController.redrive_webhook_result("00000000-0000-4000-8000-000000000000")

Redrive repeats delivery of the original payload, so the receiver must still deduplicate it. Redrive does not repair a downstream worker failure after your endpoint already returned 2xx; recover those jobs through your own queue.

Test before enabling the workflow

Send the API's test payload and confirm signature handling, queue insertion and event routing. Exercise duplicate delivery, receiver failure, worker restart and a downstream timeout. Check that only one business action occurs when an event is replayed.

POST /webhooks/{webhookId}/test

Send webhook test data

Request, parameters, and responses

Path parameters

NameTypeRequiredDescription
webhookIdstring:uuidYes

Responses

StatusSchemaDescription
201WebhookTestResultCreated
HTTP and SDK snippets

HTTP

HTTP
POST /webhooks/00000000-0000-4000-8000-000000000000/test HTTP/1.1
Host: api.mailslurp.com
x-api-key: YOUR_API_KEY
Accept: application/json

cURL

cURL
curl -X POST "https://api.mailslurp.com/webhooks/00000000-0000-4000-8000-000000000000/test" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Accept: application/json"

JavaScript SDK

JavaScript SDK
import { Configuration, WebhookControllerApi } from "mailslurp-client";

const config = new Configuration({ apiKey: "YOUR_API_KEY" });
const webhookController = new WebhookControllerApi(config);
const request = {
  "webhookId": "00000000-0000-4000-8000-000000000000"
};

const result = await webhookController.sendTestData(request);

Python SDK

Python SDK
import mailslurp_client
from mailslurp_client.api.webhook_controller_api import WebhookControllerApi

configuration = mailslurp_client.Configuration()
configuration.api_key["x-api-key"] = "YOUR_API_KEY"

with mailslurp_client.ApiClient(configuration) as api_client:
    webhookController = WebhookControllerApi(api_client)
    result = webhookController.send_test_data("00000000-0000-4000-8000-000000000000")
GET /webhooks/{webhookId}/wait

Wait for webhook results for a webhook

Request, parameters, and responses

Path parameters

NameTypeRequiredDescription
webhookIdstring:uuidYesID of webhook to get results for

Query parameters

NameTypeRequiredDescription
expectedCountinteger:int32YesExpected result count
timeoutinteger:int32YesMax time to wait in milliseconds

Responses

StatusSchemaDescription
200WebhookResultDto[]OK
HTTP and SDK snippets

HTTP

HTTP
GET /webhooks/00000000-0000-4000-8000-000000000000/wait?expectedCount=value&timeout=value HTTP/1.1
Host: api.mailslurp.com
x-api-key: YOUR_API_KEY
Accept: application/json

cURL

cURL
curl -X GET "https://api.mailslurp.com/webhooks/00000000-0000-4000-8000-000000000000/wait?expectedCount=value&timeout=value" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Accept: application/json"

JavaScript SDK

JavaScript SDK
import { Configuration, WebhookControllerApi } from "mailslurp-client";

const config = new Configuration({ apiKey: "YOUR_API_KEY" });
const webhookController = new WebhookControllerApi(config);
const request = {
  "webhookId": "00000000-0000-4000-8000-000000000000",
  "expectedCount": null,
  "timeout": null
};

const result = await webhookController.waitForWebhookResults(request);

Python SDK

Python SDK
import mailslurp_client
from mailslurp_client.api.webhook_controller_api import WebhookControllerApi

configuration = mailslurp_client.Configuration()
configuration.api_key["x-api-key"] = "YOUR_API_KEY"

with mailslurp_client.ApiClient(configuration) as api_client:
    webhookController = WebhookControllerApi(api_client)
    result = webhookController.wait_for_webhook_results("00000000-0000-4000-8000-000000000000", expected_count=NaN, timeout=NaN)

Continue with webhook event schemas and setup, forwarding or AI transformer mappings, depending on whether your destination needs the original email or structured fields.