MailFlatDocs
Documentation/Test frameworks & CI/Cypress

Using MailFlat with Cypress

Read real verification emails from a Cypress test by moving the API calls into a Node task, where network access belongs.

How it fits together

Cypress specs run in the browser, so calling an external API straight from a test is awkward and CORS-bound. The supported way is a Node task: register the MailFlat calls in setupNodeEvents once, then use cy.task from any spec.

Install

Shell
npm i -D @mailflat/sdk

Register the tasks

setupNodeEvents runs in Node, so the API key never reaches the browser.
JavaScript
// cypress.config.js
const { defineConfig } = require("cypress");
const { MailFlat } = require("@mailflat/sdk");
 
const mf = new MailFlat({ apiKey: process.env.MAILFLAT_API_KEY });
 
module.exports = defineConfig({
e2e: {
setupNodeEvents(on) {
on("task", {
async createInbox() {
const inbox = await mf.create({
prefix: `cy-${Date.now().toString(36)}`,
retentionHours: 2,
});
return inbox.address;
},
async waitForOtp(address) {
return mf.inbox(address).waitForOtp({ timeout: 60000 });
},
async deleteInbox(address) {
await mf.inbox(address).delete();
return null;
},
});
},
},
});

The spec

cy.task returns a Cypress chainable, so it slots into the normal command chain.
JavaScript
// cypress/e2e/signup.cy.js
describe("signup", () => {
let address;
 
beforeEach(() => {
cy.task("createInbox").then((a) => { address = a; });
});
 
afterEach(() => cy.task("deleteInbox", address));
 
it("accepts a real verification code", () => {
cy.visit("/signup");
cy.get("#email").type(address);
cy.get("#submit").click();
 
cy.task("waitForOtp", address).then((otp) => {
cy.get("#code").type(otp);
});
cy.url().should("include", "/dashboard");
});
});

Worth knowing

A task must return a value or null — returning undefined makes Cypress fail with a confusing error.
Cypress default command timeout is 4 seconds; waiting happens inside the task, so it is not affected.