Cypress OTP email testing without a polling loop
Cypress cannot open a mailbox, so email steps usually mean a plugin, a task, and a retry loop. With an Inflovy inbox the test uses cy.request twice: once to create the inbox and once to wait for the email.
Last updated September 18, 2026
Who this is for
1Cypress suites covering signup, password reset, 2FA-by-email
2Teams that gave up on Gmail API plugins
3QA engineers who want the email visible after a failed run
In short
In Cypress, create an Inflovy inbox with cy.request, type inbox.emailAddress into the signup form, then cy.request the wait endpoint with timeout=30 and a Cypress timeout above it. The response carries the message and verificationCode, so the test types the code and continues. No plugin, no task, no sleep.
Why OTP steps are painful in Cypress
Cypress runs in the browser; reading a mailbox needs a Node task plus a mail client and credentials.
Gmail "+" aliases leak your real address, sites strip the "+", and Gmail silently de-duplicates identical test emails.
cy.wait(5000) hides the real problem and still flakes on a slow email.
Shared QA mailboxes mean the wrong test reads the wrong code.
The Inflovy way
cy.request to POST /v1/inboxes gives the test a private address.
cy.request to the wait endpoint returns the message as soon as it lands; set the Cypress timeout above the wait timeout.
verificationCode is on the response; type it and move on.
Every message is kept in the dashboard for the plan’s retention, so a red run can be inspected.
How to do it
- 1
Store the key
Add INFLOVY_API_KEY to cypress.env.json or CI secrets (Pro or Team workspace).
- 2
Create the inbox
cy.request({ method: "POST", url: api + "/inboxes", headers }) and keep body.id and body.emailAddress.
- 3
Run the flow
cy.visit("/signup"), type the address, submit.
- 4
Wait and type the code
cy.request the wait endpoint with timeout=30 and { timeout: 40000 }; type res.body.message.verificationCode.
const api = 'https://api.inflovy.com/v1';
const auth = { Authorization: `Bearer ${Cypress.env('INFLOVY_API_KEY')}` };
cy.request({ method: 'POST', url: `${api}/inboxes`, headers: auth }).then(({ body: inbox }) => {
cy.visit('/signup');
cy.get('#email').type(inbox.emailAddress);
cy.contains('Sign up').click();
cy.request({ url: `${api}/inboxes/${inbox.id}/messages/wait?timeout=30`, headers: auth, timeout: 40000 })
.its('body').then((res) => {
expect(res.status).to.eq('received');
cy.get('#code').type(res.message.verificationCode);
cy.contains('Verify').click();
});
});Needs an API key from Settings → API on a Pro or Team workspace. Full reference at /docs/api.
Gmail plugin versus an Inflovy inbox.
The moving parts each approach adds to a Cypress project.
| Need | Gmail / IMAP plugin | Inflovy |
|---|---|---|
| Setup | cypress.config task, OAuth client, refresh token, a plugin to maintain. | One environment variable with the API key. |
| Waiting | A recursive task that polls every few seconds. | One request that returns when the email arrives (1–60 s). |
| Extracting the code | Regex over the HTML body in the task. | res.body.message.verificationCode. |
| Isolation | One mailbox for the whole suite. | One inbox per test, deleted in afterEach. |
Questions people ask before switching.
Short answers. Inflovy is a receive-only test inbox for developers and QA teams; it does not replace your mailbox or send anything.
Why set a Cypress timeout above the wait timeout?
cy.request defaults to 30 seconds. wait?timeout=30 can legitimately take 30 seconds, so give cy.request a larger timeout (for example 40000 ms) or the test fails before the endpoint answers.
Does this work with magic links instead of codes?
Yes. message.links[] lists every link in the email; cy.visit the one you need. verificationCode is null when there is no code.
Can teammates see the emails my tests received?
Yes. Inboxes created through the API belong to the workspace and appear in the dashboard for every member, for the plan’s retention period.
Related guides
Nearby workflows and comparisons, so you can evaluate the right one without guessing.
Two cy.request calls. No plugin.
Copy the sample above into a spec, add an API key, and the OTP step stops flaking.