Skip to content

One-time codes and CAPTCHAs

Real sign-in flows ask for more than a password: a code from an authenticator app, a code or magic link by email, or a “prove you’re human” check. Plainwright handles the first two with the agent’s own tools, and recognises the third so you can fix the environment.

You meet Use
An authenticator-app code (Google Authenticator, 1Password, Authy…) a 2FA code secret
A code or link sent by email a connected email inbox (IMAP)
A code sent by SMS a test account with authenticator or email 2FA instead; there is no SMS tool
A CAPTCHA or bot check test keys or an allowlist in your test environment; Plainwright never solves them

Time-based one-time passwords are generated from a shared key. Give Plainwright that key and it produces the same 6-digit codes as the user’s authenticator app.

  1. Get the key. When you enable 2FA for the test account, the app shows a QR code. Most apps also offer a “can’t scan it?” text key (base32, like JBSWY3DPEHPK3PXP). If you only have the QR code, decode it: it contains an otpauth://totp/... URI, which works too.

  2. Store it as a secret. In Settings → Secrets, add a secret such as ADMIN_TOTP with the kind 2FA code and paste the key or the URI. Plainwright checks it can generate codes before saving.

  3. Use it in the test. Write the placeholder where the code goes:

    Sign in with {{secrets.ADMIN_EMAIL}} and {{secrets.ADMIN_PASSWORD}}.
    When asked for the authentication code, enter {{secrets.ADMIN_TOTP}}.
    Verify the dashboard loads.

When the agent types {{secrets.ADMIN_TOTP}}, Plainwright substitutes the current code at that moment, not the key. The key itself is treated like any secret value: it is never shown to the model and is masked in logs.

The agent also gets a get_totp_code tool whenever the project has a 2FA secret. It returns the current code and how many seconds it stays valid; if the code is about to roll over (under 3 seconds left), it waits for the next one. Smaller models sometimes find the tool easier than the placeholder; either works.

For sign-up confirmations, “magic link” logins and emailed one-time codes, connect a mailbox that receives the test account’s mail. The agent can then read the newest message and use the code or link in it.

  1. Get a test inbox your app can send to and that supports IMAP: a dedicated Gmail or Microsoft 365 account with an app password, a catch-all domain, or a self-hosted test mail server.

  2. Connect it. In Settings → Integrations, connect Test inbox with the IMAP host, port (993, or 143 for STARTTLS), folder (default INBOX), username and app password. Setup details are in Email inbox (IMAP).

  3. Ask for it in the test:

    Go to /signup and register with a new address of the form qa+<timestamp>@yourtestdomain.com.
    Read the latest email sent to that address with the subject "Confirm your email".
    Open the confirmation link from the email.
    Verify the page says the email address is confirmed.

The agent gets a read_latest_email tool when the project has an enabled inbox with a host, username and password:

Argument Default Description
to_contains (none) Only messages whose recipient contains this text, such as the address the test signed up with.
subject_contains (none) Only messages whose subject contains this text.
since_minutes 10 Only messages from the last N minutes.
wait_s 30 Keep checking, every 5 seconds, for up to N seconds (maximum 90) until a match arrives.

It returns the sender, recipient, subject and date, up to five likely codes (numbers next to words like “code” or “OTP” first, then standalone 4–8 digit numbers), the links in the message, and the text, cut at 4 KB. If no email arrives in time, the agent is told to report that as the failure.

The mailbox is opened read-only: messages are not marked as read or deleted. Only the newest 25 messages are inspected each time, so use a dedicated inbox and filter with to_contains.

Secret values that appear in an email (for example an address stored as a secret) are masked as •••• before the model sees them.

Plainwright never tries to solve a CAPTCHA. Solving them defeats their purpose and is unreliable. Instead, it detects when a run was stopped by one and tells you how to make your test environment test-friendly.

Detection runs on every failed or errored run (passing runs are never flagged). It looks at the agent’s summary, error and steps, and at the browser’s console and network log:

  • Words on the page or in the agent’s report are enough on their own: reCAPTCHA, hCaptcha, Turnstile, Cloudflare’s “Checking your browser”, “verify you are human”, “I’m not a robot” and similar.
  • Network traffic counts only when an actual challenge was served, such as a reCAPTCHA challenge frame or a Cloudflare challenge platform request. Many sites load invisible CAPTCHA scripts on every page; a script tag alone doesn’t count.
  • Negated phrases such as “no captcha appeared” are ignored.

It recognises Google reCAPTCHA, hCaptcha, Cloudflare Turnstile, Cloudflare bot protection, HUMAN (PerimeterX), Arkose Labs (FunCaptcha) and generic CAPTCHAs.

A detected run keeps its failed or error status and is additionally marked Blocked:

  • the run page names the blocker (for example “Blocked by Google reCAPTCHA.”), what was detected, and advice for that provider;
  • the run’s blocked_reason is captcha, with details in extras.blocked;
  • it is not retried automatically, since a retry would hit the same wall;
  • alerts include Blocked: CAPTCHA wall.
Protection What to do in staging or for test accounts
Google reCAPTCHA Use Google’s documented test site key 6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI, which always passes, with its matching test secret key.
hCaptcha Use the test site key 10000000-ffff-ffff-ffff-000000000001, which always passes, with hCaptcha’s test secret.
Cloudflare Turnstile Use the dummy site key 1x00000000000000000000AA (always passes) with Cloudflare’s matching dummy secret key.
Cloudflare bot protection Add a WAF or Bot Fight Mode skip rule for the outbound IP address of your Plainwright browser hosts, or test a host without it.
HUMAN (PerimeterX) Allowlist the runner’s IP address or user agent in staging.
Arkose Labs Use Arkose’s test mode or allowlist in staging.
Your own CAPTCHA Skip it for test accounts, or turn it off in staging.

These test keys are published by each provider for automated testing; check the provider’s documentation for the current values and the matching secret keys. They belong in staging only: never ship a test key to production.