# PersonaHub Automation API

> Use PersonaHub's local HTTP API to manage profiles, proxies, fingerprints, and website credentials, then launch browsers and connect over CDP for web automation.

Source: https://console.getchrovia.com/en/docs/automation-api

The PersonaHub automation API lets you create and manage profiles, configure proxies and fingerprints, manage saved website credentials, and launch or close profile browsers.

Reading pages, clicking, typing, and taking screenshots require CDP or a compatible automation tool after the browser starts.

## 1. Before you start

1. Open the PersonaHub desktop app and sign in.
2. Enable the API in the app's automation settings, copy the API key, and check the port.
3. Make sure the browser engine is ready before launching a profile.
4. Keep the app running, signed in, and connected to the internet while using the API.

The API can only access the signed-in account's profiles. Profile creation is subject to the account's quota.

| Setting | Default |
| --- | --- |
| HTTP base URL | `http://127.0.0.1:43110` |
| Automation server | Disabled |
| Listen address | `127.0.0.1`; changes to `0.0.0.0` when remote access is enabled |
| Port range | `1`–`65535`, configurable in settings |

For remote access, replace `127.0.0.1` with the address of the computer running PersonaHub. Browsers, debugging ports, and returned file paths belong to that computer.

## 2. Conventions

### 2.1 Authentication

Every request except `GET /health` requires:

```http
Authorization: Bearer <API_KEY>
```

For requests with a body, also send:

```http
Content-Type: application/json
```

The API key does not replace account sign-in or grant additional account permissions.

### 2.2 Requests and responses

- Request bodies must be JSON objects, not arrays, strings, or `null`.
- Request bodies are limited to 256 KiB. An oversized request may have its connection closed directly.
- All successful endpoints listed here return `200`, including creation and deletion.
- Responses use JSON and include `Cache-Control: no-store`.
- Profile and credential path IDs are UUIDs. Invalid path IDs return `404`.
- Replace `:id`, `:passwordId`, `:otpId`, and `:passkeyId` with actual IDs.
- List endpoints do not support pagination parameters.
- `PATCH` preserves omitted fields; nested-field rules are described below. Do not use `null` in place of an omitted field.

Error response:

```json
{
  "error": "Error description",
  "code": "not_found"
}
```

Handle errors using the HTTP status and `code`, not the wording or language of `error`.

## 3. Quick start

These commands use Bash/zsh syntax. Replace the API key and the UUID of an existing profile before running them.

```bash
export PERSONA_API_BASE='http://127.0.0.1:43110'
export PERSONA_API_KEY='<API_KEY>'

curl -sS "$PERSONA_API_BASE/health"

curl -sS "$PERSONA_API_BASE/v1/engine" \
  -H "Authorization: Bearer $PERSONA_API_KEY"

curl -sS "$PERSONA_API_BASE/v1/profiles" \
  -H "Authorization: Bearer $PERSONA_API_KEY"

export PROFILE_ID='<PROFILE_UUID>'

curl -sS -X POST "$PERSONA_API_BASE/v1/profiles/$PROFILE_ID/launch" \
  -H "Authorization: Bearer $PERSONA_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"startupUrl":"https://example.com","extraArgs":["--remote-debugging-port=9222"]}'
```

A successful launch returns:

```json
{"ok": true}
```

This confirms browser startup, not that a page, website login, or CDP endpoint is ready. Once the debugging port is available, query it with:

```bash
curl -sS http://127.0.0.1:9222/json/version
```

Connect using the returned `webSocketDebuggerUrl`, or the CDP HTTP address if your automation tool supports it. Port `43110` is the automation API port; `9222` is the example browser debugging port. Assign different debugging ports when enabling CDP for multiple profiles at once.

Close the browser started through the API:

```bash
curl -sS -X POST "$PERSONA_API_BASE/v1/profiles/$PROFILE_ID/close" \
  -H "Authorization: Bearer $PERSONA_API_KEY"
```

## 4. Service status and account entitlements

