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

# Browser Automation

> Agent browser testing that's 10x cheaper and 250% faster

<Warning>
  **Beta Feature** — Browser automation is currently in beta. Please report any issues to [founders@morphllm.com](mailto:founders@morphllm.com).
</Warning>

<img src="https://mintcdn.com/morph-555d6c14/6bm3KIDcGh3tlVrb/images/browser-testing.webp?fit=max&auto=format&n=6bm3KIDcGh3tlVrb&q=85&s=3146cd49f725fc53a65e4d8fab1098ae" alt="Browser Testing" width="100%" style={{ borderRadius: 8, marginBottom: 24 }} data-path="images/browser-testing.webp" />

Test your web apps with natural language. "Test checkout flow" or "Verify mobile menu works"—Morph runs it with a real browser.

## Quick Start

Install and run your first browser test in 30 seconds:

<CodeGroup>
  ```typescript Run theme={null}
  import { MorphClient } from '@morphllm/morphsdk';

  const morph = new MorphClient({ apiKey: "YOUR_API_KEY" });

  const result = await morph.browser.execute({
    task: "Verify the homepage loads and has a working navigation menu",
    url: "https://example.com"
  });

  console.log(result.success ? '✅ Passed' : '❌ Failed');
  console.log(result.result);
  ```
</CodeGroup>

