Guides / How to test sign-up emails in Cypress

How to test sign-up emails in Cypress

Cypress can’t read an inbox on its own, so email steps are often skipped or stubbed. With cy.request and the BlinkInbox API you can receive the real email in the same test and assert on the code or link.

Set up the API key

Create a key in the dashboard and expose it to Cypress as an environment variable, for example in cypress.config.js or with CYPRESS_BLINKINBOX_API_KEY in CI.

cypress.config.js
const { defineConfig } = require("cypress");

module.exports = defineConfig({
  e2e: {
    env: { BLINKINBOX_API_KEY: process.env.BLINKINBOX_API_KEY },
    responseTimeout: 45000 // the inbox wait below can take up to 30s
  }
});

Custom commands

Two small commands keep the tests readable: one creates an inbox, the other waits for the code or link.

cypress/support/commands.js
const API = "https://api.blinkinbox.club/api/v1";
const auth = () => ({ Authorization: `Bearer ${Cypress.env("BLINKINBOX_API_KEY")}` });

Cypress.Commands.add("createInbox", (prefix = "cy") =>
  cy.request({ method: "POST", url: `${API}/inboxes`, headers: auth(), body: { prefix, ttlMinutes: 15 } })
    .its("body")
);

// Resolves with { otp, verificationLink, subject, from, ... }
Cypress.Commands.add("waitForCode", (inbox, timeout = 30) =>
  cy.request({ url: `${API}/inboxes/${inbox.id}/otp?timeout=${timeout}`, headers: auth(), timeout: (timeout + 15) * 1000 })
    .its("body")
);

The test

cypress/e2e/signup.cy.js
it("signs up and confirms the email", () => {
  cy.createInbox().then((inbox) => {
    cy.visit("/signup");
    cy.get("input[type=email]").type(inbox.address);
    cy.contains("button", "Create account").click();

    cy.waitForCode(inbox).then(({ otp }) => {
      cy.get("input[name=code]").type(otp);
      cy.contains("button", "Verify").click();
    });
    cy.contains("Welcome").should("be.visible");
  });
});

Confirmation links instead of codes

If your app emails a link, use verificationLink from the same response and visit it. Cypress visits must stay on your app’s origin; if the link goes through a tracking domain first, use cy.request to follow it, or assert on the link and visit its final URL.

link variant
cy.waitForCode(inbox).its("verificationLink").then((link) => cy.visit(link));

Questions

Why use the API instead of a local mail catcher?
Local catchers such as Mailpit work when your app’s SMTP settings point at them. BlinkInbox receives real email from the internet, so it also works against staging and production where your app sends through its real provider.
What if the email never arrives?
The request returns 404 with the code otp_not_found after the timeout, so the test fails with a clear reason instead of hanging.

Try it

A free Developer account gets you an API key and MCP access with 3 inboxes in any 24 hours.

More guides