| Method | Path | Purpose | Success response |
| --- | --- | --- | --- |
| GET | `/health` | Server health; no authentication required | `{ "ok": true }` |
| GET | `/v1/engine` | Browser engine status | `{ "ready": true, "version": "..." }` |
| GET | `/v1/account/entitlements` | Current account entitlements | `{ "entitlements": {...} }` |

A successful `/health` response does not confirm account sign-in or engine readiness. The `version` returned by `/v1/engine` is the browser engine version, not the app version, and is an empty string when unavailable. `ready` does not indicate that a profile has launched or that CDP is available.

The `entitlements` object contains:

| Field | Type | Description |
| --- | --- | --- |
| `tier` | string | Plan identifier |
| `maxProfiles` | number | Profile limit |
| `usedProfiles` | number | Profiles currently counted toward the limit |
| `renewsAt` | string or null | Renewal time |
| `cancelAt` | string or null | Cancellation time |
| `status` | string or null | Subscription status |

## 5. Profiles

| Method | Path | Purpose | Success response |
| --- | --- | --- | --- |
| GET | `/v1/profiles` | List profiles | `{ "profiles": [...] }` |
| POST | `/v1/profiles` | Create a profile | `{ "profile": {...} }` |
| GET | `/v1/profiles/:id` | Get profile details | `{ "profile": {...} }` |
| PATCH | `/v1/profiles/:id` | Update a profile | `{ "profile": {...} }` |
| DELETE | `/v1/profiles/:id` | Delete a profile and its corresponding local data and cache | `{ "ok": true }` |

### 5.1 Create and update fields

| Field | Type | Required on creation | Description |
| --- | --- | --- | --- |
| `name` | string | Yes | Nonempty after trimming; up to 120 characters |
| `startupUrl` | string | No | Default startup URL; HTTP/HTTPS, up to 2048 characters; an empty string leaves it unset |
| `extraArgs` | string | No | Saved launch arguments, separated by whitespace; up to 4096 characters; defaults to an empty string |
| `proxy` | object | No | Profile proxy settings, described below |
| `fingerprint` | object | No | Fingerprint settings, described below |
| `proxyIpInfo` | object | No | Known proxy exit information; skips automatic detection |
| `acceptProxyChain` | boolean | No | Whether to accept the app's configured proxy chain; defaults to `true` |
| `appearanceLabel` | string | No | Browser appearance label |
| `appearanceBackgroundColor` | string | No | Appearance background color, such as `#2563EB` |
| `appearanceForegroundColor` | string | No | Appearance foreground color, such as `#FFFFFF` |

Minimal creation body:

```json
{"name": "Automation profile"}
```

Omitted settings use defaults on creation. Updates to `proxy` and `fingerprint` change only the supplied subfields. Proxy detection may affect the timezone, as described in section 5.4.

Example partial update:

```http
PATCH /v1/profiles/:id
```

```json
{
  "name": "Automation profile A",
  "startupUrl": "https://example.com",
  "extraArgs": "--remote-debugging-port=9222"
}
```

Here, `extraArgs` is a string, unlike the string array used by the launch endpoints. Saved arguments do not support shell-style quoting; pass an argument containing spaces as one item in the launch endpoint's `extraArgs` array. Set `startupUrl` or `extraArgs` to an empty string in an update to clear its saved value.

### 5.2 Proxy settings

Configure the proxy in the profile's `proxy` field.

| Field | Type | Creation default | Description |
| --- | --- | --- | --- |
| `protocol` | string | `socks5` | `http`, `https`, or `socks5` |
| `host` | string | `""` | Proxy hostname or IP; an empty string means no profile-specific proxy |
| `port` | number | `1080` | Integer from `1` to `65535` |
| `username` | string | `""` | Proxy authentication username |
| `password` | string | `""` | Proxy authentication password |
| `bypassList` | string | `localhost;127.0.0.1` | Proxy bypass rules separated by semicolons |

```json
{
  "name": "US profile",
  "proxy": {
    "protocol": "socks5",
    "host": "proxy.example.com",
    "port": 1080,
    "username": "<PROXY_USERNAME>",
    "password": "<PROXY_PASSWORD>"
  }
}
```

