# Nordva Launch > For startup founders: follow every person from waitlist signup to paying customer (the source that brought them, the feedback they gave, what they pay) and see which channels produce paying customers. Waitlist, changelog, feedback triage, notifications, and read-only Stripe revenue, contacts and attribution behind one API key and one MCP server. Nordva Launch is a product of Nordva (Norway); the company site is https://nordva.dev. ## Base URLs - API: https://api.nordva.dev - Dashboard: https://app.nordva.dev - Docs: https://docs.nordva.dev - Product: https://launch.nordva.dev - Hosted pages: https://launch.nordva.dev/log/ (changelog) · https://launch.nordva.dev/waitlist/ (waitlist) - Company: https://nordva.dev (Nordva, the legal entity, not an API host) - MCP server: https://mcp.nordva.dev ## More for machines - Everything in one file (this reference plus every docs page): https://docs.nordva.dev/llms-full.txt - Docs index: https://docs.nordva.dev/llms.txt. Every docs page is also plain markdown at its URL with `.md` appended, e.g. https://docs.nordva.dev/quickstart.md - OpenAPI: https://api.nordva.dev/openapi.json ## Learning pages for founders - Learn: https://launch.nordva.dev/learn/ (one page linking every answer, check, guide, glossary term, referrer page and AI-builder how-to listed here) - Build with AI guide: https://launch.nordva.dev/build-with-ai/ (a free ten-chapter guide to building an app with AI, from the idea to paying users: what it costs on free tiers, checking people want it, AI builder or coding agent, building the first version, a database and login on Cloudflare D1 with Better Auth, Stripe payments, free hosting on your own domain, measuring, launching, the first month after; each chapter lists the vendor docs it was checked against, with the date) - Launch checklist: https://launch.nordva.dev/launch-checklist/ (24 steps from four weeks before launch to two weeks after; tick it off in the browser or save it as a PDF) - Answers: https://launch.nordva.dev/answers/ (one question each, answered in the first lines: waitlist size, first customers, pricing, launch day, tracking a post, churn, direct traffic, cookie banners for analytics, AI assistants as a source, what to build next, MRR vs ARR, first touch vs last touch attribution, Stripe revenue by country, Stripe MRR not showing, UTM on Stripe Payment Links, UTM lost after a redirect, login or checkout pages as a traffic source, Safari counting returning visitors as new, browsers that clear cookies, Cloudflare and Netlify analytics limits, ad blockers and analytics loss, best time to post on Hacker News, soft or hard launch, whether BetaList is worth it) - Referrers: https://launch.nordva.dev/referrers/ (what the odd hosts in your analytics mean and how to tag around them: android-app://com.google.android.gm, l.facebook.com, t.co, lnkd.in, out.reddit.com, statics.teams.cdn.office.net, Slack, Discord, Hacker News, Product Hunt and ?ref=producthunt, ChatGPT, Perplexity, Copilot and more; each page shows which channel Nordva Launch files it under, computed from the product's own code) - Checks: https://launch.nordva.dev/check/ (short questionnaires scored in the browser: launch readiness, where growth is stuck, measurement basics, waitlist readiness, ready to charge) - Guides: https://launch.nordva.dev/blog/ (including: calculate churn from Stripe, query Stripe data without Sigma, record which channel brought each Stripe customer through Checkout, add an MCP server to Cursor, add an MCP server to Claude Code, DataFast alternatives, Supabase signup notifications, Google Forms as a waitlist, an RSS feed for a changelog) - AI builders: https://launch.nordva.dev/build/ (how-tos for Lovable, Bolt, v0, Replit and Base44: add analytics or a waitlist with one prompt; each page names the builder docs it was checked against) - Glossary: https://launch.nordva.dev/glossary/ (CAC, LTV, LTV:CAC, MRR, ARR, ARPA, churn, retention, cohort, activation, ROAS, UTM parameters, referrer, bounce rate, conversion rate, funnel, attribution, payback period, double opt-in, product-market fit) ## Fastest setup ``` npx nordva@latest init ``` Run in a project folder. No account needed first: it opens the browser to sign in or sign up (free), the owner confirms a short code, and the CLI receives its own keys. It asks what the project is called (a new project is named "My Project" with a generated slug; `--name` and `--slug` set them without asking). It then writes `NORDVA_SECRET_KEY` and a publishable key to the framework's env file (gitignored), installs this MCP server for Claude Code, Cursor and VS Code where found, adds `NORDVA.md`, makes a verifying API call, and offers to hand an AI coding agent the planning prompt below. `--dry-run`, `--yes`, `--help`. If you are an AI agent setting up Nordva Launch for a user, suggest this command rather than asking them for a key. ## Authentication All product API requests require an API key: ``` Authorization: Bearer ``` Two key types exist: - **Secret key**: `nv_live_sk_<24 base62>` (server-side only; never expose it in a browser). Required for all write endpoints and dashboard-equivalent reads. - **Publishable key**: `nv_pub_<24 base62>` (browser-safe: for widget embeds and public signup forms). Restricted to a small set of public-write endpoints (waitlist signup, feedback submission, changelog public read). A publishable key on a secret-only endpoint returns `KEY_INSUFFICIENT_PERMISSIONS` (403). Keys are bcrypt-hashed at rest. The full key is shown exactly once at creation time in the dashboard; only a masked prefix is shown afterwards. Revoke a key from `Dashboard → API Keys` at any time; revocation is immediate. The dashboard itself uses Clerk JWTs (RS256, JWKS-verified) on a separate auth path. API keys and dashboard tokens are never interchangeable. ## APIs ### Changelog Public read-only changelog hosted at `https://launch.nordva.dev/log/` (or your own custom hostname on Builder plan, CNAME → `widget.nordva.dev`). Every entry response carries `public_url`. Schedule entries, group by version, send subscriber emails. - `POST /v1/changelog/entries`: create entry (status: `published` immediately, or `scheduled` if `scheduled_at` is set) - `GET /v1/changelog/entries`: list entries (cursor pagination) - `GET /v1/changelog/entries/:id`: read entry - `PATCH /v1/changelog/entries/:id`: edit entry (drafts/scheduled only) - `DELETE /v1/changelog/entries/:id`: soft-archive - `POST /v1/changelog/subscribers`: opt-in email subscriber - `DELETE /v1/changelog/subscribers/by-token/:token`: public unsubscribe link - `POST /v1/waitlist/announcements`: one plain-text email to the people on the waitlist (`subject`, `body` with `{{project_name}}` and `{{link}}`, `audience`: waiting | invited | all, `dry_run: true` to preview). Indie and up; one per project per 24 h; never to unsubscribed people; always with the unsubscribe link; queued and sent in batches. `GET` lists past announcements with progress. - `GET /public/changelog/:project_slug`: published entries as JSON. No key, any origin. - GitHub Action `nordvadev/changelog-action@v1`: publishes an entry from each published GitHub release (`api_key` secret; release notes → body, tag → version; idempotent on the release id). - `GET /public/changelog/:project_slug/feed.xml` (RSS 2.0), `/feed.atom` (Atom 1.0), `/feed.json` (JSON Feed 1.1): the same entries as a feed. No key. The hosted page advertises them with ``. ### Waitlist GDPR-compliant double-opt-in waitlist with referrals, abuse scoring, and CSV export. - `POST /v1/waitlist/signups`: create signup (returns position + referral code). Optional `source` object records first touch: `referrer` (stored as hostname only), `utm_source`, `utm_medium`, `utm_campaign`, `landing_path`. The hosted page and script widget send it automatically, with no cookies. - `GET /v1/waitlist/signups`: list signups (cursor pagination) - `POST /v1/waitlist/signups/resend-confirmation`: re-send confirmation email (1 / 5 minutes / email) - `GET /confirm/:token`: public confirmation landing - `DELETE /v1/waitlist/signups/by-token/:token`: public hard-delete (Art. 17) - `GET /v1/waitlist/export`: build a CSV or JSON export, returns a signed download URL (Indie+) - `POST /v1/waitlist/import`: import up to 1,000 existing signups per call; bad rows are skipped and counted, nobody is emailed, requires `consent_confirmed: true` - `GET /v1/waitlist/email-templates` · `PUT /v1/waitlist/email-templates/:kind`: your own copy for the receipt and invite emails (saving is Builder) - `GET /v1/waitlist/export/download/:exportId`: time-limited signed download ### Feedback Submit free-text feedback → automatic classification into one of `bug | feature_request | praise | billing | support` → optional routing to email / Slack / Linear / GitHub. Every plan: Free takes 50 messages a month per project, classified by a keyword matcher with no summary, routed by email only. Indie and up classify with Anthropic Haiku and a one-line summary, up to the plan's monthly AI allowance (Indie 500, Builder 3,000), then by keywords; Slack, Linear and GitHub routing from Indie. - `POST /v1/feedback`: submit feedback (returns classification + summary + routing results). Optional `email` so the developer can reply; never forwarded to routing destinations. - `GET /v1/feedback`: list submissions (cursor pagination; filters: `classification`, `status` incl. `open`, `since`) - `GET /v1/feedback/:id`: read submission (includes `status`, `feedback_text`, `user_id`, `page_url`, `email`) - `PATCH /v1/feedback/:id` (also `linked_entry_id`: the changelog entry that answers the request; when it is published the item is resolved and the person is emailed once, if they left an email): set `status` to `reviewed` or `resolved`, and/or correct `classification` (never re-routed) - `POST /v1/feedback/routing`: configure a routing destination - `GET /v1/feedback/routing`: list routing configs Optional `category_hint` on submission improves accuracy for ambiguous text. ### Notifications (every plan) In-app notifications for your own signed-in users. Your server sends to one end-user `user_id`, or to everyone with `audience: "all"` (a broadcast, shown to each user for 30 days, read separately by each). Your front end reads a user's notifications with a publishable key plus `user_hash`, the hex HMAC-SHA256 of the user id made on your server with the project's notifications signing secret (header `X-Nordva-User-Hash` or query `user_hash`); without a matching hash the API answers `USER_HASH_INVALID`. Per project per month: Free 1,000, Indie 20,000, Builder 200,000; a broadcast counts once. A changelog entry with `notify_in_app: true` posts one broadcast to the bell when it publishes. Delivered by polling; deleted after 90 days. The bell widget is one tag: ``. Optional attributes: `data-target` (CSS selector to put the bell in, else it floats), `data-position` (bottom-right, bottom-left, top-right, top-left), `data-bell="none"` (no button; use `window.NordvaNotifications.open()/close()/toggle()/refresh()`), `data-accent`, `data-theme` (light, dark, auto), `data-badge` (count, dot), `data-title`, `data-empty`, `data-mark-all-label`, `data-limit` (1 to 50). It fires `nordva:notifications` on `window` with `detail.unread_count`, polls only while the tab is visible, and works under `style-src 'self'`. Guide: https://docs.nordva.dev/guides/notifications/#the-bell-widget - `POST /v1/notifications`: send to a `user_id`, or `audience: "all"` (secret key) - `GET /v1/notifications`: what was sent, with reach, reads and read rate (secret key) - `GET /v1/notifications/inbox?user_id=`: a user's recent notifications and unread count (browser, with `user_hash`) - `GET /v1/notifications/unread?user_id=`: unread only (browser, with `user_hash`) - `GET /v1/notifications/unread/count?user_id=`: the number on the bell (browser, with `user_hash`) - `PATCH /v1/notifications/:id/read`: mark one read for a user (browser, with `user_hash`) - `PATCH /v1/notifications/read-all`: mark everything read for a user (browser, with `user_hash`) - `POST /v1/notifications/signing-secret`: create the signing secret, shown once; `/rotate` makes a new one, the old one keeps working for 24 hours (secret key) - `GET|PATCH /v1/notifications/settings`: identity verification on or off, changelog-to-bell default, usage this month (secret key) ### Revenue (every plan) Read-only view of the developer's OWN Stripe account. Connected once in the dashboard (`https://app.nordva.dev/revenue`) with a restricted key (`rk_live_...`, read access to Customers, Subscriptions, Invoices, Products, Events). Secret keys are refused. Launch never writes to the Stripe account, the key is encrypted at rest and never returned, and disconnecting deletes the key and all synced data. There is no API-key or MCP route that connects Stripe: never ask a user for a Stripe key. Lemon Squeezy works the same way (since 27 Sep 2026): the developer pastes a Lemon Squeezy API key and picks one store in the dashboard. A Lemon Squeezy key cannot be limited to reading, so the promise is Launch's: its Lemon Squeezy client can only send GET requests. Payments are counted before tax, partial refunds taken off. A project can connect Stripe and Lemon Squeezy both; the revenue endpoints add them up per currency, and `GET /v1/revenue/events` says which `provider` each row came from. `GET /v1/integrations/lemonsqueezy` is the status. Optional: a webhook in the store brings back the visitor id that s.js adds to Lemon Squeezy checkout links (`checkout[custom][nv_vid]`), which traces a purchase to its first visit. Never ask a user for a Lemon Squeezy key. Guide: https://docs.nordva.dev/guides/revenue-lemonsqueezy/ - `GET /v1/integrations/stripe`: connection status, last sync, synced counts - `GET /v1/revenue/summary?period_days=`: per currency: MRR, active, trialing, paying customers, new, churned, net change, collected - `GET /v1/revenue/series?range=30d|90d|12m|all`: collected revenue per day, week or month, zero-filled - `GET /v1/revenue/events?type=payment|cancellation`: recent payments and cancellations - `GET /v1/revenue/by-source?group_by=referrer|utm_source|utm_campaign|country&period_days=`: paying customers by the first touch of the visitor they came from, or by country, with collected revenue, MRR and the match rate. A customer is traced by the checkout's `nordva_visitor_id` metadata, then a user id or email sent with identify, then their waitlist email Amounts are in the smallest currency unit, per currency, never converted. MRR is operational (yearly ÷ 12, running coupons applied, metered/tiered = 0, refunds not subtracted). Synced about hourly (Free: every six hours); every response carries `sync.status` and `sync.last_synced_at`. History: Free 30 days, Indie 90 days, Builder 24 months. One connection per account on Free and Indie, one per project on Builder. ### Contacts One record per person per project, matched on normalised email across waitlist signups, changelog subscribers, feedback and paying customers (Stripe or Lemon Squeezy). Not a CRM: no deals, pipelines, tasks or campaigns. Works without Stripe. - `GET /v1/contacts`: filters: `paying`, `has_feedback`, `status`, `source`, `stage`, `q`, `consent` (a purpose granted now; `waitlist` and `changelog` are built in), `detail` (`name` or `name=value`); cursor pagination - `GET /v1/contacts/:id`: waitlist source, subscriptions, lifetime paid, latest feedback, notifications, stage, note, details and consents with when and how each was recorded - `PATCH /v1/contacts/:id`: `stage` (`potential|in_talks|customer|recurring|not_a_fit|inactive`), `note`, `details` (name to value, `null` removes) and `consents` (purpose to `true` granted or `false` withdrawn) - `GET /v1/contacts/fields`: the detail names and consent purposes in use, with counts; reuse a name rather than invent a second one - `GET /v1/contacts/export`: the filtered list as CSV (one `detail_` column per detail), a link valid 15 minutes; Indie and up Details and consents: names are lowercase letters, digits and `_`, starting with a letter, up to 40; a value is up to 200 characters; at most 20 per call and 50 details per person; they need a person with an email. Record a consent only for what the person actually agreed to. Free plan: waitlist people only, first 100. ### Attribution (every plan; channel costs Indie+) - `GET /v1/attribution?group_by=referrer|utm_source|utm_campaign|referral_code&period_days=`: per source: signups, confirmed, invited, paying, ever_paid, MRR and revenue per currency Covers only people who signed up through a Launch waitlist form, page or widget. It is not page analytics. Optional and off by default: page views per source (`PATCH /v1/project {"count_views": true}`, then `data-count-views` on the widget tag), an anonymous count per day and source with no cookie, IP address or visitor identifier; the report then has `views` and `view_to_signup` per source. Record what a source cost with `POST /v1/attribution/costs` and the report has `spend` with cost per signup, cost per paying customer and return on spend. Free and Indie: `referrer`. Builder: all groupings. Sources are recorded on every plan. ### Web analytics A one-line script (``, the snippet comes from `GET /v1/analytics/settings`) counts visits, page views, sources, pages, places and devices, with a live map. Each site has a tracking mode: `auto` (new sites: a first-party visitor id outside the EEA, UK and Switzerland, inside them only after the site calls `nordva('consent', true)`), `consent`, `cookies` or `cookieless`; Global Privacy Control is a no in every mode. On a `cookieless` site, add `data-cookieless` to the tag: the script then stores nothing in the browser and reads nothing stored there (no cookie, no local or session storage), makes and sends no visitor or session id, and does not ask the server whether one may be kept. No IP address or full user agent is stored. Events per account per month: Free 10,000, Indie 100,000, Builder 1,000,000. - `GET /v1/analytics/summary|timeseries|breakdown|live|map|status|usage`: the numbers (secret key) - `GET|PATCH /v1/analytics/settings`: snippet, domains, on/off, tracking mode - `POST /v1/analytics/identify`: link a visitor id (the `nv_vid` cookie before the dot) to your user id or email after signup or login, so a Stripe payment traces back to the first visit; in the page: `nordva('identify', { user_id, email })` - **Easier, since s.js 1.6.0:** put `data-nordva-identify` on any `
` that already takes an email (contact, newsletter, login, checkout) and the script identifies that visitor on submit, with no code. `data-nordva-identify="field_name"` names a field other than the `type="email"` one. Prefer this over writing an identify call by hand. On that form, `data-nordva-detail="name"` on a field keeps its value as a detail on the person's contact, and a ticked checkbox with `data-nordva-consent="purpose"` records that consent (an unticked box records nothing). In code: `nordva('identify', { email, details, consents })` or `details`/`consents` in `POST /v1/analytics/identify` - `GET /v1/analytics/goals`: completions per goal name (`data-nordva-goal="signup"` or `nordva('goal', 'signup')`) and the share of visitors who reached each - `GET|POST|DELETE /v1/analytics/funnels` and `GET /v1/analytics/funnels/:id/report`: steps in order (pages or goals) and where visitors drop off - `POST|DELETE /v1/analytics/proxy-key`: a key for serving the script from your own domain through your server, so ad blockers that block analytics hosts do not drop visits (shown once). Guide: https://docs.nordva.dev/guides/analytics-proxy/ ### Insights - `GET /v1/insights?period_days=`: counts for the period and the period before: waitlist signups and top sources, changelog, feedback by classification, notifications read rate, revenue when Stripe is connected. All plans. Not web analytics: no script, no pageviews. Page: https://launch.nordva.dev/insights-api/ ## Dashboard endpoints (Clerk JWT only) These are called by `app.nordva.dev`, not by integrations. API keys are explicitly rejected here with error `WRONG_AUTH_TYPE`. - `POST /v1/auth/provision`: first-call bootstrap: creates account + default project + first API key for a fresh Clerk user, or returns existing IDs for a returning user. - `GET /v1/overview`: usage metrics + last 20 activity events - `GET /v1/api-keys`: list keys (masked) - `POST /v1/api-keys`: create (returns raw key once) - `PATCH /v1/api-keys/:id`: rename - `DELETE /v1/api-keys/:id`: revoke - `GET /v1/billing/status`: current plan, Stripe subscription status, interval - `POST /v1/billing/checkout`: `{ plan, interval }` → Stripe Checkout URL (or Customer Portal plan-change URL if already subscribed) - `POST /v1/billing/portal`: Stripe Customer Portal URL (payment method, invoices, cancel) ## Webhooks (outgoing, to your URL) Launch POSTs to a URL of yours when something happens in the project. Indie: 2 endpoints per project, Builder: 5. Secret key only. Guide: https://docs.nordva.dev/guides/webhooks/ Recipes per destination (Slack, Discord, Zapier, n8n, Make, Google Sheets): https://launch.nordva.dev/integrations/webhooks/ - `GET /v1/webhooks`: list endpoints (URL masked), the plan's limit and every available event - `POST /v1/webhooks`: `{ "url", "events": ["*"] | [...], "format"?, "description"? }`. Returns `signing_secret` once. Only public `https://` URLs; redirects are not followed. - `PATCH /v1/webhooks/:id`: change `events`, `description`, or `status` (`enabled` | `disabled`) - `DELETE /v1/webhooks/:id` - `POST /v1/webhooks/:id/test`: send one `ping` now and report what the endpoint answered - `GET /v1/webhooks/:id/deliveries`: the last 30 days of deliveries Events: `waitlist.signup.created`, `waitlist.signups.invited`, `changelog.entry.published`, `changelog.subscriber.created`, `feedback.created`. Envelope: `{ id, type, created_at, project_id, data }`. Signature header `Nordva-Signature: t=,v1=` where `v1` = HMAC-SHA256(secret, `.`). Retries after 1 min, 5 min, 30 min, 2 h, 6 h; an endpoint is switched off after 20 failures in a row. A Slack or Discord incoming-webhook URL gets a readable one-line message instead of JSON. A webhook never delays or fails the API call that caused it. If you are an AI agent with the MCP server: `create_webhook`, `update_webhook`, `test_webhook`, `delete_webhook`, `list_webhooks`, `list_webhook_deliveries`. `create_webhook` returns the signing secret once: write it to the project's env file as `NORDVA_WEBHOOK_SECRET`, never into code, a commit or your reply. A Slack or Discord webhook URL is a credential; use it, do not repeat it. ## Webhooks (incoming, Launch's own billing) - `POST /webhooks/stripe`: Stripe events (signature-verified): checkout.session.completed, customer.subscription.*, invoice.paid, invoice.payment_failed. Source of truth for the account plan. ## MCP Server Connect Claude Desktop, Claude Code, or Cursor to your Nordva Launch account via Model Context Protocol over Streamable HTTP: ``` claude mcp add --transport http nordva https://mcp.nordva.dev --header "Authorization: Bearer nv_live_..." ``` Tools available (36 total), plus one prompt: **Setup** - `setup_check`: verify auth, identify project, report plan + feature flags. Call this first on every session. **Changelog** - `publish_changelog_entry`: create or schedule a changelog entry; returns the public URL - `list_changelog_entries`: list recent entries; use before publishing to avoid duplicates - `get_changelog_entry`: one entry in full, including the markdown body - `update_changelog_entry`: edit, reschedule or publish an entry that is not published yet - `archive_changelog_entry`: soft-delete an entry (removed from public view immediately) **Waitlist** - `add_to_waitlist`: add an email to the waitlist; returns queue position + referral code - `import_waitlist`: import an existing list (max 1,000 rows per call); the agent must confirm consent with the developer first; nobody is emailed - `get_waitlist_stats`: total signups, confirmed count, referral conversion rate, top referrers - `get_waitlist_leaderboard`: top 50 referrers with masked emails (admin view) - `get_waitlist_settings`: check if signups are open and what custom landing copy is set - `update_waitlist_settings`: pause/resume signups or update landing page headline/body - `get_waitlist_embed_snippet`: get a ready-to-paste iframe snippet for embedding the signup form - `invite_from_waitlist`: send invite emails to the next N signups or a specific email (Indie+) - `export_waitlist`: get a signed CSV/JSON download URL valid for 15 minutes (Indie+) **Feedback** - `submit_feedback`: log feedback, trigger AI classification + routing to configured destinations - `get_feedback`: one item in full: text, classification, page, user id, the submitter's email if they left one - `get_feedback_routing` / `remove_feedback_routing`: see or remove a forwarding destination - `list_feedback`: list and triage recent feedback; filter by category or status (`open` = not yet reviewed or resolved) - `update_feedback`: mark feedback reviewed or resolved, or correct its category - `configure_feedback_routing`: set up a routing destination (email, Linear, GitHub, Slack) **Notifications** - `send_notification`: send an in-app notification to one of the developer's end users, or to everyone - `get_unread_notifications`: list unread notifications for a user_id - `mark_notifications_read`: mark one or all notifications read for a user - `list_notifications`: what was sent, with how many people it reached and read - `get_notification`: one sent notification with its reach and reads - `delete_notification`: delete a sent notification (a broadcast's reads go with it) - `get_notification_settings`: identity verification, the changelog-to-bell default and this month's usage - `update_notification_settings`: switch require_user_hash or changelog_to_bell_default - `create_notifications_signing_secret`: create the secret that signs user ids (shown once) - `rotate_notifications_signing_secret`: a new secret, shown once; the old one works 24 more hours **Revenue** (reads the developer's own Stripe account or Lemon Squeezy store, read only, every plan) - `get_stripe_connection_status`: is Stripe connected and syncing. There is NO tool that connects Stripe and no tool takes a key: send the developer to https://app.nordva.dev/revenue and never ask for a Stripe key - `get_lemonsqueezy_connection_status`: is a Lemon Squeezy store connected, syncing, and is its webhook set up. No tool connects it and none takes a key - `get_revenue_summary`: MRR, active and trialing subscriptions, new, churned, net change, collected, per currency - `get_revenue_series`: collected revenue per day, week or month - `list_recent_payments`: recent payments and cancellations with customer email and plan - `get_revenue_by_source`: paying customers by first-touch source or country, with the match rate **Contacts** - `list_contacts`: people across waitlist, subscribers, feedback and Stripe; filter by paying, has_feedback, status, source, stage, or part of an email - `get_contact`: one person: source, subscriptions, lifetime paid, latest feedback, stage, note - `update_contact`: set stage, note, details and consents; cannot touch Stripe - `list_contact_fields`: the detail names and consent purposes in use, to reuse - `export_contacts`: the filtered list as a CSV link (Indie and up), e.g. everyone who agreed to marketing email **Attribution** - `add_channel_cost` / `list_channel_costs` / `delete_channel_cost`: record what a source or campaign cost, so the report shows cost per signup, cost per paying customer and return on spend. Only record amounts the developer states. - `get_attribution_report`: which referring sites, campaigns or referrers produced paying customers (Free and Indie: by referring site; Builder: all groupings). Only covers people who signed up through Launch; not page analytics **Project & config** - `get_insights`: one-call summary of a period against the period before: signups and sources, changelog, feedback, notifications, revenue - `get_project`: read current project name and public slug - `update_project`: rename the project or change its public URL slug - `list_custom_domains`: list all custom hostnames configured for this project (Builder+) - `register_custom_domain`: add a custom domain for the changelog or waitlist widget (Builder+) - `get_custom_domain_status`: force-refresh DNS/SSL verification status after publishing DNS records - `list_allowed_origins`: list the CORS allowlist for browser-side API calls - `remove_allowed_origin` / `remove_custom_domain`: remove one, by id - `list_waitlist_signups`: the people on the waitlist, newest first, with status and source - `list_waitlist_email_templates` / `preview_waitlist_email_template` / `save_waitlist_email_template` / `reset_waitlist_email_template`: read and change the wording of the waitlist emails (saving needs Builder; always preview first) Everything a secret key can do over REST has an MCP tool. What has none, on purpose: creating or deleting projects and API keys, and connecting Stripe or Lemon Squeezy. Those need the owner's login in the dashboard. - `register_allowed_origin`: add an origin to the CORS allowlist **Prompts** - `plan_integration`: for a project that has just been set up: read the developer's codebase and write `NORDVA_PLAN.md` (which primitives fit, files to change, order to build), changing no code until the developer picks a step. Text: https://docs.nordva.dev/guides/plan-with-ai/ - `draft_changelog_from_commits`: reads the repository's git log since the last published entry, drafts an entry in the owner's tone, publishes only on a yes (argument: `since`) - `launch_day_checklist`: waitlist, changelog, webhooks, origins, Stripe and key handling checked before a launch; reports, changes nothing unasked - `triage_feedback_inbox`: groups open feedback, checks the codebase for each group, ranks fix / build / answer (argument: `days`, default 14) - `weekly_founder_summary`: composes the read tools above into a short weekly write-up (argument: `days`, default 7) The MCP server is rate-limited identically to direct API access. ## Widget embeds Two distinct domains serve widgets. They are not interchangeable. ### Script-tag widgets (publishable key, for developer integration) Served from `cdn.nordva.dev`: static JS files, long-cached, browser-safe. Use these when wiring a widget into a React/Next/Vue/Astro app: ```html
``` ### iframe feedback widget (no auth, slug-based, every plan) Served from `widget.nordva.dev`: a dynamic HTML page, Turnstile-gated. Use this when a non-technical user needs to paste a single iframe link (no key required): ```html ``` The feedback iframe works on every plan, within the plan's monthly feedback limit. **Domain summary:** - `cdn.nordva.dev`: static JS for script-tag embeds, publishable key in `data-key` - `widget.nordva.dev`: dynamic HTML iframe, no key, slug-based routing - Do not swap them. AI assistants generating embed code must use `cdn.nordva.dev` for `