# Automate inbox placement and review reports

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](/docs/inbox-placement/) 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.

```javascript
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;
```

```javascript
const mailslurp = new MailSlurp({ apiKey: YOUR_API_KEY });
const inboxPlacementController = new ApiInboxPlacementTestControllerApi(
  new Configuration({ apiKey: YOUR_API_KEY }),
);
```

```javascript
// 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.

[API endpoint: `prepareInboxPlacementTest`](/docs/api/#prepareInboxPlacementTest)

[API endpoint: `createInboxPlacementTest`](/docs/api/#createInboxPlacementTest)

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

```javascript
// 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.

[API endpoint: `confirmInboxPlacementTestSent`](/docs/api/#confirmInboxPlacementTestSent)

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

```javascript
// 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.

```javascript
// 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](/docs/email-warmup/#connect-a-sender) 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.

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

[API endpoint: `exportInboxPlacementTestResults`](/docs/api/#exportInboxPlacementTestResults)

[API endpoint: `exportInboxPlacementSeedList`](/docs/api/#exportInboxPlacementSeedList)

[API endpoint: `getInboxPlacementPublicShare`](/docs/api/#getInboxPlacementPublicShare)

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](/docs/inbox-placement/#review-content-and-domain-signals), then follow up with [domain monitoring](/docs/domain-monitor/) or [Email Audit](/docs/email-audit/) for the issue found.
