Skip to content
Documentation menu

Getting started (operator)

This guide takes you from a fresh clone to a shared white-label report. No external accounts are needed — the default database is a local file, and the offline mock engine lets you exercise the whole workflow.

1. Local quickstart

Requirements: Node ≥ 20.9 (Node 22 LTS recommended) and pnpm 10.

git clone <your-repo-url> placeable && cd placeable
pnpm install
cp .env.example .env.local

Open .env.local and set:

  • SESSION_SECRET — at least 32 characters of real entropy: openssl rand -base64 32
  • OPERATOR_EMAIL and OPERATOR_PASSWORD — your own operator account. pnpm seed creates it (FR-1.6). This is also the account you recover with pnpm user:set-password later.

Everything else can stay at its default (DB_DRIVER=pglite, database file at ./.data/pg). Then:

pnpm db:setup   # apply committed migrations
pnpm seed       # create the operator (and demo fixtures when DEMO_MODE=1)
pnpm dev        # http://localhost:3000

Sign in at /sign-in with your OPERATOR_* credentials.

Demo credentials

When the app runs with DEMO_MODE=1 (or you seed with pnpm seed -- --demo-fixtures), a fully populated demo world exists with two documented accounts. These are defined as constants in src/db/demo-fixtures.ts — public demo access is a stated, accepted decision (FR-8.1):

RoleEmailPassword
Operatordemo-operator@placeable.devoperator-demo-2026
Agency user (Northlight Digital)demo@northlight.exampleagency-demo-2026

2. Your first workflow

Create an agency

Agencies are your tenants — the marketing agencies who resell reports. Go to Agencies → New agency: name, contact email, and an initial user. The initial user's password is shown once with a copy button; store it before dismissing.

Create a brand

A brand is an agency's end client — the business being measured. On the brand form, three fields do the real work:

  • Aliases — every other name the business goes by ("Acme", "Acme CRM", "AcmeHQ"). Mention counting is name matching; a missed alias is a missed mention.
  • Domains — every domain the business owns. Anything cited on the brand's own domain is scored LOCKED (you can't "get onto" your own site; it doesn't count as third-party evidence).
  • Competitors — each with their own aliases and domains. Competitor mentions build the gap analysis, and competitor-owned domains are scored LOCKED too (a competitor won't list you on their site).

Create a prompt set

A prompt set is the list of buying questions you'll ask the engines, plus which engines and how many repeats.

What makes a good buying question: write what a real buyer types into an assistant, not a keyword. "I'm a loan officer and need a better way to manage my pipeline of leads. What's the best CRM?" beats "best mortgage CRM" — long-tail, first-person, with context. Ten to thirty prompts per set is typical.

Templates: "New from template" copies a code-shipped set (src/lib/templates.ts) into the brand, fully editable afterwards. Shipped templates: mortgage-saas, dental-software, hvac-software — each ten real buying questions at 8 runs/prompt across chatgpt, gemini, and sonar. Two more real engines exist beyond what the templates default to — Google's AI Overviews and AI Mode (ai_overviews, ai_mode) — and can be added to any prompt set; see Engine internals for what each one actually measures and the model each does or doesn't disclose.

Why 8 runs minimum: the same prompt to the same engine gives different answers on different runs — even at temperature 0, answers vary 9–28%. One run is an anecdote. Schulte et al. put the minimum at seven runs per prompt per engine for brand-level visibility and at least eight when source-level coverage matters; Placeable measures sources, so eight is the floor. The default is 8; anything below it requires a confirm dialog and permanently marks the resulting runs and reports with a low-sample warning chip. The hard floor is 3.

Start a run

From the brand page, Start run opens the preflight: total samples ("30 prompts × 8 runs × 3 engines = 720 samples"), estimated cost and duration, and per-engine configured status. An unconfigured engine blocks the start — there is no silent fallback to fake data.

  • Real providers (chatgpt, gemini via DataForSEO; sonar via Perplexity; ai_overviews and ai_mode via DataForSEO's Google SERP endpoints): set the provider env vars (see .env.example), then run the local worker in a second terminal: pnpm worker. It claims queued runs and executes them in batches; Ctrl-C is safe, it resumes where it left off.
  • Mock engine: available in dev without any keys — deterministic offline fixtures, good for exercising the pipeline. In local dev, mock runs also go through pnpm worker. In demo mode (DEMO_MODE=1) every run is mock-only, capped at 60 samples, and executes synchronously inside the request — no worker needed.

Watch progress on the run page: per-engine counts, cancel, and "Retry failed" for errored samples.

Generate the report and share it

When the run completes, Generate report creates an immutable snapshot (titled "Brand — Report #1", editable). On the report page, create a share link — a long unguessable URL your agency sends to their client. Links are revocable, can expire, and render the white-label view with the agency's logo and accent. In demo mode, share links may stop working at any time (data is in-memory).

Every report page also offers a print view (/reports/[id]/print, and /r/[token]/print on a share link) that renders the identical report component in a paper frame — "PDF" just opens the browser's own print dialog, nothing is generated or stored server-side — plus sources.csv and mentions.csv downloads; a full samples.csv of every sampled answer is available from the app (not the share link). A run started as a pitch snapshot rather than a full period measurement — one day, one wave, at the same 8-runs-per-prompt-per-engine floor — is labelled "Snapshot" everywhere its rate appears and is excluded from deltas and the monthly trend.

3. Next steps