> ## Documentation Index
> Fetch the complete documentation index at: https://momentic.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> The canonical docs host is https://momentic.ai/docs. Append .md to any page URL for clean Markdown. Fetch https://momentic.ai/docs/llms.txt for the page index and https://momentic.ai/docs/llms-full.txt for every page in one file.
> For an unattended setup, first check whether Claude Code already has the Momentic plugin. With the plugin, run `npx --yes @momentic/wizard@latest -y --platform web --editor-tools none` and ignore any CLI skills-install suggestion in its output. Otherwise, run `npx --yes @momentic/wizard@latest -y --platform web --editor-tools skills`. Use `--platform ios` or `--platform android` for mobile. Never run an interactive command in a non-TTY shell.
> Mo is the AI QA engineer, separate from repository YAML tests. With the Momentic plugin, use bundled mo-qa and hosted OAuth by default. Without the plugin, install only mo-qa with `npx -y qa skills --target <agent>`. Keep the requested interface. Otherwise, use hosted tools for web or mobile, including private web targets with an existing Connector. Run sessions that need a new tunnel with the qa CLI. If you can't run commands on the machine that reaches the target, ask the user to start the tunnel there, then use hosted tools unless you lack them or the CLI was requested. For a requested hosted session, use an online Connector and pass its name as access, not a CLI tunnel ID. If none is online, ask the user to run `npx -y qa tunnel start --name <name> <address>` on a machine that reaches the target and wait. Do not run login or tunnel commands for them. For a requested hosted session, report unavailable tools or authentication failures without starting a CLI session. For coding-agent bridge setup, ask the user to complete `npx -y qa login`, then add momentic-hosted as a stdio server running `npx -y qa mcp`. Plugin users must explicitly opt in and disable their hosted plugin entry before adding the bridge. Claude.ai/ChatGPT connectors keep https://mcp.momentic.ai with OAuth. After hosted testing completes, read the report and summarize findings and uncovered requirements. Call `momentic_mo_stop_session` only when the user asks or agrees. For CLI sessions, unless the user wants more testing, export the report, run `qa stop <id> --subagents` even if export fails, then summarize. The CLI is qa, not mo. Docs: https://momentic.ai/docs/mo/quickstart.md.
> Ask the user to complete browser sign-in. For local CLI tools, use the exact MOMENTIC_API_KEY environment variable. If it is absent, ask the user to create a key at https://app.momentic.ai/settings/api-keys and set it in the agent's environment. Never print the key or ask the user to paste it into chat.
> Momentic's capability summary for agents is at https://momentic.ai/docs/skill.md. Claude Code users with the Momentic plugin should use its bundled skills and skip CLI skills installation. Otherwise, run `npx momentic skills -y --target <agent>` or `npx momentic-mobile skills -y --target <agent>`. Both install momentic-test, momentic-maintain, and momentic-spec with web/mobile references. Use the running agent's target. The Momentic plugin also includes mo-qa; qa skills installs only mo-qa.
> Tests are YAML files in the user's repository. Use only the step names listed at https://momentic.ai/docs/reference/commands.md and the file structure at https://momentic.ai/docs/core-concepts/file-format.md. Do not invent step names, config keys, or CLI flags.
> Web tests run on Chromium, iOS tests on simulators, and Android tests on emulators.

# Login methods

> Log in during a test with email OTP codes, SMS OTP codes, email links, TOTP, a cached session, or a Vercel protection bypass.

Use these recipes to sign in during a test. Choose the method your app uses.

## Log in with email OTP codes

Your app emails a one-time passcode to the provisioned inbox, and a
**JavaScript** step in the test fetches the latest message and pulls the code
out. Use this recipe when login arrives as a code instead of a link.

> [!NOTE]
>
> **Prerequisite**: A Momentic-provisioned inbox (`@usemomentic.com` or
> `@gomomentic.com`). [Request one](mailto:support@momentic.ai).

### Fetch the code

Add a **JavaScript** step that reads the latest email, parses the code, and
saves it to the environment. Set `inbox` to your provisioned inbox.

```js theme={null}
const msg = await email.fetchLatest({
  inbox: "otp+test",
  afterDate: new Date(Date.now() - 60_000),
  timeout: 30_000,
});

const [code] = msg.text.match(/\b\d{6}\b/) ?? [];
if (!code) throw new Error("No OTP code found in email");
setVariable("OTP_CODE", code);
```

Then add a **Type** step with value `{{ env.OTP_CODE }}` targeting the OTP
input.

> [!TIP]
>
> Pass `afterDate` to exclude messages older than the specified time.