Leaving the profile-specific proxy unset does not force a direct connection. App proxy-chain or system proxy settings may still affect the connection.

### 5.3 Fingerprint settings

| Field | Type | Creation default | Description |
| --- | --- | --- | --- |
| `browserVersion` | string | `""` | Reported version, up to 32 characters; empty or `auto` selects the highest available version matching the engine's major version |
| `hardwareConcurrency` | number | Random | CPU thread count; integer from `1` to `256` |
| `cpuPerformance` | number | From random preset | CPU performance tier; integer from `0` to `4` |
| `deviceMemory` | number | Random | Reported memory: `2`, `4`, `8`, `16`, or `32` GB; does not allocate physical memory |
| `speechVoicesMode` | string | `override` | `none`: no override; `override`: full override; `append_google`: append Google voices only |
| `fontAllowlistOverride` | boolean | `true` | Apply the preset font allowlist for the host system |
| `disableCanvasNoise` | boolean | `false` | Disable pixel noise in Canvas 2D readback and image exports; noise stays enabled by default. Does not affect WebGL, WebGPU, or other noise. Relaunch after changes |
| `webrtcIpPolicy` | string | `disable_non_proxied_udp` | WebRTC IP policy |
| `timezone` | string | `America/New_York` | IANA timezone; proxy detection may supply a default timezone |
| `acceptLanguages` | string | `en-US,en` | Comma-separated language list |

Each creation randomly selects `8/12/16/20/24/32` CPU threads and `16/32 GB` of memory. The random preset uses performance tier `3` for 8 threads and `4` otherwise. Explicit fields override the corresponding preset values. Updates preserve omitted fields, and launches do not randomize them again.

`cpuPerformance` tiers: `0 = Unknown`, `1 = Low`, `2 = Mid`, `3 = High`, `4 = Ultra`.

Supported `webrtcIpPolicy` values:

- `disable_non_proxied_udp`
- `default_public_interface_only`
- `default_public_and_private_interfaces`
- `default`

UDP-enabled modes may expose direct IP addresses instead of the node IP. Close and relaunch the browser after changes.

### 5.4 Proxy exit information

When a proxy is configured without exit information, the API detects it automatically. A failed check returns `proxy_probe_failed`.

If you already have detection results, provide `proxyIpInfo`:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `ip` | string | Yes | Nonempty proxy exit IP |
| `country` | string | No | Two-letter country or region code, such as `US` |
| `region`, `city` | string | No | Region and city |
| `timezone` | string | No | IANA timezone |
| `latitude`, `longitude` | number | No | Coordinates |
| `asn`, `org` | string | No | ASN and network organization |

- If the request does not explicitly provide a nonempty `fingerprint.timezone` and the detected or supplied exit information includes a timezone, that timezone is used.
- Automatic detection does not change `acceptLanguages`. Supply it explicitly to change the language list.

### 5.5 Profile responses

Each list entry contains `id`, `name`, `startupUrl`, `extraArgs`, `createdAt`, `updatedAt`, and optional `lastLaunchedAt`.

Detail, creation, and update responses also contain:

- `proxy`: proxy connection settings, including the authentication password.
- `fingerprint`: fingerprint settings.
- `acceptProxyChain`: whether the profile accepts the app's proxy chain.
- `appearanceLabel`, `appearanceBackgroundColor`, and `appearanceForegroundColor`: appearance settings.

## 6. Browser lifecycle

| Method | Path | Purpose | Success response |
| --- | --- | --- | --- |
| POST | `/v1/profiles/:id/launch` | Launch the profile browser | `{ "ok": true }` |
| POST | `/v1/profiles/:id/command` | Get a launch command without starting the browser | `{ "executable": "...", "args": [...] }` |
| POST | `/v1/profiles/:id/close` | Close a browser started through `launch` | `{ "ok": true }` |

### 6.1 Launch options

`launch` and `command` accept the same body. Send `{}` if no temporary options are needed.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `startupUrl` | string | No | URL for this launch; omitted or empty uses the profile's saved URL |
| `extraArgs` | string[] | No | Arguments for this launch, appended after saved launch arguments |

