Cypress
Test emails in Cypress
Your Cypress spec signs up with a fresh {teamId}.signup-…@zyntramail.com address, reads the email with cy.request, and visits the link in it. No plugin, no Gmail login, no mail catcher to run next to your app.
// cypress/e2e/signup.cy.js
describe("sign-up", () => {
it("confirms the email address", () => {
const team = Cypress.env("ZYNTRA_TEAM_ID");
const email = `${team}.signup-${Date.now()}@zyntramail.com`;
cy.visit("/signup");
cy.get("input[name=email]").type(email);
cy.get("input[name=password]").type("a-long-test-password");
cy.contains("button", "Create account").click();
// One request: waits up to 30 s on the server for the email.
cy.request({
url: "https://api.zyntra.app/emails/last",
qs: { email },
headers: { "X-API-Key": Cypress.env("ZYNTRA_API_KEY") },
timeout: 45000,
}).then(({ body }) => {
expect(body.subject).to.contain("Confirm");
const confirm = body.links.find((l) => l.url.includes("/verify"));
cy.visit(confirm.url);
});
cy.contains("Email confirmed").should("be.visible");
});
});# CI: Cypress reads CYPRESS_* variables into Cypress.env()
CYPRESS_ZYNTRA_TEAM_ID=f72c2547
CYPRESS_ZYNTRA_API_KEY=your-api-keyYou need two values from the dashboard: an API key (API Keys page) and your team ID (bottom-left of the sidebar). Keep both in environment variables. Getting started shows where they are.
What happens to the email
- Your app sends it through its usual email provider, to an address on zyntramail.com.
- A real mail server receives it and stores it for your team, with its links pulled out of the HTML and text and its attachments kept.
- Your test reads it with one API request. Mail to an address you did not create in the dashboard is deleted after 1 day.
Keep the API key out of the repo
Locally, put ZYNTRA_API_KEY and ZYNTRA_TEAM_ID in a git-ignored cypress.env.json. In CI, set CYPRESS_ZYNTRA_API_KEY and CYPRESS_ZYNTRA_TEAM_ID as secrets: Cypress reads every CYPRESS_ variable into Cypress.env().
When the same address gets a second email
/emails/last returns the newest email at once when the address already has mail. For a second email to the same address, check the subject in .then() and request again until it matches. The password-reset guide shows the pattern.
Limits to know about
- Cypress's default
responseTimeoutis 30 seconds, and/emails/lastcan wait up to 30 seconds. Pass a longertimeouttocy.request, as the example does. - If the link opens a different domain from your
baseUrl, run the steps on that page insidecy.origin(). - Every API call counts toward your plan's API requests: one read of an email is one request.
- Mail to these addresses is kept 1 day. Create a permanent inbox in the dashboard to keep it longer.
Questions
- Do I need a Cypress plugin or a task?
- No.
cy.requestcalls the API directly from the spec. - Why not a local mail catcher?
- A local catcher only sees mail your app sends to a local SMTP server. With Zyntra the email goes through your real provider on staging, so the test covers delivery as well as content.
- How do I find the link I need?
- Every email comes with
links: an array of{ url, text }found in its HTML and text. Pick the one whose URL or text matches.
Add the email step to your Cypress suite
Free plan, no setup: send to any address and read it back.
Start free