# Morvero — How-to Guide (Markdown edition) > Morvero is portfolio-level product intelligence for enterprise software: usage analytics, feature-level adoption, in-product feedback, NPS, and AI-drafted backlogs across every internal app and AI-agent pilot — flat-priced per portfolio (never per-user or per-event), anonymous by design, installed with one script tag per product. Feedback flows to Jira or Azure DevOps as tickets with acceptance criteria and cited user evidence. This is the machine-readable edition of https://morvero.com/docs, served at `/docs.md` and `/llms-full.txt`. URLs below use the Morvero cloud host (`https://morvero.com`); self-hosted installs substitute their own origin. The human version with styling and anchors: https://morvero.com/docs ## 1. Getting started 1. **Create a workspace** — https://morvero.com/app/?signup=1 with your company name and work email. A single-use sign-in link verifies the address and opens the workspace. Passwordless: add a passkey (Face ID / Touch ID / security key) after first sign-in; email links remain the fallback. 2. **Create a product** — console → Products → + New product. Name it, choose a random-sampling rate (share of visitors who get a proactive feedback prompt), optionally restrict allowed domains. 3. **Copy the snippet** — one line of HTML; that's the whole integration. 4. **Follow the setup checklist** — new workspaces show a "Get Morvero live" checklist on the Overview (create product → install snippet → first feedback → invite a teammate). Progress is read from real data and the card retires itself once you're live. ## 2. Install the widget Paste the snippet into the product's footer or layout template so it renders on every page: ```html ``` - Works on multi-page apps and SPAs (route changes tracked automatically). - The widget lives in a closed Shadow DOM — your CSS can't break it, it can't break your page, and `async` means it never blocks rendering. - Deactivating the product in the console is an instant kill switch. - Appearance & language (product page → Widget appearance): accent color, corner, language auto-detect or fixed (EN/TR/DE/FR/ES); Enterprise can override individual strings. - The product card shows "receiving data ✓" once the first pageview arrives. - Set **allowed domains** for production keys — requests from other origins are rejected. - **Usage monitoring only**: untick "Collect feedback" and the widget turns invisible — no feedback button, no prompts, feedback API refuses submissions for that key; pageview and function tracking continue. ## 3. What your users see - A small floating 💬 button (bottom-right). The panel: 1–5 star rating for the current page, optional comment, and two opt-in attachments: - **Screenshot** — via the browser's native share prompt (explicit consent; the widget hides itself from the shot). - **Form entries** — values typed on the page, for "the form rejected my input" context. Passwords, hidden fields, and sensitive-looking names (card, cvv, ssn, token…) are excluded client-side AND re-scrubbed server-side; exclude anything else with a `data-feedback-ignore` attribute. - **Random sampling** — the configured share of visitors gets the panel proactively after ~8 seconds, at most once per 7 days per visitor, never again for a week after any feedback. - **Privacy** — visitors are anonymous (random ID in the browser, no cookies, no fingerprinting). Do Not Track / Global Privacy Control disable tracking and sampling entirely; deliberate manual feedback still works. ## 4. Track pages & functions **Pages — automatic.** Every page load and SPA route change is an anonymous pageview. Hash-routed SPA (`#/invoices`)? Add `data-feedback-hash-routing` to the script tag so the hash counts as part of the route. Opt-in, because on ordinary sites hashes are anchors. **Functions — tag elements (no JS):** ```html ``` Clicks appear under "Most / least used functions". **Functions — from code:** `MorveroFeedback.track('name')` records a use; `MorveroFeedback.open()` opens the panel. Two rules: API calls must live in their **own** ` ``` **Cohorts — optional, privacy-floored.** Pass a coarse group label (department, region, role family) via `data-feedback-cohort="finance"` and the product page gains a usage-and-sentiment breakdown by cohort. Aggregate-only with a k-anonymity floor: cohorts under 5 visitors in range are withheld entirely. Never pass usernames or employee ids. **Countries — automatic.** Every event is stamped with a two-letter country code resolved server-side from the request IP at collection time (in-process GeoLite2 lookup; the IP is discarded immediately, only the code is stored). Overview shows a world heat map; product pages get a Geography card. Behind Cloudflare or similar, the CDN's country header is used instead. ## 5. Mobile apps & REST API The web widget is a client of a small public REST API — native apps and server-side integrations call the same endpoints. No SDK to bundle. The full surface (collection API + Enterprise read API) is specified at https://morvero.com/openapi.json. Flow: **token, then submit.** 1. `GET https://morvero.com/api/widget-config?key=fbk_...` → short-lived `token` (~15 min). On `401 token_invalid`, re-fetch and retry. 2. `POST https://morvero.com/api/collect/feedback` with JSON: ```json { "key": "fbk_...", "token": "TOKEN_FROM_STEP_1", "rating": 4, "comment": "Love the new checkout, but Apple Pay failed once.", "path": "/checkout", "title": "Checkout", "platform": "ios", "visitorId": "STABLE_ANONYMOUS_UUID" } ``` - `rating` — required integer 1–5; everything else optional. - `path`/`title` — your screen name, so mobile screens join the same page analytics as web routes. - `platform` — `"ios"`, `"android"`, or `"api"` (default `"web"`); filterable in triage and CSV export. - `visitorId` — random UUID generated once, stored in Keychain / EncryptedSharedPreferences. Never a device identifier (IDFA/AAID) — anonymous and resettable keeps you clean under GDPR/KVKK. - `screenshot` — optional data-URL JPEG/PNG ≤ ~2 MB, only with explicit user consent. - **NPS** — `POST /api/collect/nps` with `{key, token, score: 0–10, comment?, visitorId, path?}`. - **Pageviews & actions** (no token needed) — `POST /api/collect/pageview` `{key, path, title?, visitorId}` and `POST /api/collect/action` `{key, action, path?, visitorId}`. **Drop-in native SDKs (optional):** two single-file, dependency-free wrappers implement the whole flow (anonymous id persistence, `X-Morvero-App-Id` header, token fetch + retry): - https://morvero.com/sdk/MorveroFeedback.swift — iOS 13+ / macOS 10.15+, URLSession only. - https://morvero.com/sdk/MorveroFeedback.kt — Android minSdk 21, HttpURLConnection + org.json only. ```swift MorveroFeedback.configure(origin: "https://morvero.com", key: "fbk_...", appId: "com.company.app") MorveroFeedback.trackScreen("/invoices") MorveroFeedback.trackAction("export-csv") MorveroFeedback.submitFeedback(rating: 4, comment: "Works well") ``` Every call is fire-and-forget on a background thread and never crashes the host app. Both files support the optional cohort label. **Restrict the key to your apps.** Keys are public by design. Web keys pin with allowed domains; native apps set allowed app IDs (product page → Mobile apps & REST API) and send `X-Morvero-App-Id` on every call. Both lists empty → any client. Only domains → browsers only. Only app IDs → mobile only. Both → either match passes. The header is a declaration, not proof — per-visitor caps and token expiry are the real abuse controls. **Limits:** 5 feedback items + 2 NPS answers per visitor per day; bursts return `429`. Treat any non-201 as "drop or queue, never crash". Kill switch: deactivating the product rejects the key everywhere. ## 6. Track AI agent pilots Enterprises pilot dozens of internal AI agents — support copilots, HR bots, SQL assistants — and nobody can say which are used, liked, or worth continuing. Morvero treats **each agent pilot as a product** with usage, sentiment, outcomes and cost on one scorecard. Quickstart: 1. Create a product with type **"AI agent pilot"** — it gets relabeled dashboards (conversations, active users, capabilities), a pilot scorecard, and a row in the Overview's AI agent pilots table. 2. Instrument the agent's backend with three HTTPS calls (usage telemetry needs no token; feedback uses the usual short-lived token): | Agent event | Morvero call | Notes | |---|---|---| | Conversation started | `POST /api/collect/pageview` | `path` = entry surface (`/slack`, `/web-chat`, `/vscode`) | | Capability / tool call | `POST /api/collect/action` | `action` = capability name; optional `cost` in USD | | Task outcome | `POST /api/collect/action` | reserved names: `task-completed`, `task-failed`, `escalated-to-human` — these drive the success/escalation KPIs | | User thumbs 👍/👎 | `POST /api/collect/feedback` | `thumb: "up"\|"down"` (or `rating` 1–5), `comment`, `platform: "agent"` | | Periodic CSAT | `POST /api/collect/nps` | 0–10, same as web NPS | - `visitorId` — pseudonymous per-employee id (salted hash of the employee id; never emails or names). - Rate allowance — usage telemetry is limited per source IP *per product key* (240/min each, 2,400/min per IP total), so one backend egress IP can report many pilots concurrently. - Feedback flows into triage tagged ✨ Agent — filterable, exportable, clustered by the AI backlog copilot alongside human feedback. **The pilot scorecard:** task success rate (completed ÷ decided), escalation rate (escalations ÷ conversations), cost per conversation, cost per successful task; the Overview ranks every pilot side by side. Fund the leaders, fix the frustrating, retire the unused — with evidence. **Drop-in emitter** (Node 18+, zero dependencies) — framework adapters just decide when to call it: ```js class MorveroAgent { constructor({ host, key, userId }) { Object.assign(this, { host, key, userId }); } #post(path, body) { // fire-and-forget: telemetry must never break the agent fetch(this.host + path, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ key: this.key, visitorId: this.userId, ...body }), }).catch(() => {}); } conversationStarted(surface = '/chat') { this.#post('/api/collect/pageview', { path: surface }); } capability(name, cost = 0, surface = '/chat') { this.#post('/api/collect/action', { action: name, cost, path: surface }); } taskCompleted() { this.capability('task-completed'); } taskFailed() { this.capability('task-failed'); } escalated() { this.capability('escalated-to-human'); } async thumbs(direction, comment = '', surface = '/chat') { const cfg = await (await fetch(`${this.host}/api/widget-config?key=${this.key}`)).json(); this.#post('/api/collect/feedback', { token: cfg.token, thumb: direction, comment, path: surface, platform: 'agent' }); } } const morvero = new MorveroAgent({ host: 'https://morvero.com', key: 'fbk_...', userId: 'emp-8f2a' }); ``` Adapters (treat as shapes — anywhere you can hook "run started / tool called / run ended", Morvero can score the pilot): - **Claude Agent SDK / Anthropic tool loop** — conversation per session, capability per tool call, cost from usage; `taskCompleted()` / `taskFailed()` / `escalated()` per your outcome logic. - **LangChain (JS)** — a callback handler mapping `handleChainStart` → `conversationStarted`, `handleToolStart` → `capability`, `handleLLMEnd` → `capability('turn', cost)`. - **OpenAI Agents (Python)** — the same three POSTs with `requests` from run hooks. ## 7. Connect AI coding agents (MCP) Morvero ships a remote **MCP server** so coding agents can read feedback data and instrument codebases themselves: ```bash claude mcp add --transport http morvero https://morvero.com/mcp ``` First use opens the browser: sign in (passkey or email link), review the consent screen, Allow. The connector acts with *your* role and workspace — OAuth 2.1 with PKCE, tokens stored hashed, revocable. Works with any MCP client supporting Streamable HTTP + OAuth (Claude Code, Claude.ai connectors, Cursor…). | Tool | Purpose | |---|---| | `list_products` / `create_product` | Find or create the product a codebase belongs to (apps and AI-agent pilots) | | `get_install_snippet` | Ready-to-paste widget snippet or agent-telemetry calls with the real API key, plus an AGENTS.md block to persist in the repo | | `get_instrumentation_guide` | Platform guides: web, iOS, Android, AI-agent conventions | | `list_feedback` | Real user comments & ratings, filterable | | `list_backlog` / `get_backlog_item` | AI-suggested backlog with acceptance criteria and verbatim user evidence | | `get_product_analytics` | Usage/sentiment totals + agent-pilot KPIs | | `verify_install` | Confirms data is actually arriving post-install — working / silent / never-received, naming the likely blocker. The agent's definition of done | Prompts that work well: "Add Morvero feedback tracking to this repo and verify it works" · "Implement the accepted backlog items for the Billing Portal" · "What are users complaining about on mobile? Fix the top issue." · "Instrument our support bot as an agent pilot". Working examples for every integration surface (vanilla HTML, React, Next.js, Vue, Swift/Kotlin, agent telemetry) plus a portable install skill: https://github.com/Morvero/examples The connector can read feedback and keys and create products; it can never touch billing or manage the team — it holds your role, nothing more. ## 8. AI: theme summaries & backlog suggestions Available when the server has an AI API key (Anthropic or OpenAI) and the plan is Growth+. AI output assists reading — raw feedback stays the source of truth. - **Theme summary** — clusters recent comments into named themes with sentiment and an example quote (regenerate at most daily). - **Backlog suggestions** — the AI analyzes a window (7/30/90 days, ≥10 comments), classifies every comment (praise, feature request, bug report, complaint, question), and proposes up to 8 backlog items: title, user story, 2–4 testable acceptance criteria grounded in the comments, type, and evidence-derived priority. Criteria travel into Jira / Azure DevOps on export. - **AI proposes, you dispose.** Every suggestion cites the real feedback behind it. Nothing enters the backlog or reaches Jira until a person clicks Accept; rejections teach the next run. - Accepting marks cited feedback *actioned*; rejecting marks it *dismissed*; actioned always wins conflicts. - Accepted items form a drag-to-reorder priority list; with Jira/Azure DevOps connected, accepted items export with evidence stats (AI-flagged bugs become Azure DevOps Bug work items). An auto-open switch can create tracker items automatically on accept. All decisions audit-logged. - Screenshots and form captures are never sent to the AI — only ratings, page paths, and comment text. ## 9. Read the dashboards - **Overview** — portfolio totals, usage/rating trends, products ranked by usage and rating, most/least used pages and functions, anomaly alerts. 7/30/90-day ranges. - **Portfolio scorecard** — usage × sentiment quadrant: **Fund** (high/high), **Fix** (used but disliked), **Niche** (loved but small), **Retire?** (low/low). Median-split signals, not verdicts. Movers panel shows biggest changes vs. the prior period (Growth+). - **Board report** (Growth+) — print-friendly executive report with a plain-language executive summary computed directly from the figures (no AI), stats with deltas, quadrant, movers, per-product scorecard, alerts, sample voices. - **NPS trend** (Scale+), **custom date ranges & quarter presets** (Growth+), **returning-visitors share**, **triage health** (volume by status, share triaged/actioned, median time-to-triage). - **Product detail** — per-product trend, rating distribution, page/function tables, embed snippet, recent feedback with attachments. - Reading signals: high usage + low rating = fix; low/low = retire candidate; low usage + high rating = niche gem — check who uses it first. Least-used functions are your dead-feature list. ## 10. Team, sign-in & passkeys - **Passwordless**: email → SAML SSO if your domain has it, else passkey, else single-use email link (15-min expiry). - **SAML SSO** (Scale+): claim your email domain, paste IdP SSO URL + signing certificate, give the IdP the shown Entity ID + ACS URL. Users provision on first sign-in. - Roles: **Owner** (governance), **Admin** (full product ops), **Member** (assigned products only), **Viewer** (read-only, no attachments, free — doesn't consume a paid seat). - **Tribes, POs, squads** (Growth+): tribes with tribe leads (auto member-access + per-tribe rollups), per-product Product Owner, squads serving products. A member's reach = assigned ∪ squad ∪ PO'd ∪ tribe-led products. - A **privacy-operator** flag lets a DPO manage retention/erasure without being Owner. Deactivating a teammate frees the seat and ends sessions instantly; the last active owner can never be removed. - Platform support can never enter a workspace unless an Owner grants a time-boxed access window; all staff actions land in your own audit trail. ## 11. Plans | Plan | Price | Products | Admins | Max sampling | |---|---|---|---|---| | Starter | Free | 2 | 2 | 5% | | Growth | $99/mo flat | 15 | 10 | 25% | | Scale | $499/mo flat | 50 | 25 | 50% | | Enterprise | from $15,000/yr flat | 150–unlimited | Unlimited | 100% | Always flat per portfolio — never per-user, per-MAU, or per-event. Starter→Growth→Scale are self-serve (Settings → Plan & usage); Enterprise is sales-assisted (annual invoice, SLA, DPA, security review support). Limits are API-enforced with a clear upgrade message. ## 12. Privacy & GDPR - **Retention** — 30–730 days; older raw data purged automatically. - **Workspace export** — everything as JSON (portability). - **Data subject requests** — a visitor's only identifier is the `fbk_visitor` value in their browser's localStorage; export or permanently erase everything tied to it (audit-logged). - No names, emails, IPs, or fingerprints are ever stored for visitors. - **Country codes** — resolved in-process from the request IP at collection time; the IP is discarded immediately, only the two-letter code is stored. Includes GeoLite2 data by MaxMind. ## 13. Security & abuse protection - Feedback requires a short-lived signed token minted per page load — replayed/scripted junk is rejected. - Rate limits on all collection endpoints (per IP + visitor with per-IP backstop) plus a 5-feedback-per-visitor-per-product daily cap. - Feedback-volume anomaly alerts flag possible rating poisoning on the Overview. - Per-product domain allowlists (web) and app-ID allowlists (native), product kill switch, strict payload validation, full admin audit log. - Disclosure policy and controls: https://morvero.com/security · https://morvero.com/.well-known/security.txt ## 14. Troubleshooting **Feedback isn't arriving** — Is the product active? Do allowed domains include the widget's host? One visitor caps at 5 feedback/product/day and IP bursts throttle — wait a minute. If the panel said "Thank you", it reached the server. **Function tracking isn't recording** — `MorveroFeedback.track()` inside the widget's own `