```json
{
  "startupUrl": "https://example.com",
  "extraArgs": ["--remote-debugging-port=9222"]
}
```

Launch arguments include the profile's saved arguments, the app's global launch arguments, and this call's `extraArgs`, in that order. Clearing the profile's saved arguments does not clear the app's global arguments.

These options apply only to this call and do not change the profile's saved values. `extraArgs` cannot override the data directory or browser lifecycle arguments managed by PersonaHub.

A successful `launch` only confirms browser startup. Calling it again may close and restart that profile. Do not use it to poll browser status.

### 6.2 Launch command

The `command` response contains:

| Field | Type | Description |
| --- | --- | --- |
| `executable` | string | Path to the browser executable |
| `args` | string[] | Complete launch arguments |

Pass the returned values directly to a process-spawning API on the computer running PersonaHub. Do not concatenate them into a shell command or override the data directory.

For `--remote-debugging-pipe`, use `command` and have the caller start the process with the debugging pipes connected. `launch` does not return debugging pipes.

### 6.3 Usage limits

- Keep the PersonaHub desktop app running while using the browser.
- `close` does not manage browsers launched from the app UI or processes started by the caller using `command`.
- If no API launch is available to close, the API returns `404` with code `not_running`. The first `close` request can still succeed after the browser has exited on its own, so do not use this endpoint to check whether it is running.
- A successful `close` means closure was requested, not that the browser has exited.
- Do not launch multiple instances of the same profile concurrently.

## 7. Saved website credentials

### 7.1 Website passwords

| Method | Path | Purpose | Success response |
| --- | --- | --- | --- |
| GET | `/v1/profiles/:id/passwords` | List summaries without passwords | `{ "credentials": [...] }` |
| POST | `/v1/profiles/:id/passwords` | Save a website password | `{ "ok": true }` |
| GET | `/v1/profiles/:id/passwords/:passwordId` | Get a credential, including its password | `{ "credential": {...} }` |
| PATCH | `/v1/profiles/:id/passwords/:passwordId` | Update a website password | `{ "ok": true }` |
| DELETE | `/v1/profiles/:id/passwords/:passwordId` | Delete a website password | `{ "ok": true }` |

Save request:

```json
{
  "origin": "https://example.com",
  "username": "user@example.com",
  "password": "<WEBSITE_PASSWORD>"
}
```

| Field | Rules |
| --- | --- |
| `origin` | Required HTTP/HTTPS URL; up to 2048 characters |
| `username` | Required and nonempty; up to 512 characters |
| `password` | Required and nonempty; up to 2048 characters |

`PATCH` requires at least one of these fields. Omitted fields keep their current values; empty strings are not allowed. `signon_realm` is generated automatically and is not accepted as a request field.

Summaries contain `id`, `origin`, `signon_realm`, and `username`; single-credential responses also include `password`. Saving the same sign-on realm and username updates the existing entry. Saving returns no ID; query the list afterward to obtain it.

Changes are provided to the browser on a subsequent launch. Deletion does not change the website account or remove credentials already loaded by a running browser. Later browser sync may overwrite changes or recreate deleted entries.

### 7.2 TOTP codes

| Method | Path | Purpose | Success response |
| --- | --- | --- | --- |
| GET | `/v1/profiles/:id/otp` | List entry metadata | `{ "entries": [...] }` |
| POST | `/v1/profiles/:id/otp` | Save or update a TOTP entry | `{ "entry": {...} }` |
| DELETE | `/v1/profiles/:id/otp/:otpId` | Remove an entry | `{ "ok": true }` |
| GET | `/v1/profiles/:id/otp/:otpId/code` | Generate the current code | `{ "id": "...", "code": "...", "expiresAt": "..." }` |

Save fields:

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `secret` | string | Yes | Base32 secret; up to 256 characters after removing whitespace and uppercasing; not an `otpauth://` URL |
| `label` | string | No | Up to 120 characters; derived from issuer and account when omitted, or `Authenticator` if both are empty |
| `issuer` | string | No | Up to 120 characters; defaults to an empty string |
| `accountName` | string | No | Up to 180 characters; defaults to an empty string |
| `algorithm` | string | No | `SHA1`, `SHA256`, or `SHA512`; defaults to `SHA1` |
| `digits` | number | No | `6` or `8`; defaults to `6` |
| `period` | number | No | Period in seconds; integer from `15` to `120`; defaults to `30` |

