# Profiles (/docs/profiles)



A profile is a persistent browser storage jar: cookies plus `localStorage` and `sessionStorage` per origin, held on disk in [Playwright storage-state format](https://playwright.dev/docs/api/class-browsercontext#browser-context-storage-state). Attach one to a session and the agent starts already logged in to whatever that profile was logged in to, instead of hitting a sign-in wall on every run.

Profiles are the difference between an agent that can read a public listing and one that can work inside an authenticated application.

## Create a profile [#create-a-profile]

```bash
curl -X POST https://your-host/v3/profiles \
  -H "Authorization: Bearer $OPENBROWSE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "my-profile"}'
```

The response carries the profile `id`; pass it as `profileId` when you create a session. A new profile starts with an empty jar, which the first authenticated session fills.

List what exists:

```bash
curl https://your-host/v3/profiles \
  -H "Authorization: Bearer $OPENBROWSE_API_KEY"
```

Each profile in the response includes its `cookieDomains`, the domains the jar currently holds cookies for, and `lastUsedAt`, which updates whenever a session loads the profile. Rename with `PATCH /v3/profiles/{id}`, delete with `DELETE /v3/profiles/{id}` (which also removes the jar from disk).

## Import from Browser Use Cloud [#import-from-browser-use-cloud]

A cloud profile export is the same storage-state shape, cookies plus per-origin `localStorage`, so it imports directly. Import one and **the local profile id matches the cloud id**, so every `profileId` already in your code keeps working.

From a source checkout, the import script is the shortest route. It creates the profile if it does not exist, normalises the cookies, and backs up any existing jar to `.import-bak`:

```bash
.venv/bin/python -m scripts.import_profiles personal_profile.storage_state.json \
  --profile-id <cloud-profile-id> --name "Personal Profile"
```

A bundle (a JSON list of profile entries, or `{"profiles": [...]}` wrapping one) carries an id per entry, so one command imports many:

```bash
.venv/bin/python -m scripts.import_profiles bundle.json
```

That script ships with the repository rather than with the published package, so an installed copy uses the dashboard's **Profiles** importer or the API below instead. Both do the same work, and the endpoint also creates the profile if the id does not exist yet:

```bash
curl -X PUT https://your-host/v3/profiles/<profile-id>/storage-state \
  -H "Authorization: Bearer $OPENBROWSE_API_KEY" \
  -H "Content-Type: application/json" \
  --data @personal_profile.storage_state.json
```

Verify with `GET /v3/profiles/<id>`, or the **Profiles** page in the dashboard, which lists the imported `cookieDomains`.

## The storage state format [#the-storage-state-format]

```json
{
  "cookies": [
    {
      "name": "session",
      "value": "...",
      "domain": "example.com",
      "path": "/",
      "expires": 1999999999,
      "httpOnly": true,
      "secure": true,
      "sameSite": "Lax"
    }
  ],
  "origins": []
}
```

`origins` carries each origin's `localStorage` and `sessionStorage`, which the browser restores on load. A session works against a private copy of the profile and merges its changes back when the browser closes, so cookies acquired or refreshed during the run persist without flattening changes another session made meanwhile. The merge and final write are locked per profile, atomic and shielded against shutdown.

<Callout type="warn">
  These files are live session credentials. Anyone holding one is logged in as you on every site it covers. Never commit them; a source checkout git-ignores `data/` for this reason, and an installed copy keeps it out of the repository altogether by writing under `~/.openbrowse` instead. Treat a profile export with the same care as a password manager export.
</Callout>

## Using a profile [#using-a-profile]

```ts
const session = await client.sessions.create({
  task: "Open the billing page and return every invoice as structured data.",
  model: "claude-sonnet-5",
  profileId: "<profile-id>",
  outputSchema: mySchema,
});
```

For credentials the agent must type rather than carry as cookies, use `sensitiveData` alongside the profile; see [writing tasks](/docs/tasks).

## Sharing one profile between sessions [#sharing-one-profile-between-sessions]

Concurrent sessions may use the same profile. Each starts from the profile state available when its private copy is opened, then a three-way merge applies only the keys that session changed on top of whatever the profile holds when the session finishes. Cookies are keyed by name, domain and path; `localStorage` and `sessionStorage` are merged per origin and key. Unrelated logins, preferences and cart changes therefore survive sessions closing in either order.

If two sessions change the same key, the session that merges later wins. A stale deletion does not remove a value another session refreshed after the deleting session began. A queued session that starts after an earlier session has finished sees that earlier session's merged state; sessions already running do not receive one another's changes live.

The live feed records a `system` event with action `sharedProfile` when a session opens a profile another session is already using. It is informational: both runs continue. Private working copies are removed after merging and any copies orphaned by a crash are discarded at the next startup, rather than being merged long after their session ended.

## Listing and organising profiles [#listing-and-organising-profiles]

`GET /v3/profiles` returns an envelope rather than a bare array: `{items, totalItems, pageNumber, pageSize}`, with `page` and `pageSize` to walk it and `query` to search by name. A profile also carries an optional `userId`, settable on create and on update, which is there to tag whose login a jar represents when one instance serves several people. Nothing in the runtime reads it; it is yours to filter on.

The [API reference](/docs/api) has the full field list for each endpoint.

## Next [#next]

[Choosing a model](/docs/models) covers which model and reasoning effort to pair with the work, and [troubleshooting](/docs/troubleshooting) covers what to do when a session fails.