<Note>
  Use remote URLs only (Vercel previews, e2b.dev tunnels, staging). Cannot access localhost—[see why](#remote-urls-only).
</Note>

## Why Morph Browser?

Built specifically for testing AI-generated code changes:

* **10x cheaper** than Claude Sonnet ($0.30 vs $3.00 per 1M input tokens)
* **250% faster** inference (200 tok/s vs 60 tok/s)
* **Live session streaming** - Watch tests execute in real-time
* **Rich debugging** - URLs, actions, errors auto-captured
* **Agent self-assessment** - Real success detection, not just completion

<Accordion title="Model comparison" icon="chart-line">
  | Model                      | Input      | Output     | Speed         | Best For                     |
  | -------------------------- | ---------- | ---------- | ------------- | ---------------------------- |
  | **morph-computer-use-v1**  | **\$0.30** | **\$1.50** | **200 tok/s** | Browser automation (default) |
  | **morph-computer-use-v0**  | **\$0.30** | **\$1.50** | **200 tok/s** | Browser automation (legacy)  |
  | **gemini-3-flash-preview** | \$0.50     | \$3.00     | 90 tok/s      | External API (Google)        |
  | claude-sonnet-4.5          | \$3.00     | \$15.00    | 60 tok/s      | General reasoning            |

  Per 1M tokens. Morph models are optimized for browser automation and testing.
</Accordion>

## Common Patterns

### Basic Testing

Test any web flow with natural language:

```typescript theme={null}
const result = await morph.browser.execute({
  task: "Test the login flow with test@example.com / password123",
  url: "https://myapp.vercel.app"
});

if (result.success) {
  console.log('✅ Test passed');
} else {
  console.error('❌ Test failed:', result.error);
  console.log('Failed at:', result.urls[result.urls.length - 1]);
}
```

### Watch Tests Live

Get live URL immediately, watch execution in real-time:

```typescript theme={null}
// Returns in ~2s with live URL
const task = await morph.browser.createTask({
  task: "Test checkout flow with cart persistence",
  url: "https://shop.example.com"
});

console.log('👁️  Watch live:', task.debugUrl);
// Open URL in browser to watch from start

// Wait for completion
const result = await task.complete();
```

### Test Responsive Layouts

Use built-in tools to test different screen sizes:

```typescript theme={null}
const result = await morph.browser.execute({
  task: "Resize to mobile (375x667) and verify the hamburger menu appears",
  url: "https://myapp.com"
});
```

### Site Authentication

Test sites that require login with the `auth` parameter:

```typescript theme={null}
const result = await morph.browser.execute({
  task: "Log in with x_user and x_pass, then verify the dashboard loads",
  url: "https://myapp.com/login",
  auth: {
    username: "test@example.com",
    password: "secret123"
  }
});
```

The agent sees placeholders (`x_user`, `x_pass`) in the task. Real values are injected when filling forms.

<Accordion title="Per-domain credentials" icon="globe">
  Scope credentials to specific domains:

  ```typescript theme={null}
  const result = await morph.browser.execute({
    task: "Log in and test the integration",
    url: "https://staging.myapp.com",
    auth: {
      "https://*.staging.myapp.com": { username: "staging-user", password: "staging-pass" },
      "https://api.external.com": { username: "api-user", password: "api-key" }
    }
  });
  ```
</Accordion>

<Accordion title="Cookie-based auth" icon="cookie">
  Skip login entirely with pre-authenticated cookies:

  ```typescript theme={null}
  const result = await morph.browser.execute({
    task: "Verify the admin dashboard loads",
    url: "https://myapp.com/admin",
    auth: {
      cookies: [
        { name: "session", value: "abc123...", domain: "myapp.com" },
        { name: "auth_token", value: "xyz789...", domain: "myapp.com" }
      ]
    }
  });
  ```

  Export cookies from your browser's DevTools or use a cookie manager.
</Accordion>

### Browser Profiles (Persistent Logins)

Profiles let you sign in once (manually) and reuse that authenticated state across test runs. Profiles are scoped to a repo.

#### List available repos

```typescript theme={null}
const repos = await morph.browser.profiles.listRepos();
// Pick by full name
const repo = repos.find(r => r.repoFullName === 'owner/repo');
if (!repo) throw new Error('Repo not found');
```

#### Create a profile (returns a live URL)

```typescript theme={null}
const setup = await morph.browser.profiles.createProfile({
  name: 'Staging',
  repoId: repo.repoId
});

console.log('Open to sign in:', setup.session.debugUrl);
// User signs in, then persist the state:
await setup.save();

console.log('Profile ID:', setup.profile.id);
```

#### Use a profile in browser tasks

```typescript theme={null}
const result = await morph.browser.execute({
  task: "Go to the dashboard and verify it loads",
  url: "https://staging.myapp.com",
  profileId: setup.profile.id
});
```

#### Update a profile (add more logins)

```typescript theme={null}
const edit = await morph.browser.profiles.updateProfile(setup.profile.id);
console.log('Open to sign in:', edit.session.debugUrl);
await edit.save();
```

#### List and delete profiles

```typescript theme={null}
const profiles = await morph.browser.profiles.listProfiles(repo.repoId);
await morph.browser.profiles.deleteProfile(profiles[0].id);
```

### Debug with Recordings

Enable video and logs for failed tests:

```typescript theme={null}
const result = await morph.browser.execute({
  task: "Complete the checkout flow",
  url: "https://shop.example.com",
  recordVideo: true,
  maxSteps: 20
});

if (!result.success && result.recordingId) {
  const rec = await morph.browser.getRecording(result.recordingId);
  console.log('📹 Video:', rec.videoUrl);
  console.log('🌐 Network:', rec.networkUrl);
  console.log('📊 Console:', rec.consoleUrl);
}
```

### CI/CD Integration

Track tests with reference IDs:

```typescript theme={null}
const result = await morph.browser.execute({
  task: "Verify homepage loads correctly",
  url: process.env.PREVIEW_URL,
  externalId: process.env.GITHUB_PR_NUMBER,
  repoFullName: process.env.GITHUB_REPOSITORY, // "owner/repo"
  commitId: process.env.GITHUB_SHA,
  recordVideo: true
});

// Link results back to your PR/build system
```

## Live Sessions

Stream browser execution in real-time at 25 fps. Perfect for debugging, monitoring, or human-in-the-loop workflows.

### Basic Live Streaming

```typescript theme={null}
const task = await morph.browser.createTask({
  task: "Test the payment flow",
  url: "https://myapp.com"
});

// Available immediately (~2s)
console.log('Watch live:', task.debugUrl);
// Share with team, embed in dashboard, or monitor yourself

const result = await task.complete();
```

### Embed in Dashboard

```typescript theme={null}
const task = await morph.browser.createTask({
  task: "Monitor production health check",
  url: "https://myapp.com"
});

// Read-only viewer
const viewer = task.getLiveIframe?.('readonly');
document.getElementById('monitor').innerHTML = viewer;

// Interactive control (human takeover)
const controller = task.getLiveIframe?.({
  interactive: true,
  height: '800px',
  width: '100%'
});
```

<Warning>
  Live URLs are **unauthenticated**. Anyone with the URL can view (and control if `interactive=true`) the session. Add your own auth for production use.
</Warning>

## Built-in Tools

The agent automatically uses tools when needed based on your task description.

### Responsive Testing

Test layouts at different screen sizes—just mention dimensions in your task:

```typescript theme={null}
const result = await morph.browser.execute({
  task: "Resize to iPhone 12 size (390x844) and verify the mobile nav works",
  url: "https://myapp.com"
});
```

**Common dimensions:**

* **Desktop**: 1920x1080, 1440x900, 1280x720
* **Tablet**: 1024x768 (iPad), 768x1024 (iPad Portrait)
* **Mobile**: 375x667 (iPhone SE), 414x896 (iPhone 11), 390x844 (iPhone 12/13)

<Info>
  More tools coming: file uploads, API interactions, human-in-the-loop. All tools are globally available.
</Info>

## Recordings

Enable `recordVideo: true` to capture full session details:

```typescript theme={null}
const result = await morph.browser.execute({
  task: "Test the checkout flow",
  url: "https://shop.example.com",
  recordVideo: true
});

if (result.recordingId) {
  const recording = await morph.browser.getRecording(result.recordingId);
  
  console.log('📹 Video:', recording.videoUrl);       // MP4/WebM playback
  console.log('🔄 Interactive:', recording.replayUrl); // rrweb DOM replay
  console.log('🌐 Network:', recording.networkUrl);    // All HTTP requests
  console.log('📊 Console:', recording.consoleUrl);    // JS console logs
}
```

**What you get:**

* Video file (MP4/WebM)
* Interactive DOM replay (rrweb)
* Network logs (all requests/responses)
* Console logs (JS output)
* Screenshots (per step)

### Get Animated WebP

Convert recordings to animated WebP for embedding in PRs or dashboards:

```typescript theme={null}
const recording = await morph.browser.getRecording(result.recordingId);

const { webpUrl } = await recording.getWebp({
  width: 780,
  fps: 10,
  quality: 65,
  maxDuration: 15
});

console.log(`![Recording](${webpUrl})`);
```

**With file size budget:**

```typescript theme={null}
const { webpUrl } = await recording.getWebp({
  maxSizeMb: 2.0  // Output guaranteed ≤ 2MB
});
```

When `maxSizeMb` is set, the output is guaranteed to stay under that size. For long recordings with tight budgets, the video is automatically sped up to fit.

Cached in S3—subsequent calls return instantly.

### Get Errors with Screenshots

```typescript theme={null}
const recording = await morph.browser.getRecording(result.recordingId);

const { errors, totalErrors } = await recording.getErrors();

errors.forEach(err => {
  console.log(`[${err.type}] ${err.message}`);
  if (err.screenshotUrl) {
    console.log(`Screenshot: ${err.screenshotUrl}`);
  }
});
```

## Choosing a Model

Specify which model to use for browser automation with the `model` parameter:

```typescript theme={null}
const result = await morph.browser.execute({
  task: "Test the checkout flow",
  url: "https://myapp.com",
  model: "morph-computer-use-v1"  // or "morph-computer-use-v0", "gemini-3-flash-preview"
});
```

### Available Models

| Model                    | Description                                                       |
| ------------------------ | ----------------------------------------------------------------- |
| `morph-computer-use-v1`  | **Default.** Latest Morph model, optimized for browser automation |
| `morph-computer-use-v0`  | Legacy Morph model, stable fallback                               |
| `gemini-3-flash-preview` | Google Gemini 3 Flash, uses external Google API                   |

<Info>
  `morph-computer-use-v1` is the default and recommended for most use cases. Use `gemini-3-flash-preview` if you prefer Google's Gemini model (requires `GOOGLE_API_KEY` on the server).
</Info>

## API Reference

### execute()

Synchronous execution—waits for completion (\~30-60s).

```typescript theme={null}
const result = await morph.browser.execute({
  // Required
  task: string,              // Natural language description
  
  // Optional
  url?: string,              // Starting URL
  model?: string,            // See "Choosing a Model" below
  maxSteps?: number,         // 1-50 (default: 10)
  recordVideo?: boolean,     // Enable recording (default: false)
  viewportWidth?: number,    // Browser width (default: 1280)
  viewportHeight?: number,   // Browser height (default: 720)
  externalId?: string,       // Your tracking ID
  repoFullName?: string,     // Repository full name ("owner/repo")
  repoId?: string,           // Repository UUID (if you already have it)
  commitId?: string,         // Commit/version identifier
  
  // Authentication
  auth?: {                   // Global credentials
    username?: string,
    password?: string,
    cookies?: any            // Cookie array for pre-authenticated sessions
  } | Record<string, AuthCredentials>  // Per-domain credentials
});
```

**Returns:**

```typescript theme={null}
{
  success: boolean,          // Agent self-assessment
  result?: string,           // Task result/findings
  error?: string,            // Error message if failed
  stepsTaken: number,        // Actions executed
  executionTimeMs: number,
  
  // Debugging
  urls: (string | null)[],   // All URLs visited
  actionNames: string[],     // All actions taken
  errors: (string | null)[], // Per-step errors
  
  // Recording
  recordingId?: string,
  recordingStatus?: string,  // PENDING | RUNNING | PROCESSING | COMPLETED | ERROR
  debugUrl?: string          // Live session URL
}
```

### createTask()

Async execution—returns immediately with live URL (\~2s).

```typescript theme={null}
const task = await morph.browser.createTask({
  // Same parameters as execute()
  task: "Test the login flow",
  url: "https://myapp.com"
});

console.log(task.debugUrl);    // Watch live
const result = await task.complete(); // Wait for completion
```

## Writing Good Tasks

Be specific—the agent performs better with clear instructions.

**✅ Good (specific):**

* "Navigate to pricing and verify all three tiers display"
* "Add item to cart, go to checkout, verify subtotal matches"
* "Test login with [test@example.com](mailto:test@example.com) / password123"

**❌ Bad (vague):**

* "test the app"
* "check if everything works"
* "make sure there are no bugs"

**Choosing maxSteps:**

* **5-10 steps**: Simple navigation and verification
* **10-15 steps**: Form submissions, multi-step flows
* **15-30 steps**: Complex journeys (checkout, onboarding)

## Remote URLs Only

The browser runs on our infrastructure—it **cannot access localhost**.

**✅ Use these:**

* `https://3000-abc.e2b.dev` (e2b tunnel)
* `https://preview-abc.vercel.app` (Vercel preview)
* `https://staging.myapp.com` (staging environment)

**❌ Don't use:**

* `localhost:3000`
* `127.0.0.1`
* `192.168.x.x` or any local IPs

<Tip>
  Tunnel local servers with [e2b.dev](https://e2b.dev), [ngrok](https://ngrok.com), or deploy to Vercel for instant preview URLs.
</Tip>

## Python SDK

Morph is **OpenAI-compatible**. Use with browser-use Python SDK:

```python theme={null}
from browser_use import Agent, ChatOpenAI
import os

llm = ChatOpenAI(
    model="morph-computer-use-v1",  # or "morph-computer-use-v0", "gemini-3-flash-preview"
    api_key="YOUR_API_KEY",
    base_url="https://api.morphllm.com/v1"
)

agent = Agent(
    task="Navigate to amazon.com and get the first product title",
    llm=llm
)

result = await agent.run(max_steps=10)
```

Get browser-use's Python interface with Morph's 10x cheaper pricing. See the [full guide](/guides/browser-use).

## Setting Up Test Accounts

<Note>
  **Use a separate development environment** for browser automation testing. This lets you freely disable 2FA, bot protection, and other security features without affecting production.
</Note>

Most auth providers (Clerk, Auth0, Supabase, Firebase) support creating separate development/staging instances. Create your test accounts there, where you can:

* **Disable 2FA/MFA** on test accounts
* **Turn off bot protection** and CAPTCHA
* **Skip email verification**
* **Add preview URL wildcards** to allowed origins (e.g., `https://*.vercel.app`)

### Quick Setup by Provider

<AccordionGroup>
  <Accordion title="Clerk" icon="key">
    1. Create a **Development instance** in Clerk Dashboard
    2. Create test user: `Dashboard → Users → Create user`
    3. Disable bot protection: `Configure → Attack Protection → Bot Protection`
    4. Add allowed origins: `Configure → Paths` → add `https://*.vercel.app`
    5. Use development keys in preview environment:

    ```bash theme={null}
    # Vercel Preview environment variables
    NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_xxx
    CLERK_SECRET_KEY=sk_test_xxx
    ```
  </Accordion>

  <Accordion title="Auth0" icon="key">
    1. Create a **Development tenant** in Auth0
    2. Create test user: `User Management → Users → Create User`
    3. Disable MFA: `Security → Multi-factor Auth` → disable or add rule to skip for test emails
    4. Add allowed origins: `Applications → Settings → Allowed Web Origins`
    5. Use development tenant in preview:

    ```bash theme={null}
    AUTH0_ISSUER_BASE_URL=https://dev-xxx.auth0.com
    AUTH0_CLIENT_ID=dev_client_id
    ```
  </Accordion>

  <Accordion title="Supabase" icon="key">
    1. Create a **separate Supabase project** for staging
    2. Create test user via Dashboard or SQL
    3. Disable email confirmation: `Authentication → Providers → Email`
    4. Disable CAPTCHA: `Authentication → Settings`
    5. Add redirect URLs: `Authentication → URL Configuration` → add `https://*.vercel.app/**`

    ```bash theme={null}
    # Vercel Preview environment
    NEXT_PUBLIC_SUPABASE_URL=https://xxx-staging.supabase.co
    NEXT_PUBLIC_SUPABASE_ANON_KEY=staging_anon_key
    ```
  </Accordion>

  <Accordion title="Firebase" icon="key">
    1. Create a **separate Firebase project** for development
    2. Create test user and mark email as verified
    3. Add authorized domains: `Authentication → Settings → Authorized domains`
    4. For CI/CD, consider using Firebase Auth Emulator

    ```bash theme={null}
    FIREBASE_AUTH_EMULATOR_HOST=localhost:9099
    ```
  </Accordion>
</AccordionGroup>

### Vercel Environment Variables

Configure different auth credentials per environment:

```bash theme={null}
# Vercel Dashboard → Settings → Environment Variables

# Production: Use production auth instance
CLERK_SECRET_KEY=sk_live_xxx  (Production only)

# Preview: Use development auth instance
CLERK_SECRET_KEY=sk_test_xxx  (Preview only)
TEST_USER_EMAIL=test@yourdomain.com  (Preview only)
TEST_USER_PASSWORD=xxx  (Preview only)
```

Then use with Morph:

```typescript theme={null}
const result = await morph.browser.execute({
  task: "Log in with x_user and x_pass, verify dashboard",
  url: "https://preview-abc.vercel.app/login",
  auth: {
    username: process.env.TEST_USER_EMAIL,
    password: process.env.TEST_USER_PASSWORD
  }
});
```

## FAQ

<AccordionGroup>
  <Accordion title="Why can't I access localhost?" icon="network-wired">
    The browser runs on our servers, not your machine.

    **Won't work:** `localhost:3000`, `127.0.0.1`, `192.168.x.x`

    **Solutions:**

    * Deploy to Vercel/Netlify for instant preview URLs
    * Tunnel with [e2b.dev](https://e2b.dev) or [ngrok](https://ngrok.com)
    * Use a staging environment
  </Accordion>

  <Accordion title="execute() vs createTask()—which should I use?" icon="circle-question">
    **`execute()`** - Waits for completion (\~30-60s)

    ```typescript theme={null}
    const result = await morph.browser.execute({
      task: "Test login",
      url: "https://app.com"
    });
    // Returns complete result
    ```

    **`createTask()`** - Returns immediately (\~2s)

    ```typescript theme={null}
    const task = await morph.browser.createTask({
      task: "Test login",
      url: "https://app.com"
    });
    console.log(task.debugUrl); // Watch live!
    const result = await task.complete();
    ```

    Use `createTask()` to watch from the start or get the live URL for monitoring/debugging.
  </Accordion>

  <Accordion title="How do I debug failed tests?" icon="bug">
    Every task returns rich debugging data automatically:

    ```typescript theme={null}
    const result = await morph.browser.execute({
      task: "Test checkout",
      url: "https://myapp.com",
      recordVideo: true,
      maxSteps: 20
    });

    if (!result.success) {
      // Always available
      console.log('Error:', result.error);
      console.log('URLs:', result.urls);
      console.log('Actions:', result.actionNames);
      console.log('Step errors:', result.errors.filter(e => e));
      
      // With recording
      if (result.recordingId) {
        const rec = await morph.browser.getRecording(result.recordingId);
        console.log('Video:', rec.videoUrl);
        console.log('Network:', rec.networkUrl);
      }
    }
    ```

    You get: agent self-assessment, all URLs visited, every action taken, per-step errors, and optional video/logs.
  </Accordion>

  <Accordion title="What counts as a step?" icon="shoe-prints">
    Each browser action = 1 step:

    * Click → 1 step
    * Fill form → 1 step
    * Navigate → 1 step
    * Wait → 1 step

    **Sizing:**

    * **5-10 steps**: Simple tasks
    * **10-15 steps**: Forms, multi-step
    * **15-30 steps**: Complex flows

    Hit the limit? Increase `maxSteps` or simplify the task.
  </Accordion>

  <Accordion title="Can I get structured output?" icon="table">
    Yes—use Zod schemas with `createTask()`:

    ```typescript theme={null}
    import { z } from 'zod';

    const task = await morph.browser.createTask({
      task: "Get the product price",
      url: "https://store.com/product",
      schema: z.object({
        price: z.number(),
        inStock: z.boolean()
      })
    });

    const result = await task.complete();
    console.log(result.parsed); // { price: 29.99, inStock: true }
    ```
  </Accordion>

  <Accordion title="What's in recordings?" icon="video">
    Enable `recordVideo: true` to get:

    * **Video** (MP4/WebM) - Visual playback
    * **rrweb replay** - Interactive DOM timeline
    * **Network logs** - All HTTP requests
    * **Console logs** - JS console output
    * **Screenshots** - Per step

    Stored in S3 with 7-day presigned URLs.
  </Accordion>

  <Accordion title="How do live sessions work?" icon="eye">
    WebRTC streaming at 25 fps. Use cases:

    **Monitoring:**

    ```typescript theme={null}
    const iframe = task.getLiveIframe?.('readonly');
    // Embed in dashboard
    ```

    **Human takeover:**

    ```typescript theme={null}
    const control = task.getLiveUrl?.({ interactive: true });
    // Send to operator
    ```

    **Debugging:**

    ```typescript theme={null}
    console.log(task.debugUrl);
    // Share with team
    ```

    **Compatibility:** Chrome 90+, Firefox 90+, Safari 14.1+
  </Accordion>

  <Accordion title="Is Morph OpenAI-compatible?" icon="openai">
    Yes—works with any OpenAI SDK:

    ```typescript theme={null}
    import OpenAI from 'openai';

    const client = new OpenAI({
      apiKey: "YOUR_API_KEY",
      baseURL: 'https://api.morphllm.com/v1'
    });

    const completion = await client.chat.completions.create({
      model: 'morph-computer-use-v1',
      messages: [{ role: 'user', content: 'Test checkout' }]
    });
    ```

    Works with: browser-use, Browserbase, Browserless, Steel, and more.
  </Accordion>

  <Accordion title="How do I track sessions?" icon="tag">
    Link tests to your systems with reference IDs:

    ```typescript theme={null}
    const result = await morph.browser.execute({
      task: "Test checkout",
      url: "https://myapp.com",
      externalId: "PR-1234",    // PR, Jira, CI/CD
      repoFullName: "myorg/myrepo", // Repository
      commitId: "abc123def456"  // Specific commit
    });
    ```

    All fields optional. Filter by these IDs in the dashboard.
  </Accordion>

  <Accordion title="How do I test authenticated pages?" icon="lock">
    Use the `auth` parameter with username/password or cookies:

    ```typescript theme={null}
    // Username/password login
    const result = await morph.browser.execute({
      task: "Log in with x_user and x_pass, then verify dashboard",
      url: "https://myapp.com/login",
      auth: { username: "test@example.com", password: "secret" }
    });

    // Skip login with cookies
    const result = await morph.browser.execute({
      task: "Verify admin dashboard",
      url: "https://myapp.com/admin",
      auth: {
        cookies: [{ name: "session", value: "abc123", domain: "myapp.com" }]
      }
    });
    ```

    Reference credentials in your task as `x_user` and `x_pass`.
  </Accordion>

  <Accordion title="Agent can't sign into my app (Clerk, Auth0, etc.)" icon="user-lock">
    Third-party auth providers like Clerk, Auth0, and Supabase Auth often block requests from unfamiliar origins or automated browsers.

    **Solutions:**

    1. **Use cookie-based auth** (recommended):
       ```typescript theme={null}
       const result = await morph.browser.execute({
         task: "Verify dashboard loads",
         url: "https://myapp.com/dashboard",
         auth: {
           cookies: [
             { name: "__session", value: "your-session-cookie", domain: "myapp.com" }
           ]
         }
       });
       ```
       Export cookies from your browser's DevTools after logging in manually.

    2. **Add preview URL to your auth provider's allowed origins**:

       * **Clerk**: Dashboard → API Keys → Allowed origins → Add your preview URL
       * **Auth0**: Applications → Settings → Allowed Origins → Add preview URL
       * **Supabase**: Authentication → URL Configuration → Add preview URL

       Example: Add `https://your-preview-abc.vercel.app` to allowed origins.

    Cookie auth is more reliable since it bypasses the login flow entirely.
  </Accordion>
</AccordionGroup>

***

## Need Help?

* **OpenAI Config**: `base_url: https://api.morphllm.com/v1` • `model: morph-computer-use-v1`
* **Agent Tools**: See [tool integration guide](/sdk/components/browser/tool)
* **Support**: Questions? Reach out in our Discord or email [support@morphllm.com](mailto:support@morphllm.com)
