---
title: "Profiles & personas"
description: "What a profile is made of: the fields you set, the identity drawn from its seed, which values are taken from your machine rather than invented, and what may be changed after creation."
canonical: "https://scalebrowser.net/docs/profiles"
---

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

# Profiles & personas

> What a profile is made of: the fields you set, the identity drawn from its seed, which values are taken from your machine rather than invented, and what may be changed after creation.

A profile is one persistent identity: its own browser directory, its own fingerprint, optionally its own proxy. This page is what it is made of and what you may change.

## Creating one
Only `name` is required. Everything else is either drawn from a seed or takes the daemon's default.

```bash
$ curl -X POST http://127.0.0.1:8787/v1/profiles \
    -H "Authorization: Bearer $SCALEBROWSER_BEARER_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{"name": "shop-01", "geo_mode": "follow_exit", "proxy_id": "px_7f3a"}'
```

| Field              | Set at create | Changed later | What it does                                                                |
| ------------------ | ------------- | ------------- | ----------------------------------------------------------------------------- |
| `name`             | required      | yes           | Yours. The only field with no effect on anything the page sees.               |
| `proxy_id`         | optional      | yes           | Which stored proxy this profile leaves through. `null` means your own address. |
| `geo_mode`         | optional      | yes           | What happens when the persona and the exit disagree. Default `follow_exit`.   |
| `expected_country` | optional      | yes           | The country `strict_expected` measures against.                               |
| `persona_country`  | optional      | **no**        | The region the identity is drawn for. See [What you may constrain](#what-you-may-constrain). |
| `group_id`         | optional      | yes           | A flat label. See [Groups, presets & bulk](/docs/fleet).                       |
| `enabled`          | defaults true | yes           | A disabled profile is skipped by leases and bulk starts.                       |
| `seed`             | optional      | **no**        | Reproduce a known identity down to the byte.                                   |
| `engine_version`   | optional      | **no**        | Pin a specific installed engine.                                               |

<Warning>

**`seed` and `persona_country` are fixed at create time.** Neither can be changed afterwards, because the whole identity was drawn from them.

`persona_country` is refused on any value, echo included: it is never part of a response, so nothing can be echoing it back, and the whole identity is drawn from the seed. A region you want to change is not an edit, it is a different profile.

**But a profile's region is not frozen.** What moves it is the exit, not the field: give the profile a proxy that leaves in the country you want, and the next start rewrites timezone, language and voices to it. That is the same mechanism that gives a proxy-less profile your own country, and the reason the region field is a starting value rather than a setting.

</Warning>

## The persona
Every profile carries a full identity, and the whole thing rides in the profile object under `persona`. It is dozens of fields: user agent, platform, screen, GPU, timezone, locale, fonts, audio properties, speech voices, client hints, media devices, permission defaults, network information, battery, and a platform authenticator.

The interesting question is not what is in there. It is **where each value came from**, because that is what decides whether a page can catch it.

### Values taken from your machine
Anything a page can cross-check against the machine's own rendering is read off the host, not invented. There is no version of this that works by claiming.

| Axis                       | Why it cannot be invented                                                                                            |
| -------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| GPU vendor and renderer    | The machine's own drawing answers back. A claimed card that does not draw the way that card draws is a contradiction found in one comparison. |
| Screen geometry            | The desktop answers, in both directions: window size, available area, the taskbar's bite.                             |
| Device pixel ratio         | Same. The display scale is measurable from inside the page.                                                            |

These are adopted at create **and again at every launch**, which is what makes a profile survive being moved to another machine of the same operating system: it heals rather than lying about the machine it is now on. Across operating systems it does not heal, and the launch is refused instead: see [Tier](/docs/concepts#tier).

The consequence is honest and worth stating: every profile on one host shares that host's screen and GPU. Within one machine those axes are linkable by design. The alternative is a claim the page can disprove, and a disprovable claim is worse than a shared truth.

### Values drawn from the seed
Axes with no hardware feedback are sampled deterministically from the profile's seed: timezone, locale, font set, browser version, device memory, hardware concurrency, audio properties, voices, media devices, battery, network information.

Same seed, same values, every time. That is why a profile is the same machine at every start, and why a `seed` you kept lets you rebuild a profile you lost.

### Values rewritten at launch
Under the default `follow_exit`, timezone and locale are rewritten to match the proxy exit's cohort. That happens at launch, so moving a profile to a proxy in another country does the right thing without you editing anything.

An exit the daemon **cannot** identify is left alone rather than half-rewritten. Half a rewrite is a contradiction the profile did not have before. [Coherence & proxies](/docs/coherence) is the page for that.

**With no proxy the same thing happens against your own connection.** The daemon resolves the address the browser really leaves through and rewrites timezone and locale to that country. It is the same rule, not an exception to it: a profile claiming Berlin from an address that geolocates to Vienna is a contradiction any site can check for free, and the timezone is the half we can fix.

This now happens **when you create the profile**, not only when you first start it. Before, a profile made without a proxy showed a random country in its identity panel until its first launch, and then changed under you. The lookup is capped at four seconds and simply skipped when it fails, so creating a profile offline still works and leaves the drawn identity alone.

## What you may constrain
Almost nothing, and that is deliberate.

A caller may narrow an axis the launch does not overwrite, and only as a weight on an existing draw. Of the axes anyone asked for, exactly one qualifies: **country**. Set it as `persona_country` when you create one profile, or as a preset's `constraints.country` for a batch. Both go through the same check.

Choosing a region pins language, timezone and speech voices together, because all three come out of one row of the model. It does not pin anything else: two profiles created for the same region still differ on every other axis.

```bash
$ curl http://127.0.0.1:8787/v1/persona/constraints \
    -H "Authorization: Bearer $SCALEBROWSER_BEARER_TOKEN"
```

That endpoint answers with the country codes the embedded model actually carries locale rows for. Read it rather than writing your own list: the set is a property of a pinned dataset, and a hand-copied list would drift into offering a region the daemon then refuses with a `400` nobody can explain.

<Note>

**A region is a starting value, not a promise.** Under the default `geo_mode` the profile takes the country it really exits in: its proxy's, or with no proxy your own connection's. So the region decides the identity of a profile you run through a matching proxy, or one you deliberately set `geo_mode: "off"` on. Choosing a region that the exit would overwrite is refused when you create it, with a message naming both countries, rather than accepted and quietly changed.

There is deliberately **no prefill of your own country**. It would be wrong twice over: the identity model draws only eleven countries while the exit rule can rewrite to seventeen, so a country like Austria is one you can be given but never pick; and the usual case is not your own country but your proxy's, which a prefill would make you clear every time. A profile with no proxy already ends up with your country without anyone choosing it.

**Why the list is so short.** An extra knob is an extra way for a fleet to look alike. Every constraint a caller can set is one more thing a hundred profiles created by the same script will share, and a shared unusual value is exactly what a fleet is caught by. An unconstrained draw stays byte-identical whether or not you passed a constraint.

</Note>

## The protection verdict
A profile that is listed or fetched carries a `protection` object beside its own fields:

```json
{
  "id": "pr_9c21e4",
  "name": "shop-01",
  "runtime_state": "stopped",
  "protection": { "level": "…", "blocked": false, "reasons": [] }
}
```

It is produced by the same functions the launch itself runs, so `blocked: true` means "this profile will refuse to start", not a second opinion that might disagree. The management app deliberately does not render it; the API carries it because a script that starts fifty profiles wants to know which two will fail before it tries.

## Deleting one
`DELETE /v1/profiles/:id` removes the row and the profile's browser directory. A running browser is stopped first.

That takes every cookie, every stored session and every login inside it, and it is not recoverable.

<Warning>

**An agent can do this too.** `profile.batch` with `action: "delete"` is part of the `management` tool set, and the Windows desktop client ships that set enabled. It is the one destructive tool the app serves. A profile somebody is actively working is refused, but nothing else stands in the way: there is no confirmation step and no undo. See [the tool set](/docs/agents/mcp-server#the-tool-set) for how to serve a narrower catalog.

</Warning>

If you sync, deleting here also deletes the synced copy. See [Profile sync](/docs/sync).

## Next
- [Coherence & proxies](/docs/coherence): what is checked before a profile is allowed to start.
- [Groups, presets & bulk](/docs/fleet): making fifty of these at once.
- [Proxies & exits](/docs/proxies): the half of the identity that lives outside the browser.
- [REST API](/docs/rest): the endpoints behind this page.
