Simulate-first QA (without Resend)

June 6, 2026 · View on GitHub

When staging does not send mail yet, or you need deterministic OTP in CI — use POST /v1/inboxes/:id/simulate.

When to use

ScenarioSolution
CI consumer repo without Neonsimulate via REST or @mailagent/qa
Local OTP parser debugDebug UI → "Simulate OTP email"
MailAgent contract / smokenpm run test:contract:qa (API key only)
Full E2E with stagingNormal waitForVerification + Resend

REST

INBOX=$(curl -sS -X POST "$MAILAGENT_API_URL/v1/inboxes" \
  -H "Authorization: Bearer $MAILAGENT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"label":"sim-test","ttlMinutes":15}' | jq -r .id)

curl -sS -X POST "$MAILAGENT_API_URL/v1/inboxes/$INBOX/simulate" \
  -H "Authorization: Bearer $MAILAGENT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"otp":"482910","subject":"Verify (simulated)"}' | jq .

curl -sS "$MAILAGENT_API_URL/v1/inboxes/$INBOX/extract" \
  -H "Authorization: Bearer $MAILAGENT_API_KEY" | jq .otp

Threading (v0.18)

Simulate a thread without SMTP — for contract npm run test:contract:qa:threads:

ROOT=$(curl -sS -X POST "$MAILAGENT_API_URL/v1/inboxes/$INBOX/simulate" \
  -H "Authorization: Bearer $MAILAGENT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"subject":"Support ticket","rfcMessageId":"root@sim.test"}')

ROOT_ID=$(echo "$ROOT" | jq -r .messageId)

curl -sS -X POST "$MAILAGENT_API_URL/v1/inboxes/$INBOX/simulate" \
  -H "Authorization: Bearer $MAILAGENT_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"subject\":\"Re: Support ticket\",\"inReplyToMessageId\":\"$ROOT_ID\"}" | jq .threadId

curl -sS "$MAILAGENT_API_URL/v1/inboxes/$INBOX/threads" \
  -H "Authorization: Bearer $MAILAGENT_API_KEY" | jq .

@mailagent/qa

const inbox = await mail.createInbox({ label: MailAgentQa.runLabel("ci") });

const verification = await mail.simulateAndVerify(inbox.id, {
  otp: "556677",
  subjectContains: "simulated",
});

expect(verification.otp).toBe("556677");
await mail.deleteInbox(inbox.id);

Playwright

GitHub Actions (consumer repo)

Copy examples/github-actions/qa-simulate-only.yml — secret: MAILAGENT_API_KEY.

MCP / Codex

mailagent_simulate_message → then mailagent_wait_and_extract or mailagent_verify_signup.

See also QA-TROUBLESHOOTING.md · QA-LOCAL-SMTP.md.