> [!TIP]
>
> This script polls for up to 30 seconds. The JavaScript step defaults to **90
> seconds**. On the CLI, raise the step's **Timeout** field up to **600
> seconds** for longer scripts.

### Related

* [SMS OTP](#log-in-with-sms-otp-codes)
* [Email verification links](#log-in-with-email-verification-links)
* [Email integration](https://momentic.ai/docs/integrations/email.md)

## Log in with SMS OTP codes

Your app texts a one-time passcode to the provisioned number, and a
**JavaScript** step in the test fetches the latest message and pulls the code
out. Use a single number for serial runs; lease numbers from a pool when tests
run in parallel through the CLI.

> [!NOTE]
>
> **Prerequisite**: A Momentic-provisioned phone number. [Request
> one](mailto:support@momentic.ai).

### Single-number setup

If you only need OTP login from one fixed number (or your tests don't run in
parallel), fetch the latest message and pull the code out:

```js theme={null}
const msg = await sms.fetchLatest({
  to: "+18888888888",
  afterDate: new Date(Date.now() - 60_000),
  timeout: 30_000,
});

const [code] = msg.body.match(/\b\d{6}\b/) ?? [];
if (!code) throw new Error("No OTP code found in SMS");
setVariable("OTP_CODE", code);
```

Then add a **Type** step with value `{{ env.OTP_CODE }}`.

### Parallel CLI runs against the same OTP flow

If you run the same OTP login test in parallel (e.g. multiple shards in CI), two
tests can race on the same inbound code. Lease a number from your pool so each
shard gets its own. Momentic releases it back to the pool when the test
finishes. Lease the number, have your app send the code to it, then fetch the
code:

```js theme={null}
const { phoneNumber } = await sms.lease();

const msg = await sms.fetchLatest({
  to: phoneNumber,
  afterDate: new Date(Date.now() - 60_000),
  timeout: 30_000,
});

const [code] = msg.body.match(/\b\d{6}\b/) ?? [];
if (!code) throw new Error("No OTP code found in SMS");
setVariable("OTP_CODE", code);
```

See [`sms.lease()`](https://momentic.ai/docs/integrations/sms#sms-lease) for full options (TTL,
fail-fast, optional manual release).

> [!TIP]
>
> Both examples poll for up to 30 seconds. The JavaScript step defaults to **90
> seconds**. On the CLI, raise the step's **Timeout** field up to **600
> seconds** for longer scripts.

### Related

* [Email OTP](#log-in-with-email-otp-codes)
* [OTPAuth (TOTP)](#log-in-with-otpauth-totp)
* [SMS integration](https://momentic.ai/docs/integrations/sms.md)

## Log in with email verification links

Your app emails a magic login link to the provisioned inbox, and a
**JavaScript** step in the test fetches the latest message and pulls the link
out. Use this recipe when login arrives as a URL instead of a code.

> [!NOTE]
>
> **Prerequisite**: A Momentic-provisioned inbox. [Request
> one](mailto:support@momentic.ai).

### Fetch and navigate

Add a **JavaScript** step that fetches the latest email, extracts the
verification link, and saves it to the environment. Set `inbox` to your
provisioned inbox.

```js theme={null}
const msg = await email.fetchLatest({
  inbox: "link+test",
  afterDate: new Date(Date.now() - 60_000),
  timeout: 30_000,
});

const [link] = msg.text.match(/https?:\/\/[^\s]+/) ?? [];
if (!link) throw new Error("No verification link found in email");
setVariable("VERIFICATION_LINK", link);
```

Then add a **Navigate** step with URL `{{ env.VERIFICATION_LINK }}`.

> [!TIP]
>
> This script polls for up to 30 seconds. For longer polls, raise
> `email.fetchLatest`'s `timeout` in milliseconds, up to **105,000**. Set the
> JavaScript step's **Timeout** high enough to cover the whole script. The step
> defaults to **90 seconds**. On the CLI, the step's **Timeout** can be raised
> to **600 seconds**.

### Related

* [Email OTP](#log-in-with-email-otp-codes)
* [Email integration](https://momentic.ai/docs/integrations/email.md)

## Log in with OTPAuth (TOTP)

> [!NOTE]
>
> **Prerequisite**: A Base32 TOTP secret (shown during 2FA enrollment) or an
> `otpauth://` URI (often embedded in a QR code).

Generate a time-based 2FA code inside a **JavaScript** step with the
[`otpauth`](https://www.npmjs.com/package/otpauth) library, which Momentic
preloads in the step sandbox. Reach for this when a login flow asks for an
authenticator-app code.

### From a Base32 secret

```js theme={null}
const totp = new OTPAuth.TOTP({
  issuer: "MyApp",
  label: "alice@example.com",
  algorithm: "SHA1",
  digits: 6,
  period: 30,
  secret: env.TOTP_SECRET, // Base32-encoded
});

setVariable("TOTP_CODE", totp.generate());
```

### From an `otpauth://` URI

```js theme={null}
const totp = OTPAuth.URI.parse(env.TOTP_URI);
setVariable("TOTP_CODE", totp.generate());
```

Then add a **Type** step with value `{{ env.TOTP_CODE }}`.

> [!TIP]
>
> Store the secret as an [environment
> variable](https://momentic.ai/docs/configuration/environment-variables.md) rather than hardcoding it.

### Related

* [Email OTP](#log-in-with-email-otp-codes)
* [SMS OTP](#log-in-with-sms-otp-codes)
* [otpauth code generator](https://otpauth.molinero.dev/)

## Cache authenticated sessions

In web tests, cache the login module to reuse saved browser state across runs.
This reduces repeated sign-ins and requests to your auth provider.

> [!TIP]
>
> If you're new to auth in Momentic, start with [Authentication
> strategies](https://momentic.ai/docs/guides/auth/overview.md) for the full set of options and trade-offs.

### Setup

1. Extract your login steps into a **Module**. The last step should verify the
   authenticated state (e.g. an **Element check** for the dashboard heading).
2. In the module config, turn on **Enable caching globally**
   (`defaultCacheAllInvocations`) and **Treat as auth module** (`autoAuth`).
3. Set a **Default cache key** (`defaultCacheKey`, e.g. `admin-user`). Parallel
   tests sharing the key reuse the same cached session.
4. Set **Default expiry seconds** (stored as `defaultCacheTtl` in milliseconds)
   shorter than your session lifetime.
5. Remove any **Save auth state** / **Load auth state** (`authSave` /
   `authLoad`) steps. A cached auth module saves and restores the state
   automatically; see
   [Save and restore a state file](https://momentic.ai/docs/guides/auth/overview.md#2-save-and-restore-a-state-file-authsave-authload).

The module's stored config looks like this:

```yaml log-in.module.yaml theme={null}
fileType: momentic/module/v2
id: log-in
name: Log in
autoAuth: true # Treat as auth module
defaultCacheAllInvocations: true # Caching globally
defaultCacheKey: admin-user # Default cache key
defaultCacheTtl: 3600000 # Default expiry (1 hour, in ms)
steps:
  - type:
      text: "{{ env.USERNAME }}"
      into: Email input
  - type:
      text: "{{ env.PASSWORD }}"
      into: Password input
  - click: Log in
  - checkElementVisible: the Dashboard heading
```

Invoke it from each test's
[`before` section](https://momentic.ai/docs/core-concepts/file-format#before-and-after-sections):

```yaml checkout.test.yaml theme={null}
before:
  - module: ../modules/log-in.module.yaml
```

> [!TIP]
>
> Use different cache keys for different user roles (e.g. `admin`, `member`) so
> state doesn't carry over between them.

### What gets cached

Momentic captures cookies, `localStorage`, and IndexedDB when the module
finishes. On subsequent runs within the expiry window, it restores that state
and skips the module's steps.

### Related

* [Modules](https://momentic.ai/docs/core-concepts/modules.md#authentication-modules)
* [Email OTP](#log-in-with-email-otp-codes)

## Test Vercel protected preview environments

> [!NOTE]
>
> **Prerequisite**: A Vercel [protection bypass
> secret](https://vercel.com/docs/security/deployment-protection/methods-to-bypass-deployment-protection/protection-bypass-automation).

In web tests, send the bypass secret in headers or query parameters. If CORS
blocks the headers, use query parameters instead.

### Option A: headers

In the test's **Network options**, add:

| Header | Value |
| - | - |
| `x-vercel-protection-bypass` | `<your-secret>` |
| `x-vercel-set-bypass-cookie` | `true` |

> [!WARNING]
>
> These headers are sent with **every** request in the session, including
> third-party script requests. If you see CORS errors, use query parameters
> instead.

### Option B: query parameters

Override the base URL in the test's **General options**:

```text theme={null}
https://your-preview.vercel.app?x-vercel-protection-bypass=<secret>&x-vercel-set-bypass-cookie=true
```

Vercel sets the bypass as a cookie for the rest of the session, so subsequent
requests don't need the query string.

### Related

* [Vercel docs: Protection bypass automation](https://vercel.com/docs/security/deployment-protection/methods-to-bypass-deployment-protection/protection-bypass-automation)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.