---
title: The browser
description: Open a URL, enumerate tabs, and drive the rest of it as a desktop.
---

The image ships a browser on the desktop. Two routes address it directly; everything else about
it is the desktop — click it, type in it, screenshot it.

| Call                               | What it does        |
| ---------------------------------- | ------------------- |
| `POST /machines/{id}/browser/open` | open a URL          |
| `GET /machines/{id}/browser/tabs`  | enumerate open tabs |

```ts
await machine.browser.open("https://example.com");
await machine.browser.open("https://example.com", { newWindow: true });
```

Opening a URL is an input action and takes the lease. It focuses the browser and navigates,
which is why it is a product route rather than something you assemble out of a hotkey and typing
into an address bar.

## Tabs answer, they do not throw

```ts
const tabs = await machine.browser.tabs();
if (tabs.supported) {
  for (const tab of tabs.tabs) console.log(tab.title, tab.url);
} else {
  console.log(tabs.reason); // why this image cannot enumerate them
}
```

Tab enumeration depends on what the image's browser exposes. When it cannot, the route answers
`supported: false` with a `reason` rather than failing — an agent that gets a `501` learns
nothing it can act on, and one that gets a sentence can fall back to reading the screen.

## Driving pages

There is no page-object model, no selector engine and no injected script. A page is pixels and
a keyboard:

```ts
await machine.browser.open("https://example.com");
await machine.keyboard.press("F6"); // focus the address bar
await machine.keyboard.type("search terms");
await machine.keyboard.press("Return");

const shot = await machine.screen.screenshot({ format: "png" });
await machine.mouse.click({ at: { x: 640, y: 400 } });
```

That is deliberate: the point of a real desktop is that a site cannot tell it apart from a
person's, and an injected automation surface is exactly the thing that gives that away. If you
want a headless browser API, run one **inside** the machine over
[a terminal](/docs/reference/terminal) and reach it through
[a published port](/docs/reference/ports).

## The browser profile persists

Cookies, logins and extensions live on the disk, so they survive a stop and a start, and they
are carried by a [snapshot, fork or template](/docs/reference/persistence). Logging into a site
once and saving the machine as a template is the intended way to give every future machine that
session.

A [secret](/docs/reference/secrets) is the opposite: delivered to tmpfs, and deliberately not
carried by a capture.