List and save responses include `id`, `label`, `issuer`, `accountName`, `algorithm`, `digits`, and `period`. They do not return the secret.

Within the same profile, the same normalized secret updates or restores the existing entry. A different secret creates a new entry. Omitted metadata and settings use defaults on each save; they do not preserve existing values.

Example code response:

```json
{
  "id": "<OTP_UUID>",
  "code": "012345",
  "expiresAt": "2026-09-17T08:00:30.000Z"
}
```

`code` is a string; preserve leading zeroes. Codes use the clock on the computer running PersonaHub. `expiresAt` is a UTC ISO timestamp. The API does not submit the code. Saving or deleting an entry does not enable or disable two-step verification on the website.

### 7.3 Passkeys

| Method | Path | Purpose | Success response |
| --- | --- | --- | --- |
| GET | `/v1/profiles/:id/passkeys` | List passkey metadata | `{ "entries": [...] }` |
| DELETE | `/v1/profiles/:id/passkeys/:passkeyId` | Remove a saved entry | `{ "ok": true }` |

Each entry contains `id`, `rpId`, `userName`, and `userDisplayName`. Private keys are not returned. The automation API has no endpoint for creating or importing passkeys.

Deletion affects subsequent launches. It does not unregister the credential on the website or remove copies already loaded by a running browser. See the [passkey guide](https://console.getchrovia.com/en/docs/passkeys) for plan requirements when using passkeys in the browser.

## 8. Common errors

| HTTP status | `code` | Meaning |
| --- | --- | --- |
| 400 | `body_invalid` | Invalid JSON |
| 400 | `body_not_object` | The top-level request value is not an object |
| 400 | `input_invalid` | Fingerprint, website password, or TOTP fields are invalid |
| 400 | `profile_name_required` / `profile_name_too_long` | Missing or oversized profile name |
| 400 | `startup_url_invalid` / `startup_url_too_long` | Invalid startup URL type, format, or length |
| 400 | `launch_args_too_long` | Saved launch arguments are too long |
| 400 | `extra_args_invalid` | Temporary arguments are not a string array or include a protected argument |
| 400 | `proxy_invalid` | Proxy settings are not an object |
| 400 | `protocol_invalid` | Unsupported proxy protocol |
| 400 | `proxy_port_invalid` | Invalid proxy port |
| 400 | `proxy_ip_info_invalid` | Invalid proxy exit information or missing IP |
| 400 | `proxy_probe_failed` | Proxy check failed |
| 400 | `password_account_taken` | Updated sign-on realm and username conflict with another credential |
| 400 | `otp_code_failed` | Failed to generate a TOTP code |
| 401 | `unauthorized` | Missing or incorrect API key |
| 401 | `not_authenticated` | Desktop account is signed out or its session has expired |
| 403 | `profile_quota_exceeded` | Account profile limit reached |
| 404 | `not_found` | Resource or route not found, or invalid path ID |
| 404 | `not_running` | No API launch available to close |
| 405 | `method_not_allowed` | Unsupported HTTP method |
| 413 | `body_too_large` | Request body exceeds the limit; the connection may close directly |
| 500 | `automation_api_error` | Request processing failed |
| 503 | `console_session_not_ready` | The desktop session is not ready |
| 503 | `browser_engine_not_ready` | The browser engine is not ready |

## Related guides

- [Create a profile](https://console.getchrovia.com/en/docs/create-profile)
- [Launch a profile browser](https://console.getchrovia.com/en/docs/launch-profile)
- [Enable CDP](https://console.getchrovia.com/en/docs/enable-cdp)
- [Website password management](https://console.getchrovia.com/en/docs/password-management)
- [TOTP guide](https://console.getchrovia.com/en/docs/totp)
- [Passkey guide](https://console.getchrovia.com/en/docs/passkeys)
