QA / E2E guide
Open-source QA flows — temporary inbox per test run, OTP and magic links
without a shared test@company.com.
Source on GitHub.
Problems we solve
| Pain | Solution |
|---|---|
| Shared inbox races in CI | label per run / worker |
| Long waits in tests | POST /v1/inboxes/open |
| Wrong email picked | subjectContains + allowlist |
| Parse HTML for OTP | verification.otp, primaryLink |
| Debug failed run | GET /v1/inboxes?label=... |
30-minute pilot
Fastest: copy
qa-pilot-starter
(simulate in CI · staging via APP_SIGNUP_URL) ·
Cypress starter.
Guide:
QA-PILOT.md
· validate with npm run wizard:qa-pilot.
CI quick start
export MAILAGENT_API_URL=https://api.webmailagent.com
export MAILAGENT_API_KEY=your_key
export RUN_ID="${GITHUB_RUN_ID:-local}"
curl -sS -X POST "$MAILAGENT_API_URL/v1/inboxes/open" \
-H "Authorization: Bearer $MAILAGENT_API_KEY" \
-H "Content-Type: application/json" \
-d "{
\"label\": \"ci-$RUN_ID\",
\"service\": \"auth0\",
\"subjectContains\": \"verification\",
\"timeoutSeconds\": 120
}"
Recommended E2E order
POST /v1/inboxeswithlabel→ getaddress- Fill signup form on staging
GET /v1/inboxes/:id/wait?subjectContains=...then/extract
Playwright / @mailagent/qa
npm install @mailagent/qa@latest
import { createMailAgentQa, MailAgentQa } from "@mailagent/qa";
const mail = createMailAgentQa(); // MAILAGENT_API_URL + MAILAGENT_API_KEY
const inbox = await mail.createInbox({
label: MailAgentQa.ciLabel("pw"),
service: "auth0",
});
// page.fill(inbox.address) → submit
const v = await mail.waitForVerification(inbox.id, {
subjectContains: "verify",
messageIndex: 0, // 1 = second matching email
});
// callbackUrl flows: await mail.waitForCallback(inbox.id, { since: inbox.createdAt });
Fixture with auto-delete: examples/playwright/mailagent.fixture.ts
QA fields
| Field | Where | Purpose |
|---|---|---|
label | create, open | CI run / worker id |
subjectContains | open, wait | Filter by subject |
messageIndex | open, wait, verify | 0=newest match, 1=second, … |
callbackUrl | create, open | HTTPS webhook on new mail |
deleteAfter | open | false to keep inbox for debug |
Cypress
import { createMailAgentCypressTasks } from "@mailagent/qa/cypress";
// setupNodeEvents: on("task", createMailAgentCypressTasks())
Example: examples/cypress
CI secrets
Use a scoped key per pipeline — labelPrefix: "ci-" so a leaked
secret cannot touch other runs or team admin.
Scoped keys guide →
Debug after failure
Open Debug UI with
?inbox=INBOX_ID from @mailagent/qa errors.
GET /v1/inboxes?label=ci-12345
GET /v1/inboxes/:id/messages
GET /v1/inboxes/:id/callbacks
More guides
- Troubleshooting (timeout, OTP, webhook)
- Local dev with Mailpit
- Release checklist (smoke, migrate, publish)
- Scoped API keys
- Service presets matrix
- Callback cookbook
- QA team onboarding
QA roadmap (P0–P2): docs/QA-ROADMAP.md
Debug failed runs in the Debug console. Pilot access: hello@webmailagent.com.