Automate inbox placement and review reports
Documentation navigation
Create placement runs, send the current tracking marker, wait for completion and compare provider results over time.
Automate a placement test when a campaign, template or sender changes. Send through the same ESP and sender identity used for the campaign so the result measures that delivery path. Start with the manual workflow if you are testing a provider for the first time.
Create a run and use its current seed list
The JavaScript example uses mailslurp-client and its ApiInboxPlacementTestControllerApi. Define YOUR_API_KEY, YOUR_DOMAIN and YOUR_FROM_EMAIL from server-side configuration.
import { MailSlurp, Configuration, ApiInboxPlacementTestControllerApi } from 'mailslurp-client';
import { setTimeout as sleep } from 'node:timers/promises';
const YOUR_API_KEY = process.env.MAILSLURP_API_KEY;
const YOUR_DOMAIN = 'example.com';
const YOUR_FROM_EMAIL = 'hello@example.com';
const YOUR_SENDER_INBOX_ID = process.env.MAILSLURP_SENDER_INBOX_ID;
const mailslurp = new MailSlurp({ apiKey: YOUR_API_KEY });
const inboxPlacementController = new ApiInboxPlacementTestControllerApi(
new Configuration({ apiKey: YOUR_API_KEY }),
);
// Create an inbox placement run and ask MailSlurp for seed inboxes.
const inboxPlacementRun =
await inboxPlacementController.createInboxPlacementTest({
createInboxPlacementTestOptions: {
requestedSegment: "ALL_INBOXES",
senderDomain: YOUR_DOMAIN,
fromEmail: YOUR_FROM_EMAIL,
publicShareRequested: true,
addressFormat: "COMMA",
},
});
console.log({
id: inboxPlacementRun.id,
totalTargets: inboxPlacementRun.totalTargets,
seedAddresses: inboxPlacementRun.seedAddresses,
});
Keep the run ID, trackingToken and seedAddresses together. Each test supplies its own marker and targets; do not reuse values from an earlier test. Use prepareInboxPlacementTest when you want to inspect preparation before consuming a test allowance.
/inbox-placement-tests/prepare
Prepare a direct-send inbox placement test without consuming allowance
Reserve seed addresses for the dashboard create flow. The run remains staged and allowance is not committed until send confirmation. Reuses a recent active preparation when possible.
Request, parameters, and responses
Request body (required)
| Field | Type | Required | Description |
|---|---|---|---|
seedlistProfile | string | No | |
requestedSegment | enum: ALL_INBOXES | PERSONAL_INBOXES | PROFESSIONAL_INBOXES | Yes | |
senderDomain | string | No | |
fromEmail | string | No | |
publicShareRequested | boolean | Yes | |
addressFormat | enum: COMMA | NEWLINE | Yes |
{
"requestedSegment": "ALL_INBOXES",
"publicShareRequested": true,
"addressFormat": "COMMA",
"seedlistProfile": "value",
"senderDomain": "example.com",
"fromEmail": "user@example.com"
}
Responses
| Status | Schema | Description |
|---|---|---|
200 | InboxPlacementTestRunDto | OK |
HTTP and SDK snippets
HTTP
POST /inbox-placement-tests/prepare HTTP/1.1
Host: api.mailslurp.com
x-api-key: YOUR_API_KEY
Accept: application/json
Content-Type: application/json
{
"requestedSegment": "ALL_INBOXES",
"publicShareRequested": true,
"addressFormat": "COMMA",
"seedlistProfile": "value",
"senderDomain": "example.com",
"fromEmail": "user@example.com"
}
cURL
curl -X POST "https://api.mailslurp.com/inbox-placement-tests/prepare" \
-H "x-api-key: YOUR_API_KEY" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
--data '{"requestedSegment":"ALL_INBOXES","publicShareRequested":true,"addressFormat":"COMMA","seedlistProfile":"value","senderDomain":"example.com","fromEmail":"user@example.com"}'
JavaScript SDK
import { Configuration, api-inbox-placement-test-controllerControllerApi } from "mailslurp-client";
const config = new Configuration({ apiKey: "YOUR_API_KEY" });
const apiInboxPlacementTestControllerController = new api-inbox-placement-test-controllerControllerApi(config);
const request = {
"createInboxPlacementTestOptions": {
"requestedSegment": "ALL_INBOXES",
"publicShareRequested": true,
"addressFormat": "COMMA",
"seedlistProfile": "value",
"senderDomain": "example.com",
"fromEmail": "user@example.com"
}
};
const result = await apiInboxPlacementTestControllerController.prepareInboxPlacementTest(request);
Python SDK
import mailslurp_client
from mailslurp_client.api.api_inbox_placement_test_controller_controller_api import api-inbox-placement-test-controllerControllerApi
configuration = mailslurp_client.Configuration()
configuration.api_key["x-api-key"] = "YOUR_API_KEY"
with mailslurp_client.ApiClient(configuration) as api_client:
apiInboxPlacementTestControllerController = api-inbox-placement-test-controllerControllerApi(api_client)
create_inbox_placement_test_options = {
"requestedSegment": "ALL_INBOXES",
"publicShareRequested": True,
"addressFormat": "COMMA",
"seedlistProfile": "value",
"senderDomain": "example.com",
"fromEmail": "user@example.com"
}
result = apiInboxPlacementTestControllerController.prepare_inbox_placement_test(create_inbox_placement_test_options)
/inbox-placement-tests
Create a new direct-send inbox placement test
Request, parameters, and responses
Request body (required)
| Field | Type | Required | Description |
|---|---|---|---|
seedlistProfile | string | No | |
requestedSegment | enum: ALL_INBOXES | PERSONAL_INBOXES | PROFESSIONAL_INBOXES | Yes | |
senderDomain | string | No | |
fromEmail | string | No | |
publicShareRequested | boolean | Yes | |
addressFormat | enum: COMMA | NEWLINE | Yes |
{
"requestedSegment": "ALL_INBOXES",
"publicShareRequested": true,
"addressFormat": "COMMA",
"seedlistProfile": "value",
"senderDomain": "example.com",
"fromEmail": "user@example.com"
}
Responses
| Status | Schema | Description |
|---|---|---|
200 | InboxPlacementTestRunDto | OK |
HTTP and SDK snippets
HTTP
POST /inbox-placement-tests HTTP/1.1
Host: api.mailslurp.com
x-api-key: YOUR_API_KEY
Accept: application/json
Content-Type: application/json
{
"requestedSegment": "ALL_INBOXES",
"publicShareRequested": true,
"addressFormat": "COMMA",
"seedlistProfile": "value",
"senderDomain": "example.com",
"fromEmail": "user@example.com"
}
cURL
curl -X POST "https://api.mailslurp.com/inbox-placement-tests" \
-H "x-api-key: YOUR_API_KEY" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
--data '{"requestedSegment":"ALL_INBOXES","publicShareRequested":true,"addressFormat":"COMMA","seedlistProfile":"value","senderDomain":"example.com","fromEmail":"user@example.com"}'
JavaScript SDK
import { Configuration, api-inbox-placement-test-controllerControllerApi } from "mailslurp-client";
const config = new Configuration({ apiKey: "YOUR_API_KEY" });
const apiInboxPlacementTestControllerController = new api-inbox-placement-test-controllerControllerApi(config);
const request = {
"createInboxPlacementTestOptions": {
"requestedSegment": "ALL_INBOXES",
"publicShareRequested": true,
"addressFormat": "COMMA",
"seedlistProfile": "value",
"senderDomain": "example.com",
"fromEmail": "user@example.com"
}
};
const result = await apiInboxPlacementTestControllerController.createInboxPlacementTest(request);
Python SDK
import mailslurp_client
from mailslurp_client.api.api_inbox_placement_test_controller_controller_api import api-inbox-placement-test-controllerControllerApi
configuration = mailslurp_client.Configuration()
configuration.api_key["x-api-key"] = "YOUR_API_KEY"
with mailslurp_client.ApiClient(configuration) as api_client:
apiInboxPlacementTestControllerController = api-inbox-placement-test-controllerControllerApi(api_client)
create_inbox_placement_test_options = {
"requestedSegment": "ALL_INBOXES",
"publicShareRequested": True,
"addressFormat": "COMMA",
"seedlistProfile": "value",
"senderDomain": "example.com",
"fromEmail": "user@example.com"
}
result = apiInboxPlacementTestControllerController.create_inbox_placement_test(create_inbox_placement_test_options)
Send and confirm
Put the exact tracking token in the subject or body and send to every supplied seed address. The following example uses a MailSlurp sender inbox identified by YOUR_SENDER_INBOX_ID. For an ESP campaign, perform this send through that ESP instead.
// Send the campaign email to every seed inbox from a verified sender inbox.
await mailslurp.sendEmail(YOUR_SENDER_INBOX_ID, {
to: inboxPlacementRun.seedAddresses,
subject: `Inbox placement smoke test for ${YOUR_DOMAIN}`,
body: `
<!doctype html>
<html>
<body>
<h1>Inbox placement test</h1>
<p>This message measures inbox, spam, and missing placement.</p>
</body>
</html>
`,
isHTML: true,
});
Confirm the send when your integration has submitted it. Keep send failure separate from placement failure: an ESP rejecting the request is different from a successfully sent message reaching spam.
/inbox-placement-tests/{id}/confirm-sent
Confirm an inbox placement test email has been sent
Reconcile any messages that arrived after seed reservation, commit allowance, and move a prepared dashboard test into its active lifecycle. This operation is idempotent.
Request, parameters, and responses
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string:uuid | Yes |
Responses
| Status | Schema | Description |
|---|---|---|
200 | InboxPlacementTestRunDto | OK |
HTTP and SDK snippets
HTTP
POST /inbox-placement-tests/00000000-0000-4000-8000-000000000000/confirm-sent HTTP/1.1
Host: api.mailslurp.com
x-api-key: YOUR_API_KEY
Accept: application/json
cURL
curl -X POST "https://api.mailslurp.com/inbox-placement-tests/00000000-0000-4000-8000-000000000000/confirm-sent" \
-H "x-api-key: YOUR_API_KEY" \
-H "Accept: application/json"
JavaScript SDK
import { Configuration, api-inbox-placement-test-controllerControllerApi } from "mailslurp-client";
const config = new Configuration({ apiKey: "YOUR_API_KEY" });
const apiInboxPlacementTestControllerController = new api-inbox-placement-test-controllerControllerApi(config);
const request = {
"id": "00000000-0000-4000-8000-000000000000"
};
const result = await apiInboxPlacementTestControllerController.confirmInboxPlacementTestSent(request);
Python SDK
import mailslurp_client
from mailslurp_client.api.api_inbox_placement_test_controller_controller_api import api-inbox-placement-test-controllerControllerApi
configuration = mailslurp_client.Configuration()
configuration.api_key["x-api-key"] = "YOUR_API_KEY"
with mailslurp_client.ApiClient(configuration) as api_client:
apiInboxPlacementTestControllerController = api-inbox-placement-test-controllerControllerApi(api_client)
result = apiInboxPlacementTestControllerController.confirm_inbox_placement_test_sent("00000000-0000-4000-8000-000000000000")
Wait for the complete result
Poll with a deadline. A matched target means some evidence has arrived; it does not mean every provider has completed its observation window.
// Poll the run until MailSlurp has placement evidence from the seed inboxes.
async function waitForInboxPlacementResults(testId) {
for (let attempt = 0; attempt < 20; attempt += 1) {
const run = await inboxPlacementController.getInboxPlacementTest({
id: testId,
});
if (run.completedAt || run.matchedTargets > 0) {
return inboxPlacementController.getInboxPlacementTestResults({
id: testId,
});
}
await sleep(15_000);
}
throw new Error(`Inbox placement test ${testId} did not finish in time`);
}
const inboxPlacementResults = await waitForInboxPlacementResults(
inboxPlacementRun.id,
);
const deliverability = inboxPlacementResults.results?.deliverability;
console.log({
totalTargets: deliverability?.totalTargets,
inboxTargets: deliverability?.inboxTargets,
spamTargets: deliverability?.spamTargets,
notReceivedTargets: deliverability?.notReceivedTargets,
spamScores: inboxPlacementResults.spamScores,
});
The example allows five minutes. Set the deadline according to your test window and retain the run ID if your local deadline expires, so you can inspect the run later. Do not turn a still-pending provider into an assumed inbox or missing outcome.
// Inspect per-provider and per-inbox placement outcomes.
const targetResults = inboxPlacementResults.results?.targets ?? [];
const placementsByProvider = targetResults.reduce((acc, target) => {
const provider = target.provider || "unknown";
acc[provider] ??= { inbox: 0, spam: 0, missing: 0 };
if (target.placementFolder === "INBOX") {
acc[provider].inbox += 1;
} else if (target.placementFolder === "SPAM") {
acc[provider].spam += 1;
} else {
acc[provider].missing += 1;
}
return acc;
}, {});
console.log(placementsByProvider);
Preserve each returned placement value, including Promotions and any unresolved state. Keep sender, campaign revision and audience segment with the result when comparing tests.
Schedule recurring checks
In the dashboard, connect and verify your sending service, choose the audience and configure the recurring placement test. Reuse a verified sending connection from email warmup where appropriate. Send an initial test and review its provider coverage before relying on the schedule.
Review the sender's test history after credential, template or DNS changes. Configure available Slack or Teams placement alerts in the placement workflow and verify the destination. For a custom scheduler, use the public create/send/confirm/results workflow above on each run, obtaining a fresh seed set and marker each time.
Compare, share and export
Use analytics with the same sender filters and comparable time periods. A changed seed audience or campaign is context for a difference, not proof of a sender regression by itself.
// Fetch aggregate inbox placement trends for your sender domain.
const before = new Date();
const since = new Date(before.getTime() - 30 * 24 * 60 * 60 * 1000);
const breakdown =
await inboxPlacementController.getInboxPlacementAnalyticsBreakdown({
since,
before,
senderDomain: YOUR_DOMAIN,
fromEmail: YOUR_FROM_EMAIL,
limit: 10,
});
const series = await inboxPlacementController.getInboxPlacementAnalyticsSeries({
since,
before,
bucket: "DAY",
groupBy: "SENDER_DOMAIN",
senderDomain: YOUR_DOMAIN,
runLimit: 100,
groupLimit: 5,
});
console.log({
summary: breakdown.summary,
folders: breakdown.folders,
points: series.points,
});
/inbox-placement-tests/{id}/results/export
Export inbox placement test results as CSV
Export one row per placement target, including run-level delivery totals, 0-10 placement scores, and target placement outcome fields.
Request, parameters, and responses
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string:uuid | Yes |
Responses
| Status | Schema | Description |
|---|---|---|
200 | Response | OK |
HTTP and SDK snippets
HTTP
GET /inbox-placement-tests/00000000-0000-4000-8000-000000000000/results/export HTTP/1.1
Host: api.mailslurp.com
x-api-key: YOUR_API_KEY
Accept: application/json
cURL
curl -X GET "https://api.mailslurp.com/inbox-placement-tests/00000000-0000-4000-8000-000000000000/results/export" \
-H "x-api-key: YOUR_API_KEY" \
-H "Accept: application/json"
JavaScript SDK
import { Configuration, api-inbox-placement-test-controllerControllerApi } from "mailslurp-client";
const config = new Configuration({ apiKey: "YOUR_API_KEY" });
const apiInboxPlacementTestControllerController = new api-inbox-placement-test-controllerControllerApi(config);
const request = {
"id": "00000000-0000-4000-8000-000000000000"
};
const result = await apiInboxPlacementTestControllerController.exportInboxPlacementTestResults(request);
Python SDK
import mailslurp_client
from mailslurp_client.api.api_inbox_placement_test_controller_controller_api import api-inbox-placement-test-controllerControllerApi
configuration = mailslurp_client.Configuration()
configuration.api_key["x-api-key"] = "YOUR_API_KEY"
with mailslurp_client.ApiClient(configuration) as api_client:
apiInboxPlacementTestControllerController = api-inbox-placement-test-controllerControllerApi(api_client)
result = apiInboxPlacementTestControllerController.export_inbox_placement_test_results("00000000-0000-4000-8000-000000000000")
/inbox-placement-tests/{id}/seed-list.csv
Download inbox placement seed addresses as an email-platform CSV
Export the current run's seed addresses with headers recognized by the selected email platform. Downloading a prepared seed list does not confirm the send or consume allowance.
Request, parameters, and responses
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | string:uuid | Yes |
Query parameters
| Name | Type | Required | Description |
|---|---|---|---|
provider | enum: GENERIC | MAILCHIMP | KLAVIYO | SALESFORCE | HUBSPOT | BRAZE | No | Values: GENERIC, MAILCHIMP, KLAVIYO, SALESFORCE, HUBSPOT, BRAZE, BREVO, MAILERLITE, MAILJET, OMNISEND, GETRESPONSE, CONSTANT_CONTACT |
Responses
| Status | Schema | Description |
|---|---|---|
200 | Response | OK |
HTTP and SDK snippets
HTTP
GET /inbox-placement-tests/00000000-0000-4000-8000-000000000000/seed-list.csv?provider=GENERIC HTTP/1.1
Host: api.mailslurp.com
x-api-key: YOUR_API_KEY
Accept: application/json
cURL
curl -X GET "https://api.mailslurp.com/inbox-placement-tests/00000000-0000-4000-8000-000000000000/seed-list.csv?provider=GENERIC" \
-H "x-api-key: YOUR_API_KEY" \
-H "Accept: application/json"
JavaScript SDK
import { Configuration, api-inbox-placement-test-controllerControllerApi } from "mailslurp-client";
const config = new Configuration({ apiKey: "YOUR_API_KEY" });
const apiInboxPlacementTestControllerController = new api-inbox-placement-test-controllerControllerApi(config);
const request = {
"id": "00000000-0000-4000-8000-000000000000",
"provider": "GENERIC"
};
const result = await apiInboxPlacementTestControllerController.exportInboxPlacementSeedList(request);
Python SDK
import mailslurp_client
from mailslurp_client.api.api_inbox_placement_test_controller_controller_api import api-inbox-placement-test-controllerControllerApi
configuration = mailslurp_client.Configuration()
configuration.api_key["x-api-key"] = "YOUR_API_KEY"
with mailslurp_client.ApiClient(configuration) as api_client:
apiInboxPlacementTestControllerController = api-inbox-placement-test-controllerControllerApi(api_client)
result = apiInboxPlacementTestControllerController.export_inbox_placement_seed_list("00000000-0000-4000-8000-000000000000", provider="GENERIC")
/inbox-placement-tests/share/{shareToken}
Get a public inbox placement share
Request, parameters, and responses
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
shareToken | string | Yes |
Responses
| Status | Schema | Description |
|---|---|---|
200 | InboxPlacementPublicShareDto | OK |
HTTP and SDK snippets
Export the result CSV for analysis and use the seed-list CSV to prepare recipient imports. Request public sharing only when the campaign is suitable for external review. Inspect content and domain findings, then follow up with domain monitoring or Email Audit for the issue found.