Guide

Profiles

A profile is Morrow's unit of identity: a real, persistent Camoufox browser on disk, not a database record.

The persistence model

Each profile is a real Firefox (Camoufox) profile directory on disk, stored under <MORROW_DATA_DIR>/profiles/<id> — cookies, localStorage, IndexedDB, and any signed-in session state for sites the profile has visited. Profile metadata (name, status, proxy, locale, timezone, viewport, timestamps) lives separately in a local SQLite database.

Because the browser directory itself is what persists, stopping a profile flushes cookies and storage to disk, and starting it again resumes exactly where it left off — same fingerprint, same logins, same open state. This is what makes Morrow different from a throwaway headless browser: a profile behaves like a returning human on a real machine, not a fresh instance every run.

Naming

Profile names are lowercase letters, digits, and dashes, and must start with a letter or digit (^[a-z0-9][a-z0-9-]{0,62}$). Names are how you address a profile everywhere — REST paths, the Playwright websocket URL, and MCP tool arguments all take the name, not the internal id.

Lifecycle

All lifecycle endpoints need Authorization: Bearer $MORROW_API_KEY.

Create

POST /api/v1/profiles
curl -X POST http://localhost:3000/api/v1/profiles \
  -H "Authorization: Bearer $MORROW_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "research-eu", "locale": "de-DE"}'

Start

POST /api/v1/profiles/:name/start
curl -X POST http://localhost:3000/api/v1/profiles/research-eu/start \
  -H "Authorization: Bearer $MORROW_API_KEY"

Launches the browser. A profile that's already running when a request needs it (Playwright connect, scrape with a profile field, an MCP tool call) auto-starts — you rarely need to call this directly except from the dashboard.

Stop

POST /api/v1/profiles/:name/stop
curl -X POST http://localhost:3000/api/v1/profiles/research-eu/stop \
  -H "Authorization: Bearer $MORROW_API_KEY"

Flushes browser state to disk and closes the browser process.

Clone

POST /api/v1/profiles/:name/clone
curl -X POST http://localhost:3000/api/v1/profiles/research-eu/clone \
  -H "Authorization: Bearer $MORROW_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "research-eu-2"}'

Copies the source profile's on-disk directory (cookies, storage, logins) and its proxy/locale/timezone/ viewport into a new profile. The source must be stopped first.

Reset

POST /api/v1/profiles/:name/reset
curl -X POST http://localhost:3000/api/v1/profiles/research-eu/reset \
  -H "Authorization: Bearer $MORROW_API_KEY"

Deletes the profile's on-disk browser directory — cookies, storage, logins, and the pinned fingerprint — while keeping the profile record and its settings. The profile must be stopped first; the next start begins from a clean browser again.

Delete

DELETE /api/v1/profiles/:name
curl -X DELETE http://localhost:3000/api/v1/profiles/research-eu \
  -H "Authorization: Bearer $MORROW_API_KEY"

Removes the profile record and its on-disk directory entirely. Irreversible; the profile must be stopped first.

Configuration

Set at create time, or updated later with PATCH /api/v1/profiles/:name (pass null to clear a field):

  • proxy — a proxy URL this profile's browser routes through, so the network origin matches the persisted identity.
  • locale — e.g. de-DE, baked into the spoofed fingerprint.
  • timezone — e.g. Europe/Berlin.
  • viewport{ width, height }.

Full request/response shapes are in the OpenAPI reference at /api-docs.

Events timeline

Every profile records events — created, started, stopped, reset, and more — retrievable via GET /api/v1/profiles/:name/events (optionally ?limit=, default 200, max 1000). The dashboard's profile page renders this as a timeline alongside the live viewer, active sessions, and connect snippets.

Concurrency limit

MORROW_MAX_PROFILES caps how many profiles can be running at once (default 5) — not how many profiles can exist. Starting a profile past the limit returns 429 too_many_profiles. See Self-hosting.