Reliable email and SMS webhook processing
Documentation navigation
Verify incoming events, queue work durably, prevent duplicate actions and recover failed webhook deliveries.
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
- Parse the supported event type and retain its
messageIdand source resource IDs. - Verify
x-signaturewithx-message-idusing MailSlurp's signature verification endpoint. - Atomically insert the event into a durable queue or inbox table with a unique constraint on
messageId. - Return a 2xx response after the insert succeeds. An already-recorded duplicate can also return 2xx.
- Let a worker fetch the source message or result, process it and record completion.
/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)
| Field | Type | Required | Description |
|---|---|---|---|
messageId | string | Yes | |
signature | string | Yes |
{
"messageId": "00000000-0000-4000-8000-000000000000",
"signature": "value"
}
Responses
| Status | Schema | Description |
|---|---|---|
200 | VerifyWebhookSignatureResults | OK |
HTTP and SDK snippets
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 -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
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
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.
/webhooks/{webhookId}/results
Get a webhook results for a webhook
Request, parameters, and responses
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
webhookId | string:uuid | Yes | ID of webhook to get results for |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
page | integer:int32 | No | Optional page index in list pagination |
size | integer:int32 | No | Optional page size in list pagination |
sort | enum: ASC | DESC | No | Optional createdAt sort direction ASC or DESCValues: ASC, DESC |
searchFilter | string | No | Optional search filter |
since | string:date-time | No | Filter by created at after the given timestamp |
before | string:date-time | No | Filter by created at before the given timestamp |
unseenOnly | boolean | No | Filter for unseen exceptions only |
resultType | enum: BAD_RESPONSE | EXCEPTION | EXHAUSTED | SUCCESS | REDRIVEN | No | Filter by result typeValues: BAD_RESPONSE, EXCEPTION, EXHAUSTED, SUCCESS, REDRIVEN |
eventName | enum: EMAIL_RECEIVED | NEW_AI_TRANSFORM_RESULT | NEW_EMAIL | NEW_CONTACT | NEW_ATTACHMENT | EMAIL_OPENED | No | Filter 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 |
minStatusCode | integer:int32 | No | Minimum response status |
maxStatusCode | integer:int32 | No | Maximum response status |
inboxId | string:uuid | No | Inbox ID |
smsId | string:uuid | No | Sms ID |
attachmentId | string:uuid | No | Attachment ID |
emailId | string:uuid | No | Email ID |
phoneId | string:uuid | No | Phone ID |
aiTransformerId | string:uuid | No | AI Transformer ID |
Responses
| Status | Schema | Description |
|---|---|---|
200 | PageWebhookResult | OK |
HTTP and SDK snippets
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 -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
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
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")
/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
| Name | Type | Required | Description |
|---|---|---|---|
webhookResultId | string:uuid | Yes | Webhook Result ID |
Responses
| Status | Schema | Description |
|---|---|---|
200 | WebhookRedriveResult | OK |
HTTP and SDK snippets
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 -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
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
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.
/webhooks/{webhookId}/test
Send webhook test data
Request, parameters, and responses
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
webhookId | string:uuid | Yes |
Responses
| Status | Schema | Description |
|---|---|---|
201 | WebhookTestResult | Created |
HTTP and SDK snippets
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 -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
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
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")
/webhooks/{webhookId}/wait
Wait for webhook results for a webhook
Request, parameters, and responses
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
webhookId | string:uuid | Yes | ID of webhook to get results for |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
expectedCount | integer:int32 | Yes | Expected result count |
timeout | integer:int32 | Yes | Max time to wait in milliseconds |
Responses
| Status | Schema | Description |
|---|---|---|
200 | WebhookResultDto[] | OK |
HTTP and SDK snippets
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 -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
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
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.