MailSlurp logo

Automate inbox placement and review reports

Documentation navigation
Search documentation

Create placement runs, send the current tracking marker, wait for completion and compare provider results over time.

View MarkdownAgent setup

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.

POST /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)

FieldTypeRequiredDescription
seedlistProfilestringNo
requestedSegmentenum: ALL_INBOXES | PERSONAL_INBOXES | PROFESSIONAL_INBOXESYes
senderDomainstringNo
fromEmailstringNo
publicShareRequestedbooleanYes
addressFormatenum: COMMA | NEWLINEYes
Request example
{
  "requestedSegment": "ALL_INBOXES",
  "publicShareRequested": true,
  "addressFormat": "COMMA",
  "seedlistProfile": "value",
  "senderDomain": "example.com",
  "fromEmail": "user@example.com"
}

Responses

StatusSchemaDescription
200InboxPlacementTestRunDtoOK
HTTP and SDK snippets

HTTP

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
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

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

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)
POST /inbox-placement-tests

Create a new direct-send inbox placement test

Request, parameters, and responses

Request body (required)

FieldTypeRequiredDescription
seedlistProfilestringNo
requestedSegmentenum: ALL_INBOXES | PERSONAL_INBOXES | PROFESSIONAL_INBOXESYes
senderDomainstringNo
fromEmailstringNo
publicShareRequestedbooleanYes
addressFormatenum: COMMA | NEWLINEYes
Request example
{
  "requestedSegment": "ALL_INBOXES",
  "publicShareRequested": true,
  "addressFormat": "COMMA",
  "seedlistProfile": "value",
  "senderDomain": "example.com",
  "fromEmail": "user@example.com"
}

Responses

StatusSchemaDescription
200InboxPlacementTestRunDtoOK
HTTP and SDK snippets

HTTP

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
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

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

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.

POST /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

NameTypeRequiredDescription
idstring:uuidYes

Responses

StatusSchemaDescription
200InboxPlacementTestRunDtoOK
HTTP and SDK snippets

HTTP

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
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

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

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,
});
GET /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

NameTypeRequiredDescription
idstring:uuidYes

Responses

StatusSchemaDescription
200ResponseOK
HTTP and SDK snippets

HTTP

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
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

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

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")
GET /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

NameTypeRequiredDescription
idstring:uuidYes

Query parameters

NameTypeRequiredDescription
providerenum: GENERIC | MAILCHIMP | KLAVIYO | SALESFORCE | HUBSPOT | BRAZENoValues: GENERIC, MAILCHIMP, KLAVIYO, SALESFORCE, HUBSPOT, BRAZE, BREVO, MAILERLITE, MAILJET, OMNISEND, GETRESPONSE, CONSTANT_CONTACT

Responses

StatusSchemaDescription
200ResponseOK
HTTP and SDK snippets

HTTP

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
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

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

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")
GET /inbox-placement-tests/share/{shareToken}

Get a public inbox placement share

Request, parameters, and responses

Path parameters

NameTypeRequiredDescription
shareTokenstringYes

Responses

StatusSchemaDescription
200InboxPlacementPublicShareDtoOK
HTTP and SDK snippets

HTTP

HTTP
GET /inbox-placement-tests/share/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/inbox-placement-tests/share/value" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Accept: application/json"

JavaScript SDK

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 = {
  "shareToken": "value"
};

const result = await apiInboxPlacementTestControllerController.getInboxPlacementPublicShare(request);

Python SDK

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.get_inbox_placement_public_share("value")

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.