# FunnelFizz > FunnelFizz is a marketing funnel builder for solo founders and small SaaS teams. Map the AWARENESS → CONSIDERATION → TRIAL → CUSTOMER journey visually, split traffic by source/UTM/device/country/A-B/Stripe-product, track users end-to-end, and send email campaigns and automations tied to funnel stages. - [FunnelFizz](https://docs.funnelfizz.com/index.md) ## ai-agents ### hand-it-to-your-agent The exact prompt to paste into Claude, Cursor, or ChatGPT to have them set up FunnelFizz for you end-to-end. - [Hand it to your agent](https://docs.funnelfizz.com/ai-agents/hand-it-to-your-agent.md): The exact prompt to paste into Claude, Cursor, or ChatGPT to have them set up FunnelFizz for you end-to-end. ### llms-txt How FunnelFizz publishes its docs in a machine-readable format that agents can consume in one shot. - [llms.txt and llms-full.txt](https://docs.funnelfizz.com/ai-agents/llms-txt.md): How FunnelFizz publishes its docs in a machine-readable format that agents can consume in one shot. ### mcp-and-context7 Connect your agent to live FunnelFizz data via the Model Context Protocol, or via Context7's docs index. - [MCP & Context7](https://docs.funnelfizz.com/ai-agents/mcp-and-context7.md): Connect your agent to live FunnelFizz data via the Model Context Protocol, or via Context7's docs index. ### overview FunnelFizz docs are written for humans AND AI agents. Here's how to get the most out of them if you're working through Claude, Cursor, ChatGPT, or any other LLM-powered tool. - [For AI agents](https://docs.funnelfizz.com/ai-agents/overview.md): FunnelFizz docs are written for humans AND AI agents. Here's how to get the most out of them if you're working through Claude, Cursor, ChatGPT, or any other LLM-powered tool. ## concepts ### automations DAG-based workflows that fire on stage entry or Stripe events. Send emails, wait, branch, and converge. - [Automations](https://docs.funnelfizz.com/concepts/automations.md): DAG-based workflows that fire on stage entry or Stripe events. Send emails, wait, branch, and converge. ### conversion The three conversion positions between stages and the methods you can use to define each one. - [Conversion](https://docs.funnelfizz.com/concepts/conversion.md): The three conversion positions between stages and the methods you can use to define each one. ### email Send stage-triggered emails, drip automations, and churn-recovery campaigns — all native, no Mailchimp required. - [Email](https://docs.funnelfizz.com/concepts/email.md): Send stage-triggered emails, drip automations, and churn-recovery campaigns — all native, no Mailchimp required. ### full-funnel-tracking How FunnelFizz stitches anonymous sessions, identified users, and Stripe customers into one continuous journey. - [Full funnel tracking](https://docs.funnelfizz.com/concepts/full-funnel-tracking.md): How FunnelFizz stitches anonymous sessions, identified users, and Stripe customers into one continuous journey. ### splitting-and-extensions Split your funnel by traffic source, UTM, device, country, A/B variant, Stripe product, and more. Then track upsells with extensions. - [Splitting & extensions](https://docs.funnelfizz.com/concepts/splitting-and-extensions.md): Split your funnel by traffic source, UTM, device, country, A/B variant, Stripe product, and more. Then track upsells with extensions. ### stages AWARENESS, CONSIDERATION, TRIAL, CUSTOMER — the four fixed stages that every FunnelFizz funnel flows through. - [The four stages](https://docs.funnelfizz.com/concepts/stages.md): AWARENESS, CONSIDERATION, TRIAL, CUSTOMER — the four fixed stages that every FunnelFizz funnel flows through. ## faq Common questions about FunnelFizz — plans, data retention, privacy, limits, migration, and troubleshooting. - [FAQ](https://docs.funnelfizz.com/faq.md): Common questions about FunnelFizz — plans, data retention, privacy, limits, migration, and troubleshooting. ## features ### automation The DAG-based automation engine — drag-and-drop workflows triggered by stage entry or Stripe events. - [Automation](https://docs.funnelfizz.com/features/automation.md): The DAG-based automation engine — drag-and-drop workflows triggered by stage entry or Stripe events. ### email Build, send, and A/B test email campaigns targeting specific funnel stages. MJML editor, dynamic recipient lists, full tracking. - [Email](https://docs.funnelfizz.com/features/email.md): Build, send, and A/B test email campaigns targeting specific funnel stages. MJML editor, dynamic recipient lists, full tracking. ### extensions Track what happens after a customer becomes a customer — upsells, upgrades, expansion revenue. - [Extensions](https://docs.funnelfizz.com/features/extensions.md): Track what happens after a customer becomes a customer — upsells, upgrades, expansion revenue. ### mentions Monitor 13 sources for mentions of your brand — HackerNews, Reddit, X, YouTube, DEV.to, GoogleNews, StackExchange, and more. - [Mentions](https://docs.funnelfizz.com/features/mentions.md): Monitor 13 sources for mentions of your brand — HackerNews, Reddit, X, YouTube, DEV.to, GoogleNews, StackExchange, and more. ### providers Connect X, YouTube, LinkedIn, Instagram, TikTok, Reddit, Google Search Console, Google Ads, and Stripe. - [Providers](https://docs.funnelfizz.com/features/providers.md): Connect X, YouTube, LinkedIn, Instagram, TikTok, Reddit, Google Search Console, Google Ads, and Stripe. ### seo Dashboard widget showing Google Search Console-style metrics. Currently preview-only. - [SEO](https://docs.funnelfizz.com/features/seo.md): Dashboard widget showing Google Search Console-style metrics. Currently preview-only. ### splitting Eight ways to split your funnel — traffic source, UTM, channel, device, country, custom event, A/B variant, Stripe product. - [Splitting](https://docs.funnelfizz.com/features/splitting.md): Eight ways to split your funnel — traffic source, UTM, channel, device, country, custom event, A/B variant, Stripe product. ## quickstart ### connect-providers Connect X, YouTube, LinkedIn, Instagram, TikTok, Reddit, Google Search Console, and Google Ads so your AWARENESS stage shows real numbers. - [Connect providers](https://docs.funnelfizz.com/quickstart/connect-providers.md): Connect X, YouTube, LinkedIn, Instagram, TikTok, Reddit, Google Search Console, and Google Ads so your AWARENESS stage shows real numbers. ### connect-stripe Generate a restricted API key or install the FunnelFizz Stripe App to track trials, paid subscriptions, and churn. - [Connect Stripe](https://docs.funnelfizz.com/quickstart/connect-stripe.md): Generate a restricted API key or install the FunnelFizz Stripe App to track trials, paid subscriptions, and churn. ### install-tracking Paste the FunnelFizz tracking snippet into your site and watch your CONSIDERATION stage light up. - [Install tracking](https://docs.funnelfizz.com/quickstart/install-tracking.md): Paste the FunnelFizz tracking snippet into your site and watch your CONSIDERATION stage light up. ### overview Sign up, install tracking, connect Stripe, and launch your first funnel in about ten minutes. - [Quickstart](https://docs.funnelfizz.com/quickstart/overview.md): Sign up, install tracking, connect Stripe, and launch your first funnel in about ten minutes. ## tutorials ### ab-funnel-for-your-app Run real A/B tests at the funnel level — two hero variants, two onboarding flows, two pricing pages — and compare conversion rates apples-to-apples. - [Creating an A/B funnel for your app](https://docs.funnelfizz.com/tutorials/ab-funnel-for-your-app.md): Run real A/B tests at the funnel level — two hero variants, two onboarding flows, two pricing pages — and compare conversion rates apples-to-apples. ### agentic-funnels Let Claude, Cursor, or ChatGPT set up FunnelFizz for you, or write custom events from your agent's codegen flow. - [Agentic funnels basics](https://docs.funnelfizz.com/tutorials/agentic-funnels.md): Let Claude, Cursor, or ChatGPT set up FunnelFizz for you, or write custom events from your agent's codegen flow. ### building-a-basic-saas-funnel End-to-end walkthrough — from signup to first paid customer — for a one-person SaaS launch. - [Building a basic SaaS funnel](https://docs.funnelfizz.com/tutorials/building-a-basic-saas-funnel.md): End-to-end walkthrough — from signup to first paid customer — for a one-person SaaS launch. ### email-drip-campaign-basics From zero to sent — create, send, and measure your first drip campaign in FunnelFizz. - [Email drip campaign basics](https://docs.funnelfizz.com/tutorials/email-drip-campaign-basics.md): From zero to sent — create, send, and measure your first drip campaign in FunnelFizz. ### geography-behavior-targeting Use nested splits and custom events to zero in on specific cohorts — US mobile users who clicked pricing, EU trial users who viewed docs, etc. - [Targeting users by geography, behavior, and actions](https://docs.funnelfizz.com/tutorials/geography-behavior-targeting.md): Use nested splits and custom events to zero in on specific cohorts — US mobile users who clicked pricing, EU trial users who viewed docs, etc. ### identifying-funnel-gaps A systematic read-the-numbers walkthrough for spotting where your funnel is leaking and what to fix first. - [Identifying gaps in your funnel](https://docs.funnelfizz.com/tutorials/identifying-funnel-gaps.md): A systematic read-the-numbers walkthrough for spotting where your funnel is leaking and what to fix first. ### migrating-from-brevo-hubspot Export your contacts, import them into FunnelFizz, and flip your drip campaigns over without losing history. - [Migrating from Brevo / HubSpot to FunnelFizz](https://docs.funnelfizz.com/tutorials/migrating-from-brevo-hubspot.md): Export your contacts, import them into FunnelFizz, and flip your drip campaigns over without losing history. ### saas-automations The three automations every SaaS needs — trial welcome, trial-end nudge, and churn recovery — built from scratch. - [Setting up SaaS funnel automations](https://docs.funnelfizz.com/tutorials/saas-automations.md): The three automations every SaaS needs — trial welcome, trial-end nudge, and churn recovery — built from scratch. ### stripe-basic-setup The canonical Stripe-connect walkthrough — for humans and for agents following /skill.md. - [Stripe basic setup for SaaS founders and agents](https://docs.funnelfizz.com/tutorials/stripe-basic-setup.md): The canonical Stripe-connect walkthrough — for humans and for agents following /skill.md. ### tracking-custom-events Fire the right events at the right time. A framework for instrumenting your app without drowning in noise. - [Tracking & custom events setup](https://docs.funnelfizz.com/tutorials/tracking-custom-events.md): Fire the right events at the right time. A framework for instrumenting your app without drowning in noise. --- # Full Documentation Content Copy as markdown[View .md](https://docs.funnelfizz.com/ai-agents/hand-it-to-your-agent "View the raw markdown for this page")[Open in Claude](https://claude.ai/new?q=Read%20https%3A%2F%2Fdocs.funnelfizz.com%2Fai-agents%2Fhand-it-to-your-agent.md%20and%20help%20me%20with%20this%20FunnelFizz%20topic%3A%20Hand%20it%20to%20your%20agent "Open this page in Claude with context")[Open in ChatGPT](https://chat.openai.com/?q=Read%20https%3A%2F%2Fdocs.funnelfizz.com%2Fai-agents%2Fhand-it-to-your-agent.md%20and%20help%20me%20with%20this%20FunnelFizz%20topic%3A%20Hand%20it%20to%20your%20agent "Open this page in ChatGPT with context") # Hand it to your agent The simplest possible flow: paste a prompt into your AI coding agent, and it handles signup, tracking, Stripe, providers, and your first automation. ## The prompt[​](#the-prompt "Direct link to The prompt") Paste this verbatim into Claude Code, Cursor, Windsurf, ChatGPT, or any other AI coding agent: ``` Read https://docs.funnelfizz.com/skill.md and follow the skill exactly. Walk me through each step. Stop and ask me before doing anything destructive or anything that requires my credentials. ``` That's it. The agent will: 1. Fetch [skill.md](https://docs.funnelfizz.com/skill) and parse it. 2. Ask you the preliminaries it needs to know (your domain, what framework you're on, whether you have Stripe set up). 3. Walk you through signup and onboarding. 4. Write the tracking snippet into your codebase correctly for your framework. 5. Walk you through creating a Stripe restricted key with the right seven scopes. 6. Help connect providers (X, YouTube, GSC, whatever you use). 7. Write `funnelfizz('identify', ...)` calls into your auth layer. 8. Instrument custom events in key places. 9. Build a starter trial welcome automation. ## What the agent sees[​](#what-the-agent-sees "Direct link to What the agent sees") [skill.md](https://docs.funnelfizz.com/skill) is a \~2500-word self-contained runbook covering every step above. It's specifically designed for one-shot agent consumption — no cross-page dependencies, no navigation chrome. The agent reads it once and has everything it needs. ## What you still need to do[​](#what-you-still-need-to-do "Direct link to What you still need to do") Some things your agent can't do for you: * **Create the FunnelFizz account** — you have to sign up yourself at funnelfizz.com (agents generally don't handle email-verification flows well). * **Create the Stripe restricted key** — you'll click through Stripe's UI. The agent will guide you but won't click buttons for you. * **OAuth provider connects** — you'll sign into X / YouTube / LinkedIn / etc. yourself. The agent will kick off each one and wait. * **Approve changes to your codebase** — the agent will propose code changes. You confirm. Everything else — reading snippets, writing code, generating config, drafting email copy — the agent handles. ## Tips for best results[​](#tips-for-best-results "Direct link to Tips for best results") ### Give it your codebase context upfront[​](#give-it-your-codebase-context-upfront "Direct link to Give it your codebase context upfront") ``` This is a Next.js 14 App Router project with next-auth. I'm on the Hobby plan. I use Stripe for billing. My domain is widgetspro.com, marketing is at the root, app is at app.widgetspro.com. ``` Saves several rounds of back-and-forth. ### Tell it what "activation" means for your product[​](#tell-it-what-activation-means-for-your-product "Direct link to Tell it what \"activation\" means for your product") The skill file mentions firing an `activation` custom event. Activation is product-specific. Tell the agent: ``` For my product, "activation" means the user created their first project and invited at least one teammate. Please instrument those two events. ``` ### Ask it to commit along the way[​](#ask-it-to-commit-along-the-way "Direct link to Ask it to commit along the way") ``` After each major change (tracking install, identify call, event instrumentation), make a commit with a clear message. Don't bundle unrelated changes into one commit. ``` ### Double-check the Stripe scopes[​](#double-check-the-stripe-scopes "Direct link to Double-check the Stripe scopes") Stripe keys are the most common failure point. When the agent asks you to create the restricted key: * Name it `FunnelFizz (read-only)`. * Turn **Read** on for exactly these 7: 1. Customers 2. Charges and Refunds 3. Events 4. Products and Prices 5. Invoices 6. Subscriptions 7. Balance * Leave everything else **None**. If FunnelFizz says "missing scopes," the validation tells you exactly which ones — go back to Stripe, edit the key, add them. ## Security[​](#security "Direct link to Security") Your agent should NEVER: * Commit your Stripe key to a repo. * Send your key, OAuth tokens, or session cookies to any third-party service. * Create Stripe accounts or sub-accounts on your behalf. skill.md explicitly instructs agents on these boundaries. If you see your agent about to violate one of them, stop it and reset. ## If something goes wrong[​](#if-something-goes-wrong "Direct link to If something goes wrong") Ask the agent: ``` That didn't work. Paste the error you got into the context and walk me through diagnosis. Don't retry blindly — let's understand what's happening first. ``` The most common failures: | Symptom | Likely cause | | ------------------------------------- | -------------------------------------------------------------------- | | Tracking snippet not verifying | Meta tag outside ``, subdomain mismatch, CSP blocking script | | Stripe key rejected | Missing one of 7 scopes — FunnelFizz tells you which | | `identify` never fires | Script not loaded on authenticated pages, or auth state check misses | | Custom events not appearing in splits | Events fired before `identify`, so attributed to anonymous visitor | ## For agents writing this kind of skill file themselves[​](#for-agents-writing-this-kind-of-skill-file-themselves "Direct link to For agents writing this kind of skill file themselves") If you're an agent or developer wondering how to write a skill.md for your own product, the canonical template is ours: [https://docs.funnelfizz.com/skill.md](https://docs.funnelfizz.com/skill). Moltbook's version ([moltbook.com/skill.md](https://www.moltbook.com/skill.md)) is also excellent. Key principles: * **Self-contained.** No cross-page links the agent needs to fetch. * **Ordered.** Steps must run in sequence; dependencies explicit. * **Conservative.** Err on the side of asking the user before destructive actions. * **Versioned.** Include a `Version:` line. Update when your product changes. *** **Next:** [llms.txt convention](https://docs.funnelfizz.com/ai-agents/llms-txt.md) · [MCP & Context7](https://docs.funnelfizz.com/ai-agents/mcp-and-context7.md) --- Copy as markdown[View .md](https://docs.funnelfizz.com/ai-agents/llms-txt "View the raw markdown for this page")[Open in Claude](https://claude.ai/new?q=Read%20https%3A%2F%2Fdocs.funnelfizz.com%2Fai-agents%2Fllms-txt.md%20and%20help%20me%20with%20this%20FunnelFizz%20topic%3A%20llms.txt%20and%20llms-full.txt "Open this page in Claude with context")[Open in ChatGPT](https://chat.openai.com/?q=Read%20https%3A%2F%2Fdocs.funnelfizz.com%2Fai-agents%2Fllms-txt.md%20and%20help%20me%20with%20this%20FunnelFizz%20topic%3A%20llms.txt%20and%20llms-full.txt "Open this page in ChatGPT with context") # llms.txt and llms-full.txt FunnelFizz publishes its docs in two machine-readable formats following the [llms.txt convention](https://llmstxt.org) proposed by Jeremy Howard (Answer.AI) in 2024: ## The two files[​](#the-two-files "Direct link to The two files") ### `/llms.txt` — the index[​](#llmstxt--the-index "Direct link to llmstxt--the-index") **URL:** A sparse, structured index of every docs page. Each entry has a title and a one-line summary. Use this when your agent needs to know *what pages exist*, not *what they say*. Size: \~3-5KB. Fits into a small part of any agent's context. ### `/llms-full.txt` — the corpus[​](#llms-fulltxt--the-corpus "Direct link to llms-fulltxt--the-corpus") **URL:** The entire docs corpus concatenated into one markdown file. Use this when your agent needs the full knowledge of FunnelFizz in context — e.g., to answer general questions, to understand how features interact, to write code that uses FunnelFizz. Size: depends on how many docs there are at any given time (we'll keep it under 200KB / 50K tokens so it fits in most context windows). ## When to use which[​](#when-to-use-which "Direct link to When to use which") **Use `llms.txt`** when: * Your agent just needs a map to decide which page to fetch next. * Context is tight and you don't want to pay the token cost of the full corpus. * You're building an agentic workflow that iteratively fetches relevant pages. **Use `llms-full.txt`** when: * You want the agent to have everything in context from the start. * You're doing one-shot "set up FunnelFizz" or "answer a question about FunnelFizz" and don't need precision about which page. * Your context window is big enough (Claude Opus 1M, Gemini 1.5 Pro, GPT-4.1, etc.). ## How to use in an agent[​](#how-to-use-in-an-agent "Direct link to How to use in an agent") ### Claude Code / Cursor / Windsurf[​](#claude-code--cursor--windsurf "Direct link to Claude Code / Cursor / Windsurf") ``` Read https://docs.funnelfizz.com/llms-full.txt and answer my questions about FunnelFizz from that context. ``` ### ChatGPT with web browsing[​](#chatgpt-with-web-browsing "Direct link to ChatGPT with web browsing") ``` Fetch https://docs.funnelfizz.com/llms.txt. Use the index to decide which pages to fetch for my specific question, then fetch those page URLs (they all support .md suffix for clean markdown). ``` ### Custom agent code[​](#custom-agent-code "Direct link to Custom agent code") ``` import urllib.request llms_txt = urllib.request.urlopen("https://docs.funnelfizz.com/llms-full.txt").read().decode() # Pass llms_txt as system prompt or context ``` ## What's in the index[​](#whats-in-the-index "Direct link to What's in the index") Our `llms.txt` is structured with sections matching the docs sidebar: * Quickstart * Concepts * Features * Tutorials * AI agents * FAQ Each entry is a markdown link to the page's `.md` version (the clean markdown-only route), followed by a one-line description. Example excerpt: ``` # FunnelFizz > FunnelFizz is a marketing funnel builder for solo founders and small SaaS teams. Map the > AWARENESS → CONSIDERATION → TRIAL → CUSTOMER journey visually, split traffic by > source/UTM/device/country/A-B/Stripe-product, track users end-to-end, and send email > campaigns and automations tied to funnel stages. ## Quickstart - [Overview](https://docs.funnelfizz.com/quickstart/overview.md) — Sign up, install tracking, connect Stripe, and launch your first funnel. - [Install tracking](https://docs.funnelfizz.com/quickstart/install-tracking.md) — Paste the snippet and verify pageviews are flowing. - [Connect Stripe](https://docs.funnelfizz.com/quickstart/connect-stripe.md) — The seven required scopes and two ways to connect. ... ``` ## Per-page markdown[​](#per-page-markdown "Direct link to Per-page markdown") Every docs page has a `.md` version. For example: * Page: `https://docs.funnelfizz.com/concepts/stages` * Markdown: `https://docs.funnelfizz.com/concepts/stages.md` The markdown version is stripped of all HTML chrome (navigation, footer, sidebar), so it's the cheapest thing to include in an agent's context when you want to reference a single topic. ## Regeneration[​](#regeneration "Direct link to Regeneration") Both files regenerate on every docs build (automatic on every PR merge), so they're always in sync with the published docs. ## Agent directive[​](#agent-directive "Direct link to Agent directive") We include an "agent directive" at the top of `llms.txt` — a few lines of instructions for agents reading the file. Things like: ``` - FunnelFizz plans: FREE, HOBBY, PRO. There is no TEAM plan — do not suggest one. - Domain: funnelfizz.com (.com, not .app). - Brand: "FunnelFizz" in PascalCase for user-facing copy. - The 4 stages are AWARENESS → CONSIDERATION → TRIAL → CUSTOMER. Fixed order. Do not invent additional stages. - Stripe connection uses restricted keys (rk_live_...) with 7 specific read scopes. See https://docs.funnelfizz.com/skill.md for the exact list. ``` This is the Stripe-pioneered pattern: use the top of llms.txt to correct common agent mistakes or preferences. It applies before the agent even reads the page content. ## Related[​](#related "Direct link to Related") * [llms.txt spec (llmstxt.org)](https://llmstxt.org) * [AI agents → Hand it to your agent](https://docs.funnelfizz.com/ai-agents/hand-it-to-your-agent.md) * [AI agents → MCP & Context7](https://docs.funnelfizz.com/ai-agents/mcp-and-context7.md) --- Copy as markdown[View .md](https://docs.funnelfizz.com/ai-agents/mcp-and-context7 "View the raw markdown for this page")[Open in Claude](https://claude.ai/new?q=Read%20https%3A%2F%2Fdocs.funnelfizz.com%2Fai-agents%2Fmcp-and-context7.md%20and%20help%20me%20with%20this%20FunnelFizz%20topic%3A%20MCP%20%26%20Context7 "Open this page in Claude with context")[Open in ChatGPT](https://chat.openai.com/?q=Read%20https%3A%2F%2Fdocs.funnelfizz.com%2Fai-agents%2Fmcp-and-context7.md%20and%20help%20me%20with%20this%20FunnelFizz%20topic%3A%20MCP%20%26%20Context7 "Open this page in ChatGPT with context") # MCP & Context7 The FunnelFizz Model Context Protocol (MCP) server lets your agent (Claude Desktop, Cursor, Windsurf, ChatGPT, Zed) **set up and read your funnel** from inside the agent — no copy-pasting URLs or numbers. ## What it does[​](#what-it-does "Direct link to What it does") **Phase 1 — set up your funnel** (available on FREE+): The agent walks you from "I just signed up" to "every connection is verified": tracking sites, sender domains, Stripe restricted key, and OAuth for X / YouTube / LinkedIn / Instagram / TikTok / Reddit / Google Search Console / Google Ads. **Phase 2 — answer questions about your funnel** (HOBBY+ for metrics, PRO for profile reads): ``` "Which funnel has the worst trial-to-paid conversion this week?" "Show me the journey for the profile that just signed up." "Compare conversion across my three funnels for the last 30 days." ``` ## What it can't do (by design)[​](#what-it-cant-do-by-design "Direct link to What it can't do (by design)") * Sending email (test or production) * Creating, editing, or activating automations * Creating or editing funnels * Mutating profile data * Deletions of any kind * Billing, plan changes, or workspace membership A leaked API key cannot send email, modify your funnels, or destroy data — the worst case is an attacker registers a tracking site or starts an OAuth flow they can't complete. ## Install[​](#install "Direct link to Install") ### 1. Generate an API key[​](#1-generate-an-api-key "Direct link to 1. Generate an API key") [Settings → Developer → New API key](https://funnelfizz.com/settings/developer). Name it after the agent you'll paste it into ("Claude Desktop on my MacBook"). Keys are workspace-scoped, hashed at rest, and revocable from the same page. ### 2. Pick a transport[​](#2-pick-a-transport "Direct link to 2. Pick a transport") #### Remote (recommended)[​](#remote-recommended "Direct link to Remote (recommended)") Hosted at `https://mcp.funnelfizz.com`. Updates push instantly; no install. ``` { "mcpServers": { "funnelfizz": { "type": "streamable-http", "url": "https://mcp.funnelfizz.com", "headers": { "Authorization": "Bearer ff_YOUR_KEY_HERE" } } } } ``` #### Local (stdio via npm)[​](#local-stdio-via-npm "Direct link to Local (stdio via npm)") ``` { "mcpServers": { "funnelfizz": { "command": "npx", "args": ["-y", "@funnelfizz/mcp"], "env": { "FUNNELFIZZ_API_KEY": "ff_YOUR_KEY_HERE" } } } } ``` ### 3. One-click install URLs[​](#3-one-click-install-urls "Direct link to 3. One-click install URLs") For agents that support deep links: * **Claude Desktop:** `claude://mcp/install?url=https://mcp.funnelfizz.com&name=funnelfizz&authType=bearer` * **Cursor:** `cursor://install-mcp?name=funnelfizz&url=https://mcp.funnelfizz.com` * **Windsurf:** `windsurf://install-mcp?name=funnelfizz&url=https://mcp.funnelfizz.com` Each opens the agent's MCP config UI with FunnelFizz pre-filled. You still paste your API key. ## The full tool list[​](#the-full-tool-list "Direct link to The full tool list") ### Setup tools (Phase 1)[​](#setup-tools-phase-1 "Direct link to Setup tools (Phase 1)") | Tool | What it does | | -------------------------------------------------------------------------- | -------------------------------------------- | | `get_setup_status` | Workspace-level checklist — call this first. | | `add_tracking_site` / `check_tracking_site` / `list_tracking_sites` | Install + verify the tracking snippet. | | `add_email_sender` / `list_email_senders` | Register a sender address. | | `add_email_domain` / `verify_email_domain` / `list_email_domains` | Register + verify a sender domain. | | `get_stripe_setup_guide` / `connect_stripe` / `get_stripe_connection` | Stripe restricted-key flow. | | `start_provider_oauth` / `check_provider_connection` / `list_integrations` | OAuth providers. | ### Insight tools (Phase 2)[​](#insight-tools-phase-2 "Direct link to Insight tools (Phase 2)") | Tool | What it does | Plan | | -------------------------------------- | ------------------------------------------------------------------------------------------- | ------ | | `list_funnels` / `get_funnel_overview` | List + summarise funnels. | HOBBY+ | | `get_funnel_metrics` | Stage counts + conversion rates, optional split by traffic\_source / utm / stripe\_product. | HOBBY+ | | `compare_funnels` | Side-by-side conversion across up to 10 funnels. | HOBBY+ | | `get_workspace_overview` | Cross-funnel rollup + top funnels. | HOBBY+ | | `search_profiles` | Paginated profile search by email/name. | PRO | | `get_profile_journey` | Latest 100 events for a profile (PII redaction supported). | PRO | ## Pre-baked prompts[​](#pre-baked-prompts "Direct link to Pre-baked prompts") Pre-canned workflows your agent can run: * `setup_funnelfizz` — full onboarding walk * `connect_tracking` / `connect_stripe` / `connect_email_sending` / `connect_provider` / `setup_seo` * `weekly_review` — multi-funnel summary * `diagnose_funnel` — pinpoint why a funnel is underperforming * `find_dropoff` — locate the biggest conversion leak ## Authentication[​](#authentication "Direct link to Authentication") API key, scoped to workspace: * Format: `ff_…` + 48 base64url chars * Hashed at rest (SHA-256); cleartext shown once at creation * Per-key scopes: `setup:*` (Phase 1), `read:funnels` / `read:metrics` / `read:profiles` (Phase 2) * Per-plan rate limits: FREE 30/min + 5k/month, HOBBY 60/min + 50k/month, PRO 300/min + 500k/month * Per-call audit log surfaced in Settings → Developer → Activity ## Context7[​](#context7 "Direct link to Context7") FunnelFizz is also indexed in [Context7](https://context7.com), the MCP-based docs search service by Upstash. Agents with Context7 enabled can query: ``` use context7 funnelfizz ``` and get authoritative doc excerpts. This is complementary to our MCP server — Context7 serves docs, our MCP serves your live workspace. ### Our context7.json[​](#our-context7json "Direct link to Our context7.json") ``` { "$schema": "https://context7.com/schema/context7.json", "projectTitle": "FunnelFizz", "description": "Marketing funnel builder for solo founders and small SaaS teams.", "folders": ["docs/docs"], "excludeFolders": ["docs/node_modules", "docs/.docusaurus"], "excludeFiles": [], "rules": [ "Plans are FREE, HOBBY, PRO only. There is no TEAM plan.", "Domain is funnelfizz.com (not .app).", "Brand is FunnelFizz (PascalCase) in user-facing copy.", "The 4 stages are AWARENESS → CONSIDERATION → TRIAL → CUSTOMER in fixed order.", "Stripe connection uses restricted keys (rk_live_...) with exactly 7 read scopes." ], "previousVersions": [] } ``` ## Related[​](#related "Direct link to Related") * [MCP homepage](https://modelcontextprotocol.io) * [Context7 homepage](https://context7.com) * [AI agents → Overview](https://docs.funnelfizz.com/ai-agents/overview.md) * [Hand it to your agent](https://docs.funnelfizz.com/ai-agents/hand-it-to-your-agent.md) * [`llms.txt`](https://docs.funnelfizz.com/ai-agents/llms-txt.md) --- Copy as markdown[View .md](https://docs.funnelfizz.com/ai-agents/overview "View the raw markdown for this page")[Open in Claude](https://claude.ai/new?q=Read%20https%3A%2F%2Fdocs.funnelfizz.com%2Fai-agents%2Foverview.md%20and%20help%20me%20with%20this%20FunnelFizz%20topic%3A%20For%20AI%20agents%20%E2%80%94%20overview "Open this page in Claude with context")[Open in ChatGPT](https://chat.openai.com/?q=Read%20https%3A%2F%2Fdocs.funnelfizz.com%2Fai-agents%2Foverview.md%20and%20help%20me%20with%20this%20FunnelFizz%20topic%3A%20For%20AI%20agents%20%E2%80%94%20overview "Open this page in ChatGPT with context") # For AI agents These docs are designed to be consumed by AI coding agents — Claude Code, Cursor, Windsurf, ChatGPT, Copilot, Gemini, anything — as first-class readers alongside humans. If you're an agent reading this, you're in the right place. If you're a human thinking about how to use your agent with FunnelFizz, this is also for you. ## The three agent-facing artifacts[​](#the-three-agent-facing-artifacts "Direct link to The three agent-facing artifacts") ### 1. `/skill.md` — the single-file setup bundle[​](#1-skillmd--the-single-file-setup-bundle "Direct link to 1-skillmd--the-single-file-setup-bundle") **URL:** [https://docs.funnelfizz.com/skill.md](https://docs.funnelfizz.com/skill) Drop this URL into your agent and say *"follow this to set up FunnelFizz."* It's a 2,500-word self-contained runbook that teaches the agent to walk you through signup → tracking install → Stripe connect → provider connect → sending domain → identify + custom events → first automation. The file is designed to be pasted whole into an agent's context. No cross-page dependencies, no navigation chrome, no boilerplate. Just instructions. Inspired by Moltbook's [skill.md](https://www.moltbook.com/skill.md) pattern. → [Full walkthrough of how to hand it to your agent](https://docs.funnelfizz.com/ai-agents/hand-it-to-your-agent.md) ### 2. `/llms.txt` and `/llms-full.txt` — the docs corpus[​](#2-llmstxt-and-llms-fulltxt--the-docs-corpus "Direct link to 2-llmstxt-and-llms-fulltxt--the-docs-corpus") **URLs:** * — index of every page with one-line summaries * — the entire docs corpus as one markdown file Following the [llms.txt](https://llmstxt.org) convention proposed by Jeremy Howard / Answer.AI, we emit both a sparse index (`llms.txt`) and a dense dump (`llms-full.txt`). **Use `llms.txt` when** your agent only needs a map — "which page should I read about splits?" **Use `llms-full.txt` when** your agent needs the whole corpus in context — "answer general questions about FunnelFizz." Both files are regenerated on every docs build so they're always current. → [Details on the llms.txt convention and when to use which](https://docs.funnelfizz.com/ai-agents/llms-txt.md) ### 3. Per-page `.md` routes — clean markdown for any page[​](#3-per-page-md-routes--clean-markdown-for-any-page "Direct link to 3-per-page-md-routes--clean-markdown-for-any-page") **Pattern:** any page URL + `.md` → the markdown source, minus navigation and footer chrome. Examples: * Page: `https://docs.funnelfizz.com/concepts/stages` * Markdown: `https://docs.funnelfizz.com/concepts/stages.md` These are auto-generated from the source MDX and stripped of all HTML wrapping. They're the cleanest thing to paste into an LLM when you want it to answer a question about a specific topic without burning tokens on site chrome. ## The "contextual menu" on every page[​](#the-contextual-menu-on-every-page "Direct link to The \"contextual menu\" on every page") At the top of every docs page (after the title), you'll see a small row of buttons: * **Copy as markdown** — copies the clean `.md` version to your clipboard. * **Open in Claude** — opens `claude.ai` with the page preloaded as context. * **Open in ChatGPT** — same, for `chat.openai.com`. * **Copy MCP URL** — copies the FunnelFizz MCP server URL (see below). *(These are added via a swizzled DocItem footer component; if you don't see them yet, they may not be shipped in your build.)* ## MCP server[​](#mcp-server "Direct link to MCP server") We ship a [Model Context Protocol](https://modelcontextprotocol.io) server that exposes read-only FunnelFizz state to compatible agents (Claude Desktop, Cursor, Windsurf, etc.). Available tools (read-only): * List your funnels * Get a funnel's current metrics (counts + rates per stage) * Get a specific profile's journey timeline * Check tracking site verification status * Get Stripe integration health **Install:** see [MCP & Context7](https://docs.funnelfizz.com/ai-agents/mcp-and-context7.md) for the one-line install. ## Context7[​](#context7 "Direct link to Context7") FunnelFizz is indexed in [Context7](https://context7.com), so agents that support Context7 MCP can fetch our docs by saying *"use context7 funnelfizz"*. Our `context7.json` lives at the root of our docs repo. → [MCP & Context7 setup](https://docs.funnelfizz.com/ai-agents/mcp-and-context7.md) ## Principles[​](#principles "Direct link to Principles") We try to make these docs AI-agent-friendly not just by publishing machine-readable formats but by writing for both audiences at once: * **Use absolute URLs** in examples, not relative links that break on copy-paste. * **Quote exact enum values.** If we say the split condition is `traffic_source`, that's the literal string. Agents can trust it. * **Avoid ambiguous pronouns** ("it", "this") without a clear antecedent. * **Put the conclusion first.** TL;DRs at the top, explanations after. Agents scan before they read. * **Mark preview features explicitly.** If something's not shipped ("SEO dashboard is demo-only today"), we say so. Agents that commit to non-existent features cause real user pain. ## For humans thinking about this[​](#for-humans-thinking-about-this "Direct link to For humans thinking about this") If you're a solo founder wondering "wait, do I really need to think about AI agents reading my funnel setup docs?" — probably not directly. The reason this matters is that *your users* will increasingly delegate tool-setup to their agents. Making FunnelFizz easy to set up by agent is the same thing as making FunnelFizz easy to set up for busy humans. Agent-friendly = human- friendly. ## Related[​](#related "Direct link to Related") * [Hand it to your agent](https://docs.funnelfizz.com/ai-agents/hand-it-to-your-agent.md) * [llms.txt convention](https://docs.funnelfizz.com/ai-agents/llms-txt.md) * [MCP & Context7](https://docs.funnelfizz.com/ai-agents/mcp-and-context7.md) * [Tutorials → Agentic funnels](https://docs.funnelfizz.com/tutorials/agentic-funnels.md) --- Copy as markdown[View .md](https://docs.funnelfizz.com/concepts/automations "View the raw markdown for this page")[Open in Claude](https://claude.ai/new?q=Read%20https%3A%2F%2Fdocs.funnelfizz.com%2Fconcepts%2Fautomations.md%20and%20help%20me%20with%20this%20FunnelFizz%20topic%3A%20Automations "Open this page in Claude with context")[Open in ChatGPT](https://chat.openai.com/?q=Read%20https%3A%2F%2Fdocs.funnelfizz.com%2Fconcepts%2Fautomations.md%20and%20help%20me%20with%20this%20FunnelFizz%20topic%3A%20Automations "Open this page in ChatGPT with context") # Automations Automations are how you run a drip campaign, a welcome sequence, a churn recovery, a trial conversion flow — anything that looks like "when X happens, do Y, then wait, then maybe do Z." Under the hood, an automation is a **directed acyclic graph (DAG)** of steps. You drag step nodes onto a canvas and connect them with edges. When a profile enters the automation, they walk through it one step at a time. ## Anatomy[​](#anatomy "Direct link to Anatomy") Every automation has: * **A trigger** — what kicks it off. * **Steps** — the work to do. * **A state** — DRAFT (not running yet), ACTIVE (firing for new profiles), or PAUSED (stopped). ### Triggers[​](#triggers "Direct link to Triggers") * **`subscription_trial`** — fired when a Stripe subscription enters `status = "trialing"`. Most common for "new trial user" onboarding sequences. * **`subscription_paid`** — fired when a subscription moves to `status = "active"`. Most common for "welcome to the paid plan" sequences. * **`stage_enter`** — fires whenever a profile enters *any* stage. Legacy trigger; prefer the subscription-specific triggers when you can. * **`churn`** *(coming soon)* — fired when a subscription is canceled. ### Step types[​](#step-types "Direct link to Step types") | Step | What it does | Config | | ---------------------- | ------------------------------------ | ------------------------------------------------------------- | | **SEND\_EMAIL** | Render blocks, enqueue an email send | Email blocks, sender, subject | | **WAIT** | Pause for a fixed duration | Months / days / hours / minutes | | **WAIT\_UNTIL** | Pause until an event fires | Event name (e.g., `email_opened`), optional timeout | | **CONDITIONAL\_SPLIT** | Branch on a condition | Condition (e.g., `email_opened == true`), two output branches | | **PERCENTAGE\_SPLIT** | A/B split deterministically | Percent to left branch (e.g., 50 / 50) | ### Status[​](#status "Direct link to Status") Per-profile run state: * `RUNNING` — currently walking through the graph. * `WAITING_FOR_EVENT` — parked on a WAIT\_UNTIL step, waiting for the trigger. * `COMPLETED` — reached the end successfully. * `CANCELLED` — exited early (e.g., the profile unsubscribed, or you paused the automation). Per-automation state: * `DRAFT` — not running yet. * `ACTIVE` — new profiles enter as they match the trigger. * `PAUSED` — no new enrollments, existing runs keep executing. ## A typical automation[​](#a-typical-automation "Direct link to A typical automation") ### Trial welcome[​](#trial-welcome "Direct link to Trial welcome") ``` trigger: subscription_trial │ ▼ [SEND_EMAIL: "Welcome — here's the 60-second quickstart"] │ ▼ [WAIT: 2 days] │ ▼ [SEND_EMAIL: "3 things Pro users do first"] │ ▼ [WAIT: 3 days] │ ▼ [CONDITIONAL_SPLIT: did they upgrade yet?] │ │ ▼ yes ▼ no [SEND_EMAIL: "Glad you stuck around"] [SEND_EMAIL: "Trial ends tomorrow"] ``` ### Churn recovery[​](#churn-recovery "Direct link to Churn recovery") ``` trigger: churn (or: stage_enter CUSTOMER where bucket=Churned) │ ▼ [WAIT: 7 days] │ ▼ [SEND_EMAIL: "Can you tell us why?"] │ ▼ [WAIT_UNTIL: email_opened (timeout: 7 days)] │ │ ▼ opened ▼ timed out [SEND_EMAIL: 20% discount offer] [SEND_EMAIL: "Last chance — 30% off"] ``` ## How execution works[​](#how-execution-works "Direct link to How execution works") A cron runs every 5 minutes, picks up all RUNNING automation runs whose `nextStepAt` is in the past, and advances them one step: * **SEND\_EMAIL** — enqueues the email (actual send happens in the email cron). * **WAIT** — sets `nextStepAt = now + delay`, stays `RUNNING`. * **WAIT\_UNTIL** — sets status to `WAITING_FOR_EVENT`. When the event fires, the run flips back to `RUNNING` and continues. * **CONDITIONAL\_SPLIT** — evaluates the condition, follows the matching edge. * **PERCENTAGE\_SPLIT** — hashes (automationRunId + stepId) mod 100, compares to threshold, follows the matching edge. Deterministic — the same profile always takes the same branch. ## Unsubscribe and suppression[​](#unsubscribe-and-suppression "Direct link to Unsubscribe and suppression") If a profile unsubscribes at any point during an automation run, their current run is immediately marked `CANCELLED`. They also won't receive emails from any other campaign or automation in the workspace — unsubscribes are workspace-wide. ## Plan limits[​](#plan-limits "Direct link to Plan limits") | Plan | Automations per stage | Max step depth | Email/month | | --------- | --------------------- | -------------- | ----------- | | **FREE** | 0 | — | — | | **HOBBY** | 1 | 3 | 3,000 | | **PRO** | 5 | 5 | 20,000 | "Step depth" counts unique steps along the longest path — a 3-step sequence with one split branching off is depth 3, not 4. *** **Next:** [Conversion →](https://docs.funnelfizz.com/concepts/conversion.md) • [Full funnel tracking →](https://docs.funnelfizz.com/concepts/full-funnel-tracking.md) --- Copy as markdown[View .md](https://docs.funnelfizz.com/concepts/conversion "View the raw markdown for this page")[Open in Claude](https://claude.ai/new?q=Read%20https%3A%2F%2Fdocs.funnelfizz.com%2Fconcepts%2Fconversion.md%20and%20help%20me%20with%20this%20FunnelFizz%20topic%3A%20Conversion "Open this page in Claude with context")[Open in ChatGPT](https://chat.openai.com/?q=Read%20https%3A%2F%2Fdocs.funnelfizz.com%2Fconcepts%2Fconversion.md%20and%20help%20me%20with%20this%20FunnelFizz%20topic%3A%20Conversion "Open this page in ChatGPT with context") # Conversion A **conversion** is the transition from one stage to the next. Because there are four stages, there are exactly three conversions — the slices you care about most when you look at your funnel. ``` AWARENESS ● CONSIDERATION ● TRIAL ● CUSTOMER │ position 0 position 1 position 2 ``` Each conversion position has a **method** that defines what counts as a conversion. The defaults cover 90% of cases; you can override any of them. ## The three positions[​](#the-three-positions "Direct link to The three positions") ### Position 0: AWARENESS → CONSIDERATION[​](#position-0-awareness--consideration "Direct link to Position 0: AWARENESS → CONSIDERATION") **Default method: Site visit** (via tracking code). This is the easiest one: anyone who lands on your site with the tracking script installed converts. There's no work to do beyond [installing tracking](https://docs.funnelfizz.com/quickstart/install-tracking.md). Alternative methods: * **Email signup form** — a form post with an email field on your marketing site (bypasses the site visit requirement, useful if you have a form-first landing page). ### Position 1: CONSIDERATION → TRIAL[​](#position-1-consideration--trial "Direct link to Position 1: CONSIDERATION → TRIAL") **Default method: Stripe trial** (`status = "trialing"` on a subscription). Works out of the box once you've [connected Stripe](https://docs.funnelfizz.com/quickstart/connect-stripe.md) and mapped your product. Alternative methods: * **Custom signup event** (HOBBY+) — call `funnelfizz('event', 'signup')` from your app or site. Useful if your product has self-serve signup without Stripe (free plan signups, "book a demo" flows, email gated downloads). * **Email signup form** — form post with an email field on a deeper page (not your landing). ### Position 2: TRIAL → CUSTOMER[​](#position-2-trial--customer "Direct link to Position 2: TRIAL → CUSTOMER") **Default method: Stripe paid** (`status = "active"`). Works automatically after Stripe connect. Alternative methods: * **App Store / Play Store download** (HOBBY+) — uses a referral parameter on the store link to attribute mobile downloads. Good if your conversion event is "installed the mobile app" instead of "started a paid subscription." ## Configuring a conversion[​](#configuring-a-conversion "Direct link to Configuring a conversion") On the canvas, click the round conversion bubble between any two stages. A drawer opens with: * **Label** — how it shows up in the dashboard ("Signup", "Started trial", "Paid"). * **Method** — pick from the available options for that position. * **Configuration** — method-specific fields (Stripe product ID, form ID, event name). ### Method reference[​](#method-reference "Direct link to Method reference") Site visit (tracking code) **Fields:** tracking site ID (auto-filled if you have one), domain. **What it counts:** any `pageview` event from a unique visitor. No extra config needed. Email signup form **Fields:** form ID (any identifier you want), email field name (the `name` attribute of the email input — defaults to `email`). **What it counts:** a `form_submit` event where the form contains an email-shaped value in the named field. The tracking script auto-fires `form_submit` on every form on your site; you pick which form counts by ID. Stripe trial **Fields:** Stripe integration (auto-filled), product ID, price ID. **What it counts:** subscription with `status = "trialing"` on the specified product/price. **Config tip:** if you have multiple trial tiers, point each conversion at the specific price ID you care about. If you just want "any trial counts," leave price ID blank. Stripe paid **Fields:** Stripe integration, source product IDs (what they came from — optional), target product IDs (what counts as paid). **What it counts:** subscription with `status = "active"` on one of the target products, at any price. **Config tip:** the "source" filter is for funnel chaining — "only count people who upgraded from the Hobby tier." Leave it empty to count anyone who becomes a paying customer regardless of prior history. Custom signup event **Fields:** event name, optional expected properties. **What it counts:** a matching call to `funnelfizz('event', '', {...})` from a unique visitor. Properties let you filter further — e.g., only count events where `plan: 'pro'`. Example: ``` // Fires from your app's signup success handler funnelfizz('event', 'signup', { plan: 'pro', source: 'pricing_page' }); ``` Then configure the conversion with event name `signup` and (optionally) expected property `plan: pro`. App Store / Play Store download **Fields:** store (`app_store`, `play_store`, or `both`), referral parameter name. **What it counts:** a redirect from your site to a store link with the tracked referral param present. We record the click-out; the store attribution happens on your side via the referral parameter decoding in your onboarding analytics (Branch, AppsFlyer, Adjust, or native store referrer APIs). ## Conversion rates[​](#conversion-rates "Direct link to Conversion rates") For each position, FunnelFizz computes: * **Count** — how many unique profiles made the transition in the period. * **Rate** — count divided by the source-stage count, expressed as a percentage. * **Trend** — period-over-period delta (up/down arrow, % change). Rates are computed *after* split filters are applied, so each split branch has its own conversion rates separate from the main funnel. ## Under the hood[​](#under-the-hood "Direct link to Under the hood") Every successful conversion writes a **journey event** with `eventType = "converted"` and metadata about the method. Journey events have unique constraints per (funnel, visitor, stage, eventType) so a visitor can't be double-counted. This means: if the same user comes back next month and converts again, they don't get double-counted in that funnel. But they *do* get counted in a different funnel if you run multiple funnels for the same workspace. *** **Next:** [Full funnel tracking →](https://docs.funnelfizz.com/concepts/full-funnel-tracking.md) — how anonymous visitors get stitched together into identified customer journeys. --- Copy as markdown[View .md](https://docs.funnelfizz.com/concepts/email "View the raw markdown for this page")[Open in Claude](https://claude.ai/new?q=Read%20https%3A%2F%2Fdocs.funnelfizz.com%2Fconcepts%2Femail.md%20and%20help%20me%20with%20this%20FunnelFizz%20topic%3A%20Email "Open this page in Claude with context")[Open in ChatGPT](https://chat.openai.com/?q=Read%20https%3A%2F%2Fdocs.funnelfizz.com%2Fconcepts%2Femail.md%20and%20help%20me%20with%20this%20FunnelFizz%20topic%3A%20Email "Open this page in ChatGPT with context") # Email FunnelFizz has its own email engine. You don't need Mailchimp, HubSpot, or ConvertKit — though you can migrate away from them if you want ([see tutorial](https://docs.funnelfizz.com/tutorials/migrating-from-brevo-hubspot.md)). This page is an overview. The full feature reference is at [Features → Email](https://docs.funnelfizz.com/features/email.md). ## What makes FunnelFizz email different[​](#what-makes-funnelfizz-email-different "Direct link to What makes FunnelFizz email different") Two things: 1. **Recipient lists are dynamic.** You don't manage a static "Newsletter Subscribers" list. Instead, a campaign targets "everyone currently in the TRIAL stage of my funnel" — the list re-evaluates at send time. If someone moved from TRIAL to CUSTOMER between the moment you drafted the campaign and the moment it sends, they'll fall out of the audience automatically. 2. **Stages are the primary targeting axis.** You're emailing *people at a specific point in their journey with you* — not a generic blast list. That changes what you write. A TRIAL- stage user gets "here's how to get the most out of your trial." A CUSTOMER-stage user who moved into the Churned bucket gets "we miss you — here's 20% off to come back." ## What you can send[​](#what-you-can-send "Direct link to What you can send") * **Broadcast campaigns** — one-time send to everyone in a given stage (or split branch). * **A/B tested campaigns** (PRO) — split the audience, pick a winner based on open or click rate, auto-fan-out the winner to the rest. * **Scheduled campaigns** — set a send time, FunnelFizz queues it up. * **Automation drips** — a DAG of SendEmail / Wait / Split steps that fires on stage entry or Stripe subscription events. See [Automations →](https://docs.funnelfizz.com/concepts/automations.md). * **Churn recovery** — a specific pattern: target the *Churned* slice of CUSTOMER and send a "come back" offer. This is just an automation with a condition, but it's common enough we call it out. ## How it's built[​](#how-its-built "Direct link to How it's built") * **Editor:** drag-and-drop MJML blocks (header, text, image, button, divider, columns, video, logo, social, HTML passthrough). MJML compiles to email-client-safe HTML with inline CSS. * **Sender:** Resend under the hood, with fallback to SMTP for transactional mail. * **Tracking:** open pixel + click URL rewriting, both automatic. Opens/clicks flow back into your funnel as events. * **Domain:** you can send from `funnelfizz.com` on the HOBBY plan, or verify your own custom domain on PRO (DKIM + SPF + DMARC, we walk you through the DNS). ## Emailing people at specific points in the funnel[​](#emailing-people-at-specific-points-in-the-funnel "Direct link to Emailing people at specific points in the funnel") The most common pattern: ``` Someone enters TRIAL stage │ ├─ Day 0: "Welcome — here's the quickstart" ├─ Day 2: "3 things Pro users do first" ├─ Day 5: "Trial ends in 2 days — here's what you'd lose" └─ Day 7: "Trial ended — unlock for $X" ``` You build this as an **Automation** (DAG of Send/Wait/Send/Wait) with trigger = "subscription\_trial." FunnelFizz evaluates it per-profile — if someone upgrades during the trial, they're pulled out of the sequence automatically. ## Emailing churned users[​](#emailing-churned-users "Direct link to Emailing churned users") Also common: ``` Someone's Stripe subscription is canceled │ ├─ 7 days later: "Sorry to see you go — can you tell us why?" └─ 30 days later: "We fixed [thing]. Here's 20% off to come back." ``` Built as an automation with trigger = "churn" (Stripe `customer.subscription.deleted` webhook). The condition filters to "no active subscription anywhere" (the Churned bucket of the trichotomy) so you don't accidentally email someone who just upgraded. ## Quotas & limits[​](#quotas--limits "Direct link to Quotas & limits") | Plan | Emails/month | Custom domain | A/B testing | Max automations/stage | | --------- | ------------ | ----------------------------- | ----------- | ------------------------- | | **FREE** | — | No | No | — | | **HOBBY** | 3,000 | No (send from funnelfizz.com) | No | 1 per stage, 3-step depth | | **PRO** | 20,000 | Yes (verified) | Yes | 5 per stage, 5-step depth | Hard bounces and unsubscribes are automatically suppressed — once someone unsubscribes, they won't receive *any* subsequent emails from your workspace (not just that campaign). ## Deliverability[​](#deliverability "Direct link to Deliverability") Custom domain setup is the biggest lever. If you're on HOBBY sending from `funnelfizz.com`, your deliverability is fine for small volume but gets throttled by some corporate mail servers. On PRO, verifying your own domain with DKIM + SPF + DMARC lands you in inboxes at roughly industry-standard rates. See [Features → Email → Custom domain](https://docs.funnelfizz.com/features/email.md#custom-sending-domain) for DNS setup. *** **Next:** [Automations →](https://docs.funnelfizz.com/concepts/automations.md) — how the DAG-based drip engine actually works. --- Copy as markdown[View .md](https://docs.funnelfizz.com/concepts/full-funnel-tracking "View the raw markdown for this page")[Open in Claude](https://claude.ai/new?q=Read%20https%3A%2F%2Fdocs.funnelfizz.com%2Fconcepts%2Ffull-funnel-tracking.md%20and%20help%20me%20with%20this%20FunnelFizz%20topic%3A%20Full%20funnel%20tracking "Open this page in Claude with context")[Open in ChatGPT](https://chat.openai.com/?q=Read%20https%3A%2F%2Fdocs.funnelfizz.com%2Fconcepts%2Ffull-funnel-tracking.md%20and%20help%20me%20with%20this%20FunnelFizz%20topic%3A%20Full%20funnel%20tracking "Open this page in ChatGPT with context") # Full funnel tracking The feature that makes everything else work: **following one person from the first time they saw your brand to the day they churned**, across devices, sessions, browsers, and payment events. This page explains the identity model — the mechanism by which an anonymous pageview becomes a named customer. Understanding it helps when you're debugging "why isn't this user showing up in my funnel?" or deciding when to call `identify`. ## The three identities[​](#the-three-identities "Direct link to The three identities") FunnelFizz stitches three kinds of identifiers together: 1. **Visitor ID** (`_fn_vid` cookie) — assigned on first pageview. Permanent per browser. UUID. 2. **User ID** — your own internal ID for the user (however your app names them). Assigned when you call `funnelfizz('identify', { userId })`. 3. **Stripe customer ID** — `cus_...`. Assigned by Stripe when checkout happens. The goal is to link these three together so that a session that started anonymous on mobile can be tied to the desktop purchase three weeks later. ## The stitch[​](#the-stitch "Direct link to The stitch") ``` Day 1: anonymous visitor lands on your site. → _fn_vid cookie = "vid_abc123" (new UUID) → _fn_sid session = "sess_def456" → pageview event recorded, attributed to vid_abc123 Day 2: same visitor, signs up for a trial. → app JS calls: funnelfizz('identify', { userId: 'user_789' }) → VisitorIdentity row written: vid_abc123 → user_789 → all prior events re-attributed to user_789 Day 3: user checks out on Stripe. → Stripe fires checkout.session.completed webhook → FunnelFizz matches customer.email to user_789 → writes journey event: stage=CUSTOMER, visitor=vid_abc123, user=user_789 → stripe_customer_id = "cus_xyz" stored on the profile Day 30: same user, different device (new browser, no cookie). → anonymous pageview → _fn_vid cookie = "vid_new999" → pageview recorded attributed to vid_new999 → NO stitch yet — FunnelFizz doesn't know this is user_789 Day 30 later: user logs in on the new device. → app JS calls: funnelfizz('identify', { userId: 'user_789' }) → VisitorIdentity row written: vid_new999 → user_789 → BOTH vid_abc123 and vid_new999 now point to user_789 → all their events, on both devices, roll up to the same profile ``` ## Keys and cookies[​](#keys-and-cookies "Direct link to Keys and cookies") | Name | Storage | Lifetime | Purpose | | --------- | --------------------- | ------------ | --------------------------------- | | `_fn_vid` | Cookie + localStorage | 365 days | Permanent visitor ID | | `_fn_sid` | sessionStorage | Tab lifetime | Session ID for grouping pageviews | Neither cookie is shared across domains. If your product is split across `marketing.example.com` and `app.example.com`, enable cross-subdomain cookies by setting `cookieDomain: '.example.com'` when you install tracking (see [Features → Providers](https://docs.funnelfizz.com/features/providers.md) for multi-domain setups). ## The `identify` call[​](#the-identify-call "Direct link to the-identify-call") The single most important call in your codebase: ``` funnelfizz('identify', { userId: 'user_789', // your internal user ID — required email: 'sam@example.com', // optional but strongly recommended createdAt: '2026-04-22T...', // optional — helps with cohort analysis plan: 'pro', // optional custom trait }); ``` **Where to call it:** * Right after signup (immediately — even before the first trial starts). * Every page load after login (idempotent, safe to call repeatedly). * After any auth state change (on every re-auth, OAuth callback, etc.). **What *not* to pass:** * Passwords, tokens, credit card info — anything sensitive. * PII you haven't gotten consent to process (in GDPR jurisdictions). ## Cross-device identity[​](#cross-device-identity "Direct link to Cross-device identity") When the same `userId` is seen on two different `_fn_vid` cookies, FunnelFizz merges them — all events roll up to the same user profile, regardless of which device fired them. This is automatic; no extra config. The reverse isn't true: we don't guess that two anonymous visitors on different devices are the same person. If you never call `identify`, anonymous sessions stay separate. This is intentional — cross-device guessing without identity signals is how creepy tracking happens. ## Email handoff (from email campaigns)[​](#email-handoff-from-email-campaigns "Direct link to Email handoff (from email campaigns)") When FunnelFizz sends an email on your behalf, it embeds a signed profile token into every tracked link: ``` https://your-site.com/pricing?fn_ph= ``` When the recipient clicks, the tracking script reads the token on first event, verifies the signature server-side, and stitches the recipient's profile to the landing visitor cookie — so you can track "this trial user clicked our day-5 email and visited pricing," even if they opened the email on a totally new device. The token is stripped from the URL via `history.replaceState` immediately so it doesn't leak to downstream analytics or referrer headers. ## Journey events[​](#journey-events "Direct link to Journey events") Every stage entry and conversion writes a **journey event**: ``` { "funnelId": "funnel_abc", "visitorId": "vid_123", "userId": "user_789", "stageType": "TRIAL", "eventType": "entered", "fromStage": "CONSIDERATION", "metadata": { "method": "stripe_trial", "productId": "prod_xyz" }, "createdAt": "2026-04-22T12:34:56Z" } ``` Unique constraints per (funnel, visitor, stage, eventType) guarantee that a visitor can't enter the same stage twice in the same funnel. Churn events are the exception — a customer can churn only once per funnel (unique on `funnel × visitor × stageType = CUSTOMER × eventType = "churned"`). ## Full journey view[​](#full-journey-view "Direct link to Full journey view") On any profile's detail page, you'll see a chronological timeline: ``` 2026-04-01 12:04 pageview / Reddit 2026-04-01 12:05 pageview /pricing 2026-04-02 09:22 identify user_789 sam@example.com 2026-04-02 09:23 signup (custom event) 2026-04-05 14:10 stripe_checkout cus_xyz → prod_pro $9/mo trial 2026-04-12 22:30 subscription_active paid 2026-05-12 22:30 subscription_canceled churned ``` This is the raw source of truth. Dashboard metrics are aggregations; the journey is the trace. ## Privacy[​](#privacy "Direct link to Privacy") * No third-party tracking. FunnelFizz doesn't sell data to ad networks. * `navigator.doNotTrack` is respected — if the visitor has DNT set, no events are sent. * GDPR / CCPA: you're responsible for consent. If you're using a consent management platform (CMP), defer loading the FunnelFizz snippet until the user grants "analytics" consent. * All tokens in email links are short-lived and single-scoped. * Profile tokens in the URL are HMAC-signed and verified server-side; an attacker can't guess another user's token. See [funnelfizz.com/privacy](https://funnelfizz.com/privacy) for the full policy. *** **Next:** browse the [Features](https://docs.funnelfizz.com/features/providers.md) section for the full details on each feature. --- Copy as markdown[View .md](https://docs.funnelfizz.com/concepts/splitting-and-extensions "View the raw markdown for this page")[Open in Claude](https://claude.ai/new?q=Read%20https%3A%2F%2Fdocs.funnelfizz.com%2Fconcepts%2Fsplitting-and-extensions.md%20and%20help%20me%20with%20this%20FunnelFizz%20topic%3A%20Splitting%20%26%20extensions "Open this page in Claude with context")[Open in ChatGPT](https://chat.openai.com/?q=Read%20https%3A%2F%2Fdocs.funnelfizz.com%2Fconcepts%2Fsplitting-and-extensions.md%20and%20help%20me%20with%20this%20FunnelFizz%20topic%3A%20Splitting%20%26%20extensions "Open this page in ChatGPT with context") # Splitting & extensions A single funnel shows you the *overall* picture: total traffic in, total customers out. That's usually not enough. What you really want to know is *"which slice of traffic is actually converting?"* — which is what **splits** answer. After the main funnel, you often want to track what happens *next* — do customers upgrade? That's what **extensions** answer. ## Splits[​](#splits "Direct link to Splits") A split inserts a filter node between two stages. Everything downstream of the split only shows traffic matching that filter. ### Eight split conditions[​](#eight-split-conditions "Direct link to Eight split conditions") | Condition | Filters by | Example | | ------------------ | ------------------------------------------------------------------- | ---------------------------------- | | **Traffic source** | Referrer domain | "only traffic from reddit.com" | | **UTM parameter** | utm\_source, utm\_medium, utm\_campaign, utm\_content, utm\_term | "only utm\_campaign=summer-launch" | | **Channel** | Auto-categorized channel | "only organic search" | | **Device type** | Desktop, mobile, tablet | "only mobile" | | **Country** | ISO country code | "only US and Canada" | | **Custom event** | A goal you define with `data-fn-goal` or `funnelfizz('event', ...)` | "only users who clicked pricing" | | **A/B variant** | The `?ab=` URL parameter | "only ?ab=frog" | | **Stripe product** | Specific Stripe product ID (TRIAL/CUSTOMER only) | "only Pro tier subscribers" | All conditions combine with **AND** — a visitor must match every condition in the chain to appear in that split branch. ### Auto-categorized channels[​](#auto-categorized-channels "Direct link to Auto-categorized channels") The **Channel** condition uses a server-side categorizer that bucketizes your traffic from referrer + UTM combinations. Buckets: * `direct` — no referrer, no UTM * `organic_search` — Google, Bing, DuckDuckGo, etc. * `social` — X, LinkedIn, Reddit, TikTok, Instagram, YouTube, Facebook * `paid` — `utm_medium=cpc/paid/ads` or recognized ad networks * `email` — `utm_medium=email` or email-client referrers * `ai_llm` — chatgpt.com, claude.ai, perplexity.ai, gemini.google.com, copilot.microsoft.com * `referral` — everything else with a referrer ### Visualizing a split[​](#visualizing-a-split "Direct link to Visualizing a split") On the canvas, splits look like this: ``` ┌─ [split: mobile] ─▶ CONSIDERATION (mobile) ─▶ TRIAL ─▶ CUSTOMER │ AWARENESS ─▶ CONSIDERATION │ └─ [split: desktop] ─▶ CONSIDERATION (desktop) ─▶ TRIAL ─▶ CUSTOMER ``` Each branch is independent — you can chain further splits down a branch, connect different providers, even send different emails to each cohort. ### Nested splits[​](#nested-splits "Direct link to Nested splits") You can split a split. "Mobile visitors from the US who came via YouTube" is a three-level nested split that will compute correctly. The canvas auto-lays-out nested branches so they don't overlap. There's no fixed depth limit, but in practice three levels is about as deep as is readable. ### Adding a split in the UI[​](#adding-a-split-in-the-ui "Direct link to Adding a split in the UI") 1. Click the **+** button between any two stages on the canvas. 2. Pick a **split condition**. 3. Fill in the **value** (e.g., country = "US", UTM source = "reddit"). 4. Name the branch (e.g., "US mobile traffic"). 5. Save. That's it. The branch fills in automatically using the same data sources as the parent funnel. → Full details and per-condition configuration: [Features → Splitting](https://docs.funnelfizz.com/features/splitting.md). ## Extensions[​](#extensions "Direct link to Extensions") **Extensions** are an optional second-stage pipeline that sits *after* CUSTOMER, tracking what happens to paying customers — specifically, do they upgrade? The analogy: your main funnel is "get from stranger to paying customer." Your extension is "get from paying customer to *higher-paying* customer." ### Extension stages[​](#extension-stages "Direct link to Extension stages") Extensions mirror the main funnel's structure (minus Awareness, because by definition these are people you already have): * **Extension Consideration** — your paying customers who are *active* on your product in the reporting window (fired a pageview or an event). * **Extension Trial** — Extension-Consideration users who started a trial at a *higher-priced* tier. * **Extension Customer** — users paying at a higher tier than their anchor product. ### The anchor product[​](#the-anchor-product "Direct link to The anchor product") When you enable extensions for a funnel, you pick an **anchor product** — typically your entry-level paid plan (e.g., "Hobby — $9/mo"). The extension then tracks the cohort of customers who bought the anchor, and measures how many of them end up paying for a higher tier. If a customer: * Still has the anchor subscription → counted in **Retained**. * Has no anchor but a higher-tier sub → counted in **Upgraded** (and shows up in Extension). * Has nothing → counted in **Churned**. ### Canvas layout[​](#canvas-layout "Direct link to Canvas layout") On the canvas, extensions render to the right of the CUSTOMER stage with a double-line edge (visually distinct from main-funnel edges). They inherit the tint color of the main funnel. ``` ... ─▶ CUSTOMER ═══▶ Ext CONSIDERATION ═══▶ Ext TRIAL ═══▶ Ext CUSTOMER ``` ### When to use extensions[​](#when-to-use-extensions "Direct link to When to use extensions") * **Freemium products** — you want to know what % of free users convert to paid. Set the free tier as the anchor. * **SaaS with multiple tiers** — how often do Hobby customers upgrade to Pro? Set Hobby as the anchor. * **Lifecycle marketing** — use extension stages as triggers for upgrade automations. ### When NOT to use extensions[​](#when-not-to-use-extensions "Direct link to When NOT to use extensions") * Single-tier products (Stripe has one price → nothing to upgrade to). * Products where upsell isn't the growth lever (e.g., one-time purchases, usage-based billing where everyone's on the same plan and just pays more). *** **Next:** [Email →](https://docs.funnelfizz.com/concepts/email.md) • [Automations →](https://docs.funnelfizz.com/concepts/automations.md) • [Conversion →](https://docs.funnelfizz.com/concepts/conversion.md) • [Full funnel tracking →](https://docs.funnelfizz.com/concepts/full-funnel-tracking.md) --- Copy as markdown[View .md](https://docs.funnelfizz.com/concepts/stages "View the raw markdown for this page")[Open in Claude](https://claude.ai/new?q=Read%20https%3A%2F%2Fdocs.funnelfizz.com%2Fconcepts%2Fstages.md%20and%20help%20me%20with%20this%20FunnelFizz%20topic%3A%20The%20four%20stages "Open this page in Claude with context")[Open in ChatGPT](https://chat.openai.com/?q=Read%20https%3A%2F%2Fdocs.funnelfizz.com%2Fconcepts%2Fstages.md%20and%20help%20me%20with%20this%20FunnelFizz%20topic%3A%20The%20four%20stages "Open this page in ChatGPT with context") # The four stages FunnelFizz has exactly four stages, in a fixed order: ``` AWARENESS → CONSIDERATION → TRIAL → CUSTOMER ``` You can't rename, reorder, or add to them — the model is opinionated by design. Between each stage sits a **conversion**: a measurable transition from one state to the next. That gives you three conversions (positions 0, 1, 2), which map to the numbers you care about most: *how much of my traffic turns into paying customers?* :::info Why four, not seven or twelve? We spent a long time looking at how solo founders and small SaaS teams actually think about their funnel. Most over-segment (twelve imaginary stages that never get measured), and some under-segment (a single "conversion rate" that hides everything). Four works: it's enough to see where things break, small enough to remember. ::: Every funnel you create in FunnelFizz has these four stages by default. You can: * **Connect data sources** to each stage (social providers to AWARENESS, Stripe to TRIAL and CUSTOMER, etc.). * **Split traffic** at any stage ([see splitting](https://docs.funnelfizz.com/concepts/splitting-and-extensions.md)). * **Add extensions** after CUSTOMER to track upsells ([see extensions](https://docs.funnelfizz.com/concepts/splitting-and-extensions.md#extensions)). * **Send emails and run automations** scoped to any stage ([see email](https://docs.funnelfizz.com/concepts/email.md)). ## AWARENESS — "Eyeballs"[​](#awareness--eyeballs "Direct link to AWARENESS — \"Eyeballs\"") **What it measures:** how many people saw you exist. Data sources that feed AWARENESS: * **Social impressions** from connected providers (X, YouTube, LinkedIn, Instagram, TikTok, Reddit). * **Brand mentions** from [Mentions monitoring](https://docs.funnelfizz.com/features/mentions.md) (PRO). * **Google Search Console impressions** (planned — currently demo-only). * **Paid ads impressions** from Google Ads. * **Manual entries** for anything you can't connect. AWARENESS isn't about who clicked — it's about eyeballs, reach. Someone scrolling past your post on the timeline counts. This is the widest part of the funnel. **Typical sources of truth:** * X post impressions from your `@handle` over the last 7 / 30 / 90 days. * YouTube video views. * LinkedIn post impressions. * Any mention of your brand name on HackerNews, Reddit, ProductHunt, DEV.to. ## CONSIDERATION — "Visitors"[​](#consideration--visitors "Direct link to CONSIDERATION — \"Visitors\"") **What it measures:** how many unique people actually reached your site. Data source: your **tracking script**. Every unique visitor (identified by a `_fn_vid` cookie) shows up here at least once per measurement window. CONSIDERATION is where your *marketing site* lives. This is the stage where: * You split by traffic source — did your reddit post or your YouTube video drive more visits? * You watch bounce rate — are people landing and leaving immediately? * You test hero copy with A/B variants. * You notice the painful truth that 95% of your impressions never clicked. The conversion from AWARENESS → CONSIDERATION is, essentially, your click-through rate from the wild web into your actual website. ## TRIAL — "Active users"[​](#trial--active-users "Direct link to TRIAL — \"Active users\"") **What it measures:** how many visitors gave you their email, signed up, started a trial, or did whatever you define as "showed real intent." Default conversion method: **Stripe trial subscription** (`status = "trialing"`). You can also configure it as: * **Email signup form** — any form that posts an email. * **Custom event** — call `funnelfizz('event', 'signup')` from your app. * **App store download** (HOBBY+) — with a referral parameter. TRIAL is usually the most under-measured stage — founders tend to only track Stripe trials, but a lot of SaaS products have self-serve signup without billing-side tracking. Use [custom events](https://docs.funnelfizz.com/quickstart/install-tracking.md#4-custom-events-optional-but-powerful) to surface the real number. ## CUSTOMER — "Paying customers"[​](#customer--paying-customers "Direct link to CUSTOMER — \"Paying customers\"") **What it measures:** how many of your trials turned into paying customers. Default conversion method: **Stripe paid subscription** (`status = "active"` or `"past_due"` within grace). Unlike the other stages, CUSTOMER doesn't just show a flat count. It shows a **trichotomy**: * **Retained** — still actively paying at your anchor product tier. * **Upgraded** — moved to a higher tier (paying *more* than before). * **Churned** — no active paid subscription anywhere in the workspace. That distinction matters: a customer who upgrades isn't lost — they're more valuable. The classic "canceled = churned" model can't tell the two apart. FunnelFizz can. ## Conversion positions between stages[​](#conversion-positions-between-stages "Direct link to Conversion positions between stages") Three conversions sit between the four stages: * **Position 0: AWARENESS → CONSIDERATION** — default method: site visit (tracking code). * **Position 1: CONSIDERATION → TRIAL** — default method: Stripe trial. * **Position 2: TRIAL → CUSTOMER** — default method: Stripe paid. Each position can be configured independently per funnel. Mix and match — e.g., if you don't have Stripe trials (you use paid-only), map position 1 to a custom `signup` event and position 2 to Stripe paid. See [Concepts → Conversion](https://docs.funnelfizz.com/concepts/conversion.md) for the full configuration reference. ## What you see on the canvas[​](#what-you-see-on-the-canvas "Direct link to What you see on the canvas") On the funnel canvas, stages appear as rectangular cards left to right in this order. Between them sit circular conversion bubbles showing the rate + raw count. The default layout: ``` ┌──────────┐ ● ┌───────────────┐ ● ┌───────┐ ● ┌──────────┐ │AWARENESS │──%──▶│ CONSIDERATION │──%──▶│ TRIAL │──%──▶│ CUSTOMER │ └──────────┘ └───────────────┘ └───────┘ └──────────┘ 128k imps 11,245 visitors 892 signups 213 paying 8.8% CTR 7.9% conv 23.9% conv ``` Split nodes (diamond shapes) appear *between* stages when you filter traffic. Extension stages appear *after* CUSTOMER for tracking upsells. ## Where to next[​](#where-to-next "Direct link to Where to next") * **[Splitting & extensions](https://docs.funnelfizz.com/concepts/splitting-and-extensions.md)** — how to filter and branch your funnel. * **[Conversion](https://docs.funnelfizz.com/concepts/conversion.md)** — configure the three conversion points. * **[Full funnel tracking](https://docs.funnelfizz.com/concepts/full-funnel-tracking.md)** — how an anonymous visitor becomes identified and tied to a paying customer across sessions. --- Copy as markdown[View .md](https://docs.funnelfizz.com/faq "View the raw markdown for this page")[Open in Claude](https://claude.ai/new?q=Read%20https%3A%2F%2Fdocs.funnelfizz.com%2Ffaq.md%20and%20help%20me%20with%20this%20FunnelFizz%20topic%3A%20FAQ "Open this page in Claude with context")[Open in ChatGPT](https://chat.openai.com/?q=Read%20https%3A%2F%2Fdocs.funnelfizz.com%2Ffaq.md%20and%20help%20me%20with%20this%20FunnelFizz%20topic%3A%20FAQ "Open this page in ChatGPT with context") # FAQ ## Plans & pricing[​](#plans--pricing "Direct link to Plans & pricing") ### Which plan do I need?[​](#which-plan-do-i-need "Direct link to Which plan do I need?") * **FREE** — You have a single funnel you want to visualize. Tracking works. No email. * **HOBBY** — You want to send emails and run one automation per stage. Custom domain not required. * **PRO** — You want custom sending domain, A/B testing, mentions monitoring, more automations. Full comparison: [funnelfizz.com/pricing](https://funnelfizz.com/pricing). ### Is there a TEAM plan?[​](#is-there-a-team-plan "Direct link to Is there a TEAM plan?") No. FREE, HOBBY, PRO. That's the whole menu. ### Can I downgrade?[​](#can-i-downgrade "Direct link to Can I downgrade?") Yes. Downgrading takes effect at the end of your current billing period. Features you lose (e.g., if you drop from PRO to HOBBY, you lose custom domain + A/B testing + mentions) go dark but data isn't deleted — upgrade again anytime and it reappears. ### Can I get a refund?[​](#can-i-get-a-refund "Direct link to Can I get a refund?") Within 14 days of paying, yes. Email ****. *** ## Tracking[​](#tracking "Direct link to Tracking") ### Does FunnelFizz work with cookie-consent (GDPR)?[​](#does-funnelfizz-work-with-cookie-consent-gdpr "Direct link to Does FunnelFizz work with cookie-consent (GDPR)?") Yes. Respect your CMP (cookie management platform). Load the tracking snippet only after the user accepts analytics cookies. FunnelFizz also respects `navigator.doNotTrack` by default (no events sent if DNT is set). ### How long is data retained?[​](#how-long-is-data-retained "Direct link to How long is data retained?") * **Tracking events**: 90 days on FREE, 12 months on HOBBY, 24 months on PRO. * **Profiles (stitched user identities)**: as long as your account exists. * **Journey events, conversions**: 24 months minimum, longer if subscription is active. * **Email sends**: 90 days for individual send records, aggregated stats indefinitely. Per-plan specifics may update; current policy at [funnelfizz.com/privacy](https://funnelfizz.com/privacy). ### What exactly does the tracking script send?[​](#what-exactly-does-the-tracking-script-send "Direct link to What exactly does the tracking script send?") Pageviews, scroll milestones (25/50/75/90%), time-on-page, form submissions, outbound clicks, any custom events you fire with `funnelfizz('event', ...)`, and the `identify` calls. No keystroke logging, no session replay, no third-party ad pixels. ### Can I self-host tracking?[​](#can-i-self-host-tracking "Direct link to Can I self-host tracking?") Not today. Tracking ingestion is a managed service. Self-hosting is on the long-term roadmap for enterprise customers. ### My tracking snippet isn't verifying. What's wrong?[​](#my-tracking-snippet-isnt-verifying-whats-wrong "Direct link to My tracking snippet isn't verifying. What's wrong?") See [Quickstart → Install tracking → Verify](https://docs.funnelfizz.com/quickstart/install-tracking.md#3-verify) for the checklist. The three most common causes: 1. Meta verify tag is outside `` (e.g., inside `` by mistake). 2. The domain in FunnelFizz doesn't match where you're visiting from (subdomain mismatch). 3. An ad-blocker or privacy extension in your test browser is blocking the script. *** ## Stripe[​](#stripe "Direct link to Stripe") ### Do I need Stripe?[​](#do-i-need-stripe "Direct link to Do I need Stripe?") Strictly speaking, no — you can run FunnelFizz on just tracking. But TRIAL and CUSTOMER counts will be empty without Stripe, which limits you to CONSIDERATION-level analytics. ### What Stripe scopes do I need?[​](#what-stripe-scopes-do-i-need "Direct link to What Stripe scopes do I need?") Seven read scopes: Customers, Charges and Refunds, Events, Products and Prices, Invoices, Subscriptions, Balance. See [Quickstart → Connect Stripe](https://docs.funnelfizz.com/quickstart/connect-stripe.md). ### Can I use a test-mode key?[​](#can-i-use-a-test-mode-key "Direct link to Can I use a test-mode key?") Yes. Same seven scopes, but use `rk_test_...`. Test-mode events flow normally so you can dry- run your setup without real charges. ### Can the same Stripe account be on two workspaces?[​](#can-the-same-stripe-account-be-on-two-workspaces "Direct link to Can the same Stripe account be on two workspaces?") No. One Stripe account per workspace to prevent double-attribution. Disconnect from the first workspace if you want to move it. *** ## Email[​](#email "Direct link to Email") ### Why is my email going to spam?[​](#why-is-my-email-going-to-spam "Direct link to Why is my email going to spam?") Most likely cause: you're on HOBBY sending from `funnelfizz.com` and the recipient's corporate mail filter throttles shared sender domains. Fix: upgrade to PRO and verify your own sending domain with DKIM + SPF + DMARC. ### Can I import my HubSpot / Mailchimp list?[​](#can-i-import-my-hubspot--mailchimp-list "Direct link to Can I import my HubSpot / Mailchimp list?") Yes. See [Tutorials → Migrating from Brevo / HubSpot](https://docs.funnelfizz.com/tutorials/migrating-from-brevo-hubspot.md). ### Do unsubscribes apply across funnels?[​](#do-unsubscribes-apply-across-funnels "Direct link to Do unsubscribes apply across funnels?") Yes. Unsubscribes are **workspace-wide** — once someone unsubscribes, they don't receive anything from any funnel or campaign in your workspace. This is intentional (and legally required in most jurisdictions). ### Can I send transactional emails from FunnelFizz?[​](#can-i-send-transactional-emails-from-funnelfizz "Direct link to Can I send transactional emails from FunnelFizz?") Not recommended. FunnelFizz is for marketing email — drips, campaigns, automations. For transactional emails (password resets, receipts, 2FA codes), use Resend, Postmark, SES, or your auth provider directly. *** ## Privacy & data[​](#privacy--data "Direct link to Privacy & data") ### Do you sell my data?[​](#do-you-sell-my-data "Direct link to Do you sell my data?") No. ### Do you train AI models on my data?[​](#do-you-train-ai-models-on-my-data "Direct link to Do you train AI models on my data?") No. ### Where is data stored?[​](#where-is-data-stored "Direct link to Where is data stored?") US (primary) and EU (for EU workspaces). Postgres on managed infrastructure. Encryption at rest, TLS in transit. ### How do I delete my account / data?[​](#how-do-i-delete-my-account--data "Direct link to How do I delete my account / data?") **Settings → Delete account** at the bottom of the page. Deletion is irreversible and wipes all funnel data, tracking events, email sends, and profiles within 30 days. Individual profile deletion (e.g., for GDPR right-to-be-forgotten requests from your customers): **Profiles → \[profile] → Delete profile**. *** ## Troubleshooting[​](#troubleshooting "Direct link to Troubleshooting") ### I connected Stripe but my CUSTOMER stage is zero.[​](#i-connected-stripe-but-my-customer-stage-is-zero "Direct link to I connected Stripe but my CUSTOMER stage is zero.") 1. Did you wait at least 15 minutes after connecting? 2. Did you click **Sync now** manually? 3. Do you actually have active subscriptions in the connected mode (live vs test)? 4. Is the product mapped to the TRIAL / CUSTOMER conversion? **Conversion → Stripe Paid → Target products**. ### `identify` isn't linking my users.[​](#identify-isnt-linking-my-users "Direct link to identify-isnt-linking-my-users") 1. Check devtools → Network tab → filter by `tracking`. You should see a POST with `type: "identify"` in the payload. 2. Make sure `identify` fires after the tracking script loads (it's queued if not, but this is a common source of "why isn't it working" confusion). 3. Check that the `userId` you're passing is unique per user and consistent across sessions (don't pass session IDs). ### My funnel shows numbers but they seem wrong.[​](#my-funnel-shows-numbers-but-they-seem-wrong "Direct link to My funnel shows numbers but they seem wrong.") 1. Check the time range — is it what you expected? 2. Check if any splits are active that might be filtering traffic out. 3. Check the data source feeding that stage — provider dashboard shows last-sync time and any errors. ### Something else is broken.[​](#something-else-is-broken "Direct link to Something else is broken.") Email **** with: * Your account email * URL of the funnel * What you expected vs what you see * Screenshot * Time (with timezone) when it happened *** ## Does FunnelFizz work with \[thing]?[​](#does-funnelfizz-work-with-thing "Direct link to Does FunnelFizz work with \[thing]?") * **Next.js** — yes, App Router or Pages Router. * **Astro / Remix / SvelteKit / SolidStart** — yes. * **WordPress / Webflow / Framer / Squarespace / Shopify** — yes. * **React Native / Expo** — web-only today. Native mobile SDK is on the roadmap. * **Electron** — yes (web tracking works in Electron's BrowserWindow). * **iOS / Android native apps** — web tracking for in-app webviews works. True native SDKs coming. * **Chrome extensions** — partially; background-script tracking isn't supported, but content- script tracking works on the pages your extension injects into. * **Stripe Billing** — yes. **Stripe Checkout** — yes. **Stripe Connect (platforms)** — yes, with platform accounts. * **PayPal / Lemon Squeezy / Paddle** — no direct integration today. Use custom events to signal conversions manually. *** Still stuck? **** — we usually reply the same day. --- Copy as markdown[View .md](https://docs.funnelfizz.com/features/automation "View the raw markdown for this page")[Open in Claude](https://claude.ai/new?q=Read%20https%3A%2F%2Fdocs.funnelfizz.com%2Ffeatures%2Fautomation.md%20and%20help%20me%20with%20this%20FunnelFizz%20topic%3A%20Automation "Open this page in Claude with context")[Open in ChatGPT](https://chat.openai.com/?q=Read%20https%3A%2F%2Fdocs.funnelfizz.com%2Ffeatures%2Fautomation.md%20and%20help%20me%20with%20this%20FunnelFizz%20topic%3A%20Automation "Open this page in ChatGPT with context") # Automation FunnelFizz automations are **workflows** — DAGs of Send / Wait / Split steps — that fire when a specific event happens (a profile enters a stage, a trial starts, a subscription activates). For conceptual overview, see [Concepts → Automations](https://docs.funnelfizz.com/concepts/automations.md). This page is the feature reference. ## The canvas editor[​](#the-canvas-editor "Direct link to The canvas editor") Open any stage → **Automations** tab → **Create automation**. You get a ReactFlow canvas with: * **Step palette** (left) — drag any step type onto the canvas. * **Canvas** (center) — arrange steps and connect them with edges. * **Settings** (right, when a step is selected) — configure that specific step. * **Toolbar** — save, validate, activate/deactivate. Each step is a card with a handle on the left (input) and one or two handles on the right (outputs). Drag from an output handle to another step's input handle to connect them. Splits have two output handles; everything else has one. ## Triggers[​](#triggers "Direct link to Triggers") Pick the trigger when you create the automation: | Trigger | When it fires | | ------------------------ | ------------------------------------------------ | | `subscription_trial` | Stripe subscription enters `status = "trialing"` | | `subscription_paid` | Stripe subscription enters `status = "active"` | | `stage_enter` *(legacy)* | Profile enters any stage | Only one trigger per automation. If you want the same sequence to fire on both trial and paid events, create two automations. ## Steps[​](#steps "Direct link to Steps") ### SEND\_EMAIL[​](#send_email "Direct link to SEND_EMAIL") Composes and queues an email send. **Config:** * **Subject** — string (supports `{{variables}}`). * **Sender** — pick from your verified senders. * **Blocks** — the email body (same MJML block system as standalone campaigns). **What happens:** the step writes an `EmailSend` row with `status = "QUEUED"`. The email-queue cron picks it up within a minute and actually sends it. The step completes immediately after queuing — it doesn't wait for the email to be delivered. If you want the workflow to wait for an *open* or *click*, use a `WAIT_UNTIL` step next. ### WAIT[​](#wait "Direct link to WAIT") Pauses for a fixed duration. **Config:** * **Months / Days / Hours / Minutes** — pick any combination. **What happens:** sets `nextStepAt = now + delay`. The cron re-claims the run when the delay elapses. ### WAIT\_UNTIL[​](#wait_until "Direct link to WAIT_UNTIL") Pauses until an event fires, with optional timeout. **Config:** * **Event name** — `email_opened`, `email_clicked`, `pageview`, custom event names. * **Timeout** (optional) — "give up after X duration." **What happens:** sets status to `WAITING_FOR_EVENT`. When the event fires on this profile, the run flips back to `RUNNING` and continues. If the timeout expires first, the run also continues, but you can branch based on which happened via a CONDITIONAL\_SPLIT immediately after. ### CONDITIONAL\_SPLIT[​](#conditional_split "Direct link to CONDITIONAL_SPLIT") Branches based on a condition. **Config:** * **Condition** — examples: * `email_opened == true` * `pageview_count >= 3` * `country in ['US', 'CA']` * `profile.plan == 'pro'` * **Output A** — the "matched" branch. * **Output B** — the "didn't match" branch. **What happens:** the cron evaluates the condition against the profile's current state, follows the matching edge. ### PERCENTAGE\_SPLIT[​](#percentage_split "Direct link to PERCENTAGE_SPLIT") Deterministic A/B split. **Config:** * **Percent to A** — e.g., 50 (other 50% goes to B). **What happens:** `hash(runId + stepId) mod 100 < percent` determines the branch. Same profile always takes the same branch, so you can A/B test variants of downstream content. ## Saving, validating, activating[​](#saving-validating-activating "Direct link to Saving, validating, activating") * **Save** — writes the DAG to the database. Automation stays in `DRAFT`. * **Validate** — checks for issues: disconnected nodes, cycles (DAG must be acyclic), missing config on any step. * **Activate** — flips status to `ACTIVE`. New profiles matching the trigger enter the flow. Deactivating is symmetric: existing runs keep walking through the graph, but no new profiles enter. ## Per-profile runs[​](#per-profile-runs "Direct link to Per-profile runs") When an automation is ACTIVE and the trigger fires for a profile, FunnelFizz creates an `AutomationRun` row: * `automationId` — which automation. * `profileId` — which profile. * `currentStepId` — where they are in the graph. * `status` — RUNNING, WAITING\_FOR\_EVENT, COMPLETED, CANCELLED. * `nextStepAt` — when to wake them up. The cron every 5 minutes claims due runs, advances them by one step, updates the row. Each profile is independent of every other. ## Cancellation[​](#cancellation "Direct link to Cancellation") A run is cancelled when: * The profile unsubscribes from your emails (workspace-wide). * You manually pause the automation and explicitly cancel in-flight runs. * The profile is deleted (rare; happens on data-export/delete requests). ## Common patterns[​](#common-patterns "Direct link to Common patterns") ### Trial-to-paid conversion[​](#trial-to-paid-conversion "Direct link to Trial-to-paid conversion") ``` trigger: subscription_trial │ [SEND_EMAIL: "Welcome, here's the quickstart"] │ [WAIT: 2 days] │ [SEND_EMAIL: "Here are 3 things Pro users do"] │ [WAIT: 4 days] │ [CONDITIONAL_SPLIT: profile.subscriptionStatus == "active"] │ │ ▼ true (already upgraded) ▼ false [END] [SEND_EMAIL: "Trial ends tomorrow"] ``` ### Churn recovery[​](#churn-recovery "Direct link to Churn recovery") ``` trigger: stage_enter CUSTOMER where bucket == "Churned" │ [WAIT: 7 days] │ [SEND_EMAIL: "Can you tell us why you left?"] │ [WAIT_UNTIL: email_clicked (timeout: 14 days)] │ │ ▼ clicked ▼ timed out [SEND_EMAIL: 20% off comeback] [SEND_EMAIL: 30% off last chance] ``` ### A/B test your onboarding[​](#ab-test-your-onboarding "Direct link to A/B test your onboarding") ``` trigger: subscription_trial │ [PERCENTAGE_SPLIT: 50% to A] │ │ ▼ A ▼ B [SEND: variant A] [SEND: variant B] ``` After enough runs, compare the downstream conversion rates in the campaign dashboard. ## Plan limits[​](#plan-limits "Direct link to Plan limits") | | FREE | HOBBY | PRO | | --------------------- | ---- | ----- | --- | | Automations per stage | 0 | 1 | 5 | | Max step depth | — | 3 | 5 | Depth is the longest path from trigger to any leaf. A 3-step linear sequence with one branch is depth 3, not 4. ## What's not here (yet)[​](#whats-not-here-yet "Direct link to What's not here (yet)") * **Cross-funnel automations.** Automations are scoped to one stage in one funnel. Global automations are on the roadmap. * **Custom HTTP action step.** No "make an API call" step today. Use Zapier or n8n for outbound webhooks if you need them. * **Conditional entry filters.** Every profile matching the trigger enters. If you need to filter, put a `CONDITIONAL_SPLIT` as the first step with an "end" branch for non-matches. *** **Next:** [Splitting →](https://docs.funnelfizz.com/features/splitting.md) — the eight split conditions. --- Copy as markdown[View .md](https://docs.funnelfizz.com/features/email "View the raw markdown for this page")[Open in Claude](https://claude.ai/new?q=Read%20https%3A%2F%2Fdocs.funnelfizz.com%2Ffeatures%2Femail.md%20and%20help%20me%20with%20this%20FunnelFizz%20topic%3A%20Email "Open this page in Claude with context")[Open in ChatGPT](https://chat.openai.com/?q=Read%20https%3A%2F%2Fdocs.funnelfizz.com%2Ffeatures%2Femail.md%20and%20help%20me%20with%20this%20FunnelFizz%20topic%3A%20Email "Open this page in ChatGPT with context") # Email The native email engine. No Mailchimp, no HubSpot, no third-party integration — FunnelFizz composes, renders, sends, and tracks everything in-house (using Resend as the delivery layer under the hood). For conceptual background, start with [Concepts → Email](https://docs.funnelfizz.com/concepts/email.md). This page is the complete feature reference. ## What you can do[​](#what-you-can-do "Direct link to What you can do") * **Drag-and-drop MJML editor** — 13 block types, all mobile-responsive out of the box. * **Dynamic recipient lists** — target by funnel stage, split branch, or subscription status. * **A/B testing** (PRO) — split audience, auto-pick winner by open or click rate. * **Scheduled sends** — queue for future delivery. * **Custom sending domain** (PRO) — verify your own domain with SPF + DKIM + DMARC. * **Open & click tracking** — pixel + URL rewriting, on by default. * **Automatic suppression** — hard bounces and unsubscribes never get emailed again. * **Automations** — see [Features → Automation](https://docs.funnelfizz.com/features/automation.md) for the DAG editor. ## The editor[​](#the-editor "Direct link to The editor") Open a campaign, click **Edit** → **Drag & drop**. You see three panels: ``` ┌──────────┬─────────────────────────┬────────────┐ │ Blocks │ Canvas │ Settings │ │ │ │ │ │ header │ (Your email preview) │ font, size │ │ text │ │ padding, │ │ button │ │ color, │ │ image │ │ alignment │ │ ... │ │ │ └──────────┴─────────────────────────┴────────────┘ ``` Drag blocks from the left, drop them on the canvas, tune settings on the right. Undo / redo work. Mobile preview toggle shows what it'll look like on phones. ### Block types[​](#block-types "Direct link to Block types") | Block | What it is | | ----------- | --------------------------------------------- | | **Title** | H1-style heading, display font | | **Text** | Paragraph, rich text | | **Header** | Top banner with logo + links | | **Footer** | Legal footer with unsubscribe link (required) | | **Button** | CTA button with link | | **Image** | Image with alt text and optional link | | **Logo** | Your brand mark, centered | | **Divider** | Horizontal rule | | **Spacer** | Vertical whitespace | | **Video** | Thumbnail + play overlay linking to a video | | **Columns** | 2- or 3-column layout container | | **Social** | Your social icons (Twitter, LinkedIn, etc.) | | **HTML** | Raw HTML passthrough (advanced) | All blocks render via MJML → inlined CSS HTML. Tested across Gmail, Outlook, Apple Mail, Yahoo, and the major clients. ### Composition modes[​](#composition-modes "Direct link to Composition modes") * **Drag & drop** — default, most people use this. * **HTML** — paste raw HTML if you want total control or have a pre-built template. * **Plain text** — minimal, deliverable-friendly. * **Template** — start from a library of prebuilt templates (welcome, feedback, churn). ## Recipient selection[​](#recipient-selection "Direct link to Recipient selection") When you create a campaign, you pick a **recipient group**: * **Stage-based** — everyone currently in a specific stage of a funnel. * e.g., "all TRIAL users in Main Funnel" * e.g., "all CUSTOMER users in Main Funnel where subscription = active" * **Split-filtered** — a specific track within a split funnel. * e.g., "all TRIAL users who came from UTM source = reddit" * **Churn-targeted** — the Churned slice of CUSTOMER stage. * e.g., "everyone who was a paying customer but doesn't have an active sub anymore" The list re-evaluates at send time, not at draft time. If someone moves between stages between drafting and sending, they're automatically included/excluded correctly. ## A/B testing (PRO)[​](#ab-testing-pro "Direct link to A/B testing (PRO)") 1. Create a campaign. 2. In the settings, flip **A/B test** on. 3. Configure: * **Variant A** content. * **Variant B** content. * **Test audience %** — how much of the list goes to A/B before winner is declared (typical: 20%). * **Winner criterion** — open rate or click rate. * **Test duration** — how long before picking the winner (typical: 4 hours). 4. Schedule. At send time: * FunnelFizz randomly assigns each test-audience recipient to A or B (deterministic by recipient hash so re-runs are stable). * After the test duration, the cron compares variant stats and picks the winner. * The remaining audience gets the winning variant. You can watch the test in progress from the campaign dashboard. ## Scheduled sends[​](#scheduled-sends "Direct link to Scheduled sends") Set a send time in the future. The scheduled-campaigns cron runs every minute, picks up campaigns past their `scheduledAt`, and enqueues sends to the recipient list. You can cancel a scheduled campaign up until the cron fires. After that, the sends are in the queue and will go out. ## Custom sending domain[​](#custom-sending-domain "Direct link to Custom sending domain") By default on HOBBY, emails send from `funnelfizz.com`. That's fine for small volume but corporate mail servers sometimes throttle traffic from shared sender domains. On PRO, verify your own: 1. Go to **Settings → Email → Domains → Add domain**. 2. Enter your domain (e.g., `mail.example.com` — we recommend a subdomain, not your apex). 3. FunnelFizz shows you three DNS records: * **MX** for the subdomain pointing to Resend. * **TXT (SPF)** — adds our sending servers to your authorized senders. * **TXT (DKIM)** — signing key for DKIM authentication. * **TXT (DMARC)** (optional but recommended) — your DMARC policy. 4. Add the records to your DNS registrar (Cloudflare, Namecheap, Route 53, etc.). 5. Back in FunnelFizz, click **Verify**. We poll DNS and mark the domain verified when all records resolve. 6. Once verified, you can pick this domain as the sender for any campaign. Full guide: [Tutorials → Email drip campaign basics](https://docs.funnelfizz.com/tutorials/email-drip-campaign-basics.md#custom-domain). ## Tracking[​](#tracking "Direct link to Tracking") * **Opens** — a 1x1 pixel is injected into the email. When the recipient's email client loads images, we log an `email_opened` event. * **Clicks** — every link is rewritten to go through `funnelfizz.com/r/` which redirects and logs an `email_clicked` event. * Both events flow into your funnel as tracking events, so you can split or automate on "who opened the day-2 email." Opens are notoriously unreliable (Apple Mail Privacy Protection pre-loads images, inflating open rates). Use click rate for serious measurement. ## Suppression lists[​](#suppression-lists "Direct link to Suppression lists") * **Unsubscribes** — every email has a required unsubscribe link. If a recipient clicks it, they're added to the workspace-wide unsubscribe list and never receive anything else. * **Hard bounces** — bounces marked permanent (invalid email, domain doesn't exist) go on the bounce list automatically. * **Complaints** — anyone who marks you as spam goes on the list. These lists are workspace-wide, not per-campaign or per-funnel. An unsubscribe applies forever. ## Plan limits[​](#plan-limits "Direct link to Plan limits") | | FREE | HOBBY | PRO | | -------------------- | ---- | --------- | --------- | | Campaigns | — | Unlimited | Unlimited | | Emails/month | — | 3,000 | 20,000 | | A/B testing | — | — | Yes | | Custom domain | — | — | Yes | | Automations/stage | — | 1 | 5 | | Max automation depth | — | 3 steps | 5 steps | Quotas reset the 1st of each calendar month. If you burn through your quota mid-month, new sends are queued until the next reset — or upgrade. ## What's not here[​](#whats-not-here "Direct link to What's not here") * **No visual email builder for signups** — FunnelFizz does outbound only. Signup forms live on your site and fire events back via the tracking script. * **No transactional email SDK** — for password-reset-style emails from your own app, use Resend directly. (FunnelFizz is for marketing email, not app email.) *** **Next:** [Automation →](https://docs.funnelfizz.com/features/automation.md) — the DAG engine. --- Copy as markdown[View .md](https://docs.funnelfizz.com/features/extensions "View the raw markdown for this page")[Open in Claude](https://claude.ai/new?q=Read%20https%3A%2F%2Fdocs.funnelfizz.com%2Ffeatures%2Fextensions.md%20and%20help%20me%20with%20this%20FunnelFizz%20topic%3A%20Extensions "Open this page in Claude with context")[Open in ChatGPT](https://chat.openai.com/?q=Read%20https%3A%2F%2Fdocs.funnelfizz.com%2Ffeatures%2Fextensions.md%20and%20help%20me%20with%20this%20FunnelFizz%20topic%3A%20Extensions "Open this page in ChatGPT with context") # Extensions **Extensions** are an optional second pipeline after the CUSTOMER stage that tracks what happens next — specifically, do your customers upgrade to higher-tier plans? Your main funnel answers: *"how do I turn strangers into paying customers?"* Your extension answers: *"how do I turn paying customers into more-paying customers?"* For conceptual background, see [Concepts → Splitting & Extensions](https://docs.funnelfizz.com/concepts/splitting-and-extensions.md#extensions). This page is the feature reference. ## The extension stages[​](#the-extension-stages "Direct link to The extension stages") Three stages that mirror the main funnel (minus Awareness): | Stage | What it measures | | --------------------- | -------------------------------------------------------------------------------- | | **Ext Consideration** | Customers still active on your product in the window (fired a pageview or event) | | **Ext Trial** | Ext-Consideration customers who started a higher-tier trial | | **Ext Customer** | Customers paying at a higher tier than their anchor | On the canvas, they render to the right of the main CUSTOMER stage with double-line edges. ``` AWARENESS → CONSIDERATION → TRIAL → CUSTOMER ══▶ Ext CONSIDERATION ══▶ Ext TRIAL ══▶ Ext CUSTOMER ``` ## The anchor product[​](#the-anchor-product "Direct link to The anchor product") When you enable extensions on a funnel, you pick an **anchor product**. This is the entry-level tier — the product whose customers you're hoping to upgrade. Common choices: * Free tier → anchor = "free plan" (if you track freemium via a Stripe $0 subscription). * Entry paid tier → anchor = "Hobby" or "Basic" or whatever your cheapest paid plan is called. * Single tier with add-ons → anchor = "base plan" (and extensions track seats / usage add-ons). The cohort FunnelFizz tracks for extensions is: > Customers who have **ever** subscribed to the anchor product, are currently active in any form (main CUSTOMER stage), and match the funnel's split conditions. ## The churn trichotomy (main funnel)[​](#the-churn-trichotomy-main-funnel "Direct link to The churn trichotomy (main funnel)") Before we dig into extensions, the related mechanic on the main funnel: the CUSTOMER stage splits paying customers into three buckets: | Bucket | Definition | | ------------ | ------------------------------------------------------------------------------------- | | **Retained** | Still has an active/trialing subscription at the anchor product | | **Upgraded** | Has an active/trialing subscription priced higher than anchor (but not anchor itself) | | **Churned** | No active/trialing subscription anywhere in the workspace | This replaces the old "canceled = churned" model with something that understands upgrades aren't losses. ## How extensions compute[​](#how-extensions-compute "Direct link to How extensions compute") ### Ext Consideration[​](#ext-consideration "Direct link to Ext Consideration") **Query:** customers in the anchor cohort who fired a pageview or event in the reporting window. **Meaning:** "active customers who are engaged with my product." If they don't log in at all, they can't upgrade — so this is the pool of upgrade candidates. ### Ext Trial[​](#ext-trial "Direct link to Ext Trial") **Query:** customers in the anchor cohort with a Stripe subscription in `status = "trialing"` AND monthly amount > anchor's monthly amount. **Meaning:** "customers who are trying a more expensive tier." ### Ext Customer[​](#ext-customer "Direct link to Ext Customer") **Query:** customers in the anchor cohort with a Stripe subscription in `status = "active"` AND monthly amount > anchor's monthly amount. **Meaning:** "customers who are paying more than they used to." ## Enabling extensions on a funnel[​](#enabling-extensions-on-a-funnel "Direct link to Enabling extensions on a funnel") 1. Open your funnel. 2. **Settings → Extensions → Enable**. 3. Pick your **anchor product** from the dropdown (auto-populated from your Stripe products). 4. Save. Three new stages appear on the canvas to the right of CUSTOMER. Metrics populate as data exists. If no one has upgraded yet, all three show 0 — which is itself valuable information ("our upgrade rate is zero, that's where our growth lever is"). ## Conversion rates on extensions[​](#conversion-rates-on-extensions "Direct link to Conversion rates on extensions") Between each extension stage, you get a conversion rate: * **CUSTOMER → Ext Consideration:** the % of your customers who are actually *active* in product. * **Ext Consideration → Ext Trial:** the % of active customers who started a higher-tier trial. * **Ext Trial → Ext Customer:** the % of trial upgrades that became paid. Together these tell you where your upsell funnel is leaking. If CUSTOMER → Ext Consideration is low, you have an activation/retention problem. If Ext Consideration → Ext Trial is low, your in-product upgrade prompts aren't working. If Ext Trial → Ext Customer is low, the higher tier isn't delivering value during trial. ## Splits on extensions[​](#splits-on-extensions "Direct link to Splits on extensions") You can split extension stages just like main funnel stages. Common ones: * **By anchor product** — if you have multiple entry tiers, split to see which upgrades most often. * **By channel** — do Reddit-sourced customers upgrade at a different rate than email-sourced? * **By cohort month** — are customers who signed up in March upgrading faster than April customers? ## When NOT to use extensions[​](#when-not-to-use-extensions "Direct link to When NOT to use extensions") * **Single-tier products.** If you sell one product at one price, there's nothing to upgrade to — extensions would always be zero. * **Usage-based pricing with no tier structure.** If everyone's on the same plan and just pays more as they use more, use a different dashboard to track usage (extensions aren't designed for that — they assume discrete tier jumps). * **One-time purchases.** Extensions assume subscriptions. For e-commerce with repeat purchases, use the main funnel's repeat-buyer metrics instead. ## Canvas visual cues[​](#canvas-visual-cues "Direct link to Canvas visual cues") Extension stages are visually distinct: * **Double-line edges** between extension stages (vs single-line on the main funnel) help you spot them at a glance. * **Tint color** matches the parent funnel for visual cohesion. * The label format is **"Ext \[StageName]"** to clearly distinguish from main stages. ## Combining extensions + churn recovery automations[​](#combining-extensions--churn-recovery-automations "Direct link to Combining extensions + churn recovery automations") A powerful pattern: 1. Enable extensions with your entry tier as the anchor. 2. Look at the Churned bucket of the main CUSTOMER stage. 3. Set up a [churn recovery automation](https://docs.funnelfizz.com/features/automation.md#common-patterns) targeting the Churned slice with a win-back offer at a *discounted* tier. 4. If they come back on the discounted tier, the main funnel Retained count climbs; if they upgrade later, the Ext Customer count climbs. You get compound observability: did we save churn? Did those saves eventually upgrade? ## Roadmap[​](#roadmap "Direct link to Roadmap") * **Multi-anchor support.** Today you pick one anchor. Multi-tier products will benefit from tracking "Hobby → Pro" and "Pro → Enterprise" as separate extension pipelines. * **Expansion revenue attribution.** Pull in seat expansions / usage overages from Stripe so non-tier-upgrade expansion shows up too. *** That's all the feature pages! Next: [Tutorials →](https://docs.funnelfizz.com/tutorials/building-a-basic-saas-funnel.md). --- Copy as markdown[View .md](https://docs.funnelfizz.com/features/mentions "View the raw markdown for this page")[Open in Claude](https://claude.ai/new?q=Read%20https%3A%2F%2Fdocs.funnelfizz.com%2Ffeatures%2Fmentions.md%20and%20help%20me%20with%20this%20FunnelFizz%20topic%3A%20Mentions "Open this page in Claude with context")[Open in ChatGPT](https://chat.openai.com/?q=Read%20https%3A%2F%2Fdocs.funnelfizz.com%2Ffeatures%2Fmentions.md%20and%20help%20me%20with%20this%20FunnelFizz%20topic%3A%20Mentions "Open this page in ChatGPT with context") # Mentions **Mentions** is a PRO feature that continuously scans the internet for mentions of your brand and rolls them up into an AWARENESS-stage dashboard widget. It answers questions like: * How much buzz is my launch getting this week? * Are people talking about us on HackerNews or Reddit? * What's the sentiment breakdown — are we getting roasted or celebrated? ## What gets scanned[​](#what-gets-scanned "Direct link to What gets scanned") Thirteen sources. Some need no auth, some need a workspace-level OAuth connection, some use environment-level API keys FunnelFizz manages. | Source | Auth | Coverage | | -------------------- | ---------- | ----------------------------------------- | | HackerNews (Algolia) | None | All mentions in stories and comments | | Google News RSS | None | News articles syndicated to Google News | | Stack Exchange | None | Questions and answers across all SE sites | | DEV.to | None | Articles and comments | | Brave Search | FF key | Web search result scrape | | GitHub | FF token | Issues, PRs, commits, READMEs | | ProductHunt | FF token | Product launches, comments | | X (Twitter) | Your OAuth | Posts and quote-tweets | | Reddit | Your OAuth | Posts and comments | | Instagram | Your OAuth | Caption mentions (limited) | | TikTok | Your OAuth | Video title mentions | | YouTube | Your OAuth | Video title + description mentions | | LinkedIn | Your OAuth | Post mentions | For the sources that use your own OAuth (X, Reddit, Instagram, TikTok, YouTube, LinkedIn), connect the provider first ([Providers](https://docs.funnelfizz.com/features/providers.md)) — Mentions re-uses those credentials. No extra connection step needed. ## How it works[​](#how-it-works "Direct link to How it works") 1. You configure a **brand monitor** — brand name + optional keywords + optional exclusions. 2. FunnelFizz runs a cron every hour that fans out queries across all 13 sources. 3. Results are deduplicated by `externalId` (platform-specific, e.g., HN story ID, Reddit post ID) so the same mention isn't counted twice even if it bubbles up on multiple scans. 4. Results are stored with source, URL, excerpt, timestamp, and an auto-derived sentiment (positive / neutral / negative). 5. The dashboard shows totals, daily trend, top sources, recent mentions feed. ## Setting up a brand monitor[​](#setting-up-a-brand-monitor "Direct link to Setting up a brand monitor") Go to any **CUSTOMER stage** on your canvas and click the **Mentions** tab. 1. **Brand name** — exact string to match. Case-insensitive. E.g., `FunnelFizz`. 2. **Inclusion keywords** (optional) — at least one must also be present. E.g., `marketing`, `funnel`, `conversion`. Useful to disambiguate common brand names. 3. **Exclusion keywords** (optional) — if any are present, the mention is skipped. E.g., `not funnelfizz`, `fake funnelfizz` to prevent trolling and spam from counting. Click **Start monitoring** and the first scan kicks off immediately. Subsequent scans run hourly. ## What you see on the dashboard[​](#what-you-see-on-the-dashboard "Direct link to What you see on the dashboard") * **Total mentions** in the selected period. * **Sources breakdown** — pie chart, mentions per source. * **Daily trend** — line chart, last 7 / 30 / 90 days. * **Sentiment** — stacked bar: positive / neutral / negative. * **Recent mentions feed** — 20 most recent, each with link out to the source, excerpt, author/handle, timestamp. Clicking any mention opens the original source in a new tab. We don't republish the full text (copyright, attribution); you get the excerpt + link. ## Sentiment[​](#sentiment "Direct link to Sentiment") Sentiment is derived automatically from the mention text using keyword classification (positive signal words, negative signal words, emoji, punctuation, etc.). It's approximate — use the feed to spot-check anything that looks surprising. ## Constraints[​](#constraints "Direct link to Constraints") * **One brand per workspace.** PRO plan includes one brand monitor. If you need to track multiple products, each needs its own workspace. * **Hourly sync is the cadence.** Don't refresh every 30 seconds hoping for new mentions — the API rate limits across providers make that uneconomical. * **Old mentions aren't backfilled.** We start scanning the moment you enable monitoring. Mentions older than that won't appear unless the source's API returns them in current searches (HackerNews does, most don't). ## What it costs[​](#what-it-costs "Direct link to What it costs") Mentions is a PRO-only feature. FunnelFizz covers the third-party API costs for the keyless and FF-keyed sources. You cover the OAuth-based ones via the quota your own platforms allow you (most have generous free tiers). ## Privacy & ethics[​](#privacy--ethics "Direct link to Privacy & ethics") * We scan public content only. No scraping of private groups, DMs, or gated communities. * If a source's robots.txt or ToS disallows scraping, we honor it. * We store the excerpt + URL, not a full copy of the source content. * Deletion: if a mention is deleted at the source, we don't retroactively remove it from your dashboard, but the link will 404. ## Troubleshooting[​](#troubleshooting "Direct link to Troubleshooting") * **"No mentions yet" after 2 hours.** Try a broader query — drop exclusion keywords, or check your brand name isn't misspelled. You can test manually with the **"Run scan now"** button in settings. * **Too many false positives.** Add inclusion keywords to require context words, or add exclusion keywords for the common false matches. * **Missing a source.** Make sure the corresponding provider OAuth is connected (for X, Reddit, etc.) or that your workspace has the feature enabled in your plan. *** **Next:** [SEO →](https://docs.funnelfizz.com/features/seo.md) — dashboard widget for search performance (preview). --- Copy as markdown[View .md](https://docs.funnelfizz.com/features/providers "View the raw markdown for this page")[Open in Claude](https://claude.ai/new?q=Read%20https%3A%2F%2Fdocs.funnelfizz.com%2Ffeatures%2Fproviders.md%20and%20help%20me%20with%20this%20FunnelFizz%20topic%3A%20Providers "Open this page in Claude with context")[Open in ChatGPT](https://chat.openai.com/?q=Read%20https%3A%2F%2Fdocs.funnelfizz.com%2Ffeatures%2Fproviders.md%20and%20help%20me%20with%20this%20FunnelFizz%20topic%3A%20Providers "Open this page in ChatGPT with context") # Providers **Providers** are the external accounts FunnelFizz reads data from. This page is the complete reference for every supported provider: what it is, what permissions it needs, what data you get, and where it feeds into your funnel. For the quickstart-level walkthrough, see [Quickstart → Connect providers](https://docs.funnelfizz.com/quickstart/connect-providers.md). ## The roster[​](#the-roster "Direct link to The roster") | Provider | Auth | Plan | Stages | Refresh | | ----------------------------------------------- | ------------- | ----- | --------------- | ----------------- | | [X (Twitter)](#x-twitter) | OAuth 2.0 | FREE | AWARENESS | 30 min | | [YouTube](#youtube) | OAuth 2.0 | FREE | AWARENESS | 30 min | | [LinkedIn](#linkedin) | OAuth 2.0 | FREE | AWARENESS | 30 min | | [Instagram](#instagram) | OAuth 2.0 | FREE | AWARENESS | 30 min | | [TikTok](#tiktok) | OAuth 2.0 | FREE | AWARENESS | 30 min | | [Reddit](#reddit) | OAuth 2.0 | HOBBY | AWARENESS | 30 min | | [Google Search Console](#google-search-console) | OAuth 2.0 | HOBBY | AWARENESS | 60 min | | [Google Ads](#google-ads) | OAuth 2.0 | HOBBY | AWARENESS | 60 min | | [Stripe](#stripe) | API key / App | FREE | TRIAL, CUSTOMER | 15 min + webhooks | All OAuth providers are read-only. FunnelFizz never posts, advertises, or modifies data on your behalf. ## How connecting works (common flow)[​](#how-connecting-works-common-flow "Direct link to How connecting works (common flow)") 1. Go to your funnel's stage → **Connect provider**, or **Settings → Integrations → \[Provider]**. 2. Click **Connect \[Provider]**. 3. Provider OAuth page opens. Review the read-only permissions. Click **Allow**. 4. You're redirected back with a green checkmark. Behind the scenes: * A single-use CSRF nonce is generated and embedded in the `state` parameter. * The OAuth code is exchanged for access + refresh tokens. * Tokens are encrypted with AES-256 at rest, scoped to your workspace. * A background sync job runs immediately to pull your recent metrics. ## Workspace-wide, reusable across funnels[​](#workspace-wide-reusable-across-funnels "Direct link to Workspace-wide, reusable across funnels") Connected providers belong to your **workspace**, not a specific funnel. Connect X once, and every funnel in the workspace can attribute X impressions without re-authenticating. On the connect screen for any new funnel, you'll see a **"Reuse existing"** option. Each stage links to a provider via a `connections[]` array — so a single provider can power multiple stages across multiple funnels simultaneously. *** ## X (Twitter)[​](#x-twitter "Direct link to X (Twitter)") **Scopes requested:** `tweet.read`, `users.read`, `follows.read`, `offline.access`. **Data you get:** * Post impressions (past 28 days) * Likes, reposts, replies, bookmarks, quotes * Follower counts (current + daily change) * Top posts by impression (top 10) **How it feeds AWARENESS:** total post impressions in the period become your AWARENESS metric (or contribute to it, if you have other providers too). **Gotchas:** * X's free API tier has tight rate limits. For high-traffic accounts, you may hit 15-minute cooldowns. FunnelFizz handles rate limiting gracefully and retries on the next sync. * Premium X accounts get more granular post-level metrics via the v2 API. *** ## YouTube[​](#youtube "Direct link to YouTube") **Scopes requested:** `youtube.readonly`, `yt-analytics.readonly`. **Data you get:** * Total video views (rolling 28 days) * Subscriber count * Watch time (hours) * Top videos by views **How it feeds AWARENESS:** video views contribute to AWARENESS impressions. *** ## LinkedIn[​](#linkedin "Direct link to LinkedIn") **Scopes requested:** `r_organization_social`, `r_liteprofile`, `w_member_social` *(read only — the "w\_" scope is LinkedIn's naming quirk; we do not post)*. **Data you get:** * Post impressions * Reactions, comments, shares * Follower growth * Top posts **Gotchas:** * LinkedIn distinguishes personal profiles from company pages. You'll pick which one to connect during OAuth. To track both, connect each separately. *** ## Instagram[​](#instagram "Direct link to Instagram") **Scopes requested:** `user_profile`, `user_media`, `instagram_basic`. **Data you get:** * Post reach and impressions * Engagement (likes, comments, saves, shares) * Follower count * Top posts **Gotchas:** * Instagram Business or Creator accounts only. Personal accounts don't expose the Graph API. * Connection goes via Facebook Business Manager. You may need to accept Meta's terms once. *** ## TikTok[​](#tiktok "Direct link to TikTok") **Scopes requested:** `user.info.basic`, `video.list`, `research.data.basic`. **Data you get:** * Video views * Likes, comments, shares * Follower growth * Top videos *** ## Reddit[​](#reddit "Direct link to Reddit") **Plan:** HOBBY and above. **Scopes requested:** `read`, `history`, `identity`. **Data you get:** * Your submitted posts and their upvotes * Comment counts on your posts * Subreddit-level engagement (for subs you mod) **Gotchas:** * Reddit heavily rate-limits third-party apps. Sync is 30 min at best; if you're posting frequently, metrics may lag. *** ## Google Search Console[​](#google-search-console "Direct link to Google Search Console") **Plan:** HOBBY and above. **Scopes requested:** `webmasters.readonly`. **Data you get:** * Impressions (how often you showed up in Google results) * Clicks (CTR) * Average position * Top queries (15) * Top landing pages (15) * Country breakdown * Device breakdown **Gotchas:** * GSC has a built-in 2-day data lag. "Today" numbers are always incomplete — wait 48 hours for reliable trend analysis. * You need to have verified the site in GSC already. If you haven't, do that first at [search.google.com/search-console](https://search.google.com/search-console). *** ## Google Ads[​](#google-ads "Direct link to Google Ads") **Plan:** HOBBY and above. **Scopes requested:** `adwords` (read-only). **Data you get:** * Impressions * Clicks (CTR) * Average CPC * Spend * Top campaigns **Gotchas:** * Manager (MCC) accounts work — FunnelFizz lets you pick which sub-account to connect. * Individual campaign-level data is pulled, not keyword-level. *** ## Stripe[​](#stripe "Direct link to Stripe") See [Quickstart → Connect Stripe](https://docs.funnelfizz.com/quickstart/connect-stripe.md) for setup. **Auth:** Restricted API key OR the FunnelFizz Stripe App (same key under the hood). **Required permissions (read-only):** * Customers * Charges and Refunds * Events * Products and Prices * Invoices * Subscriptions * Balance **Data you get:** * Customer records (created date, email, country, default payment method) * Subscriptions (product, price, trial end, current period, status) * Charges (gross, net, refunds) * Invoices * Events (full webhook stream for real-time updates) **How it feeds your funnel:** * TRIAL stage: counts subscriptions with `status = "trialing"`. * CUSTOMER stage: counts subscriptions with `status = "active"`. * Churn trichotomy: computed from subscription state transitions. **Sync frequency:** 15 min via polling + real-time via webhooks (webhooks are auto-configured via the Stripe App, optional for API-key mode). ## Disconnecting or re-authenticating[​](#disconnecting-or-re-authenticating "Direct link to Disconnecting or re-authenticating") **Settings → Integrations → \[Provider] → Disconnect** revokes FunnelFizz's access and clears the stored tokens. Historical *aggregated* data stays in your funnel; new data stops flowing. If a provider's token expires (most OAuth tokens rotate every 60–90 days) and auto-refresh fails, you'll see a red "Reconnect" banner on the integration. Click it, re-auth, done. ## Manual entries[​](#manual-entries "Direct link to Manual entries") For data sources that aren't in the provider list, every stage has a **Manual** input where you can paste daily impression/visitor/customer numbers. Clunky but available — and useful for backfilling historical data from before you connected a provider. ## Requesting a new provider[​](#requesting-a-new-provider "Direct link to Requesting a new provider") Email **** with the platform name and why it matters to your funnel. We prioritize by request volume. *** **Next:** [Mentions →](https://docs.funnelfizz.com/features/mentions.md) — how to monitor 13+ sources for brand mentions. --- Copy as markdown[View .md](https://docs.funnelfizz.com/features/seo "View the raw markdown for this page")[Open in Claude](https://claude.ai/new?q=Read%20https%3A%2F%2Fdocs.funnelfizz.com%2Ffeatures%2Fseo.md%20and%20help%20me%20with%20this%20FunnelFizz%20topic%3A%20SEO "Open this page in Claude with context")[Open in ChatGPT](https://chat.openai.com/?q=Read%20https%3A%2F%2Fdocs.funnelfizz.com%2Ffeatures%2Fseo.md%20and%20help%20me%20with%20this%20FunnelFizz%20topic%3A%20SEO "Open this page in ChatGPT with context") # SEO :::warning Preview-only today The SEO dashboard exists but currently shows **demo data only**. Real Google Search Console ingestion for live funnels is on the near-term roadmap. Skip this page if you need real data today — use your GSC dashboard directly or wait for the live wiring. ::: ## What the SEO dashboard will be[​](#what-the-seo-dashboard-will-be "Direct link to What the SEO dashboard will be") A widget on your funnel that shows: * **Impressions** — how often you appeared in Google's search results. * **Clicks** — how many impressions became visits. * **CTR** — clicks ÷ impressions. * **Average position** — where in the results page you landed on average. * **Top queries** — the 15 search queries you showed up for the most. * **Top landing pages** — the 15 pages most visited from search. * **Country breakdown** — where in the world your searches came from. * **Device breakdown** — desktop vs mobile vs tablet. ## Current state[​](#current-state "Direct link to Current state") * The dashboard component is fully built. * It renders **deterministic mock data** for demo funnels on the marketing site so visitors can see what the UI looks like. * The Google Search Console provider integration ([Providers → GSC](https://docs.funnelfizz.com/features/providers.md#google-search-console)) is connectable — you can OAuth in and tokens are stored correctly. * The pipeline that takes GSC API responses and populates the dashboard **is not yet wired**. If you've connected GSC as a provider, your impressions will show up as part of the AWARENESS stage aggregate count (that part works). The per-query / per-page breakdown is the piece that's pending. ## Workarounds today[​](#workarounds-today "Direct link to Workarounds today") * **Use GSC directly.** [search.google.com/search-console](https://search.google.com/search-console) has everything the FunnelFizz widget will show, and it's updated on Google's schedule (\~48hr lag). * **Connect GSC as a provider** anyway, so the AWARENESS aggregates are accurate. * **Watch the changelog** — when live SEO ships, we'll auto-enable it for anyone who has GSC already connected, no action needed. ## Roadmap[​](#roadmap "Direct link to Roadmap") We're targeting a live ingestion + dashboard release in the next few updates. If you care about this feature specifically, drop a note to **** and we'll prioritize based on demand. *** **Next:** [Email →](https://docs.funnelfizz.com/features/email.md) — the native email engine. --- Copy as markdown[View .md](https://docs.funnelfizz.com/features/splitting "View the raw markdown for this page")[Open in Claude](https://claude.ai/new?q=Read%20https%3A%2F%2Fdocs.funnelfizz.com%2Ffeatures%2Fsplitting.md%20and%20help%20me%20with%20this%20FunnelFizz%20topic%3A%20Splitting "Open this page in Claude with context")[Open in ChatGPT](https://chat.openai.com/?q=Read%20https%3A%2F%2Fdocs.funnelfizz.com%2Ffeatures%2Fsplitting.md%20and%20help%20me%20with%20this%20FunnelFizz%20topic%3A%20Splitting "Open this page in ChatGPT with context") # Splitting Splits let you filter a funnel by one (or more) conditions, so you can see conversion rates for specific cohorts instead of the blended whole-funnel average. For conceptual background, start with [Concepts → Splitting & Extensions](https://docs.funnelfizz.com/concepts/splitting-and-extensions.md). This page is the complete reference for every split condition. ## How splits are evaluated[​](#how-splits-are-evaluated "Direct link to How splits are evaluated") When you add a split, it becomes a **node on the canvas** between two stages. Everything downstream of the split is filtered. Internally, FunnelFizz translates the split condition into a SQL WHERE clause against the `trackingEvents` or `profileSubscriptions` tables. Conditions chain with **AND**. If you nest splits, each additional split adds another AND clause — a visitor must match every split in the chain to appear in the deepest branch. ## The eight split conditions[​](#the-eight-split-conditions "Direct link to The eight split conditions") ### Traffic source[​](#traffic-source "Direct link to Traffic source") **Internal name:** `traffic_source` **Filters on:** `trackingEvents.referrer` (ILIKE pattern match) **Use case:** "show me only traffic that came from reddit.com." **Configuration:** * **Domain** — a substring of the referrer URL. E.g., `reddit.com` matches `https://www.reddit.com/r/saas/...` and `https://old.reddit.com/...`. **Example value:** `twitter.com`, `news.ycombinator.com`, `producthunt.com`. ### UTM parameter[​](#utm-parameter "Direct link to UTM parameter") **Internal name:** `utm` **Filters on:** `trackingEvents.utm` (JSONB field) **Use case:** "show me only traffic from my summer campaign." **Configuration:** * **UTM key** — pick one of: `utm_source`, `utm_medium`, `utm_campaign`, `utm_content`, `utm_term`. * **Value** — exact string match. **Example value:** `utm_campaign = summer-launch`, `utm_source = newsletter`. ### Channel[​](#channel "Direct link to Channel") **Internal name:** `channel` **Filters on:** `trackingEvents.channel` (server-side categorization) **Use case:** "show me only organic search traffic." **Categories:** * `direct` — no referrer, no UTM. * `organic_search` — Google, Bing, DuckDuckGo, Brave, etc. * `social` — X, LinkedIn, Reddit, TikTok, Instagram, YouTube, Facebook. * `paid` — `utm_medium` in `cpc`/`paid`/`ads`, or recognized ad networks. * `email` — `utm_medium = email` or email-client referrers. * `ai_llm` — chatgpt.com, claude.ai, perplexity.ai, gemini.google.com, copilot.microsoft.com. * `referral` — everything else with a referrer. **Configuration:** * **Channel** — pick one from the list above. ### Device type[​](#device-type "Direct link to Device type") **Internal name:** `device_type` **Filters on:** `trackingEvents.deviceType` (UA-parsed) **Use case:** "is my mobile flow converting at a different rate than desktop?" **Options:** `desktop`, `mobile`, `tablet`. ### Country[​](#country "Direct link to Country") **Internal name:** `geography` **Filters on:** `trackingEvents.country` (ISO 3166-1 alpha-2, from MaxMind GeoIP) **Use case:** "only show me US + Canadian traffic." **Configuration:** * **Country** — pick from a 249-country dropdown. **Note:** we don't currently support *multi*-country splits as a single branch. For "US + CA," use two splits stacked together, or filter in the dashboard exports. ### Custom event[​](#custom-event "Direct link to Custom event") **Internal name:** `custom_event` **Filters on:** `trackingEvents.props->>'goal'` (JSONB extract) **Use case:** "only show me people who clicked the pricing CTA." **Configuration:** * **Event name** — pick from the list of events your tracking script has observed. **How to define events:** fire them from your site with `funnelfizz('event', 'pricing_clicked')` or mark elements with `data-fn-goal="pricing_clicked"`. Once you've fired an event, it appears in the dropdown here. ### A/B variant[​](#ab-variant "Direct link to A/B variant") **Internal name:** `ab` **Filters on:** `trackingEvents.abVariant` **Use case:** "compare conversion rates of my hero copy variants." **How it works:** when a visitor lands on your site with `?ab=` in the URL, the tracking script stores that variant in a cookie and tags every subsequent event with it. **Configuration:** * **Variant name** — the string you used in the URL. E.g., `?ab=frog` → filter value `frog`. **Example:** you send two newsletter campaigns, one with `?ab=frog` and one with `?ab=elk`. Split on `ab = frog` and `ab = elk` side by side to see which variant drove more conversions. ### Stripe product[​](#stripe-product "Direct link to Stripe product") **Internal name:** `stripe_product` **Filters on:** `ProfileSubscription` tables (not tracking events) **Use case:** "show me only people on the Pro tier." **Configuration:** * **Product** — pick from the list of Stripe products in your connected account. **Limitation:** this split only applies at the TRIAL and CUSTOMER stages. Since it's a subscription-level filter, it doesn't make sense at AWARENESS or CONSIDERATION (there's no Stripe data yet). ## Nesting splits[​](#nesting-splits "Direct link to Nesting splits") You can split a split. "US visitors from YouTube on mobile" is a three-level chain: 1. **Country = US** → US branch. 2. On the US branch, **Traffic source = youtube.com** → US + YouTube branch. 3. On that branch, **Device = mobile** → US + YouTube + mobile branch. The canvas auto-lays-out nested branches so they don't overlap. There's no hard depth limit but in practice three levels is about as much as remains readable. ## Compound conditions (within a single split)[​](#compound-conditions-within-a-single-split "Direct link to Compound conditions (within a single split)") Each split node has exactly one condition. If you want "A AND B," that's two splits stacked. If you want "A OR B," that's two sibling splits (you'll see both branches on the canvas). There is no "NOT" condition today. If you want "everyone except US traffic," split by country and look at the *other* unfiltered stages — or use the opposite country list. ## Split metrics[​](#split-metrics "Direct link to Split metrics") Each split branch shows: * **Profiles in the branch** at each downstream stage. * **Conversion rates** between stages, computed only for matching profiles. * **Channel, device, country** breakdown of the branch's traffic. * **Top referrers** and **Top UTMs** within the branch. All metrics re-compute when you change the time window. ## Adding a split in the UI[​](#adding-a-split-in-the-ui "Direct link to Adding a split in the UI") 1. On the canvas, click the **+** button between any two stages (or between split and stage). 2. Pick a **condition** from the modal. 3. Fill in the **value**. 4. (Optional) **Name** the branch — shows up as a label on the canvas. 5. **Color** the branch — each split auto-picks a tint to stay visually distinct. 6. **Save**. The branch fills in within a second or two. ## Removing a split[​](#removing-a-split "Direct link to Removing a split") Right-click the split node → **Delete**. Everything downstream of it collapses. The main funnel stays intact. Historical data isn't deleted — if you re-add the same split later, the numbers re-appear. ## Behind the scenes[​](#behind-the-scenes "Direct link to Behind the scenes") The SplitModal in `src/components/canvas/SplitModal.tsx` lists every condition. The SQL translation happens in `src/lib/metrics/split-filter.ts`. If you're curious how a condition ends up as a WHERE clause, that's where to look. ## Patterns we see a lot[​](#patterns-we-see-a-lot "Direct link to Patterns we see a lot") * **Source-of-truth split.** Main funnel + one split per top traffic source. Answers "where does my best traffic come from?" * **UTM campaign split.** Each campaign gets its own branch. Answers "which campaign actually converted, not just got clicks?" * **A/B variant split.** Paired branches comparing two hero copy variants. Combined with [percentage-split automations](https://docs.funnelfizz.com/features/automation.md#percentage_split) for email A/B testing. * **Country + device cross.** Two-level nest. Answers "is my US mobile experience broken?" *** **Next:** [Extensions →](https://docs.funnelfizz.com/features/extensions.md) — the upsell tracking pipeline. --- Copy as markdown[View .md](https://docs.funnelfizz.com/quickstart/connect-providers "View the raw markdown for this page")[Open in Claude](https://claude.ai/new?q=Read%20https%3A%2F%2Fdocs.funnelfizz.com%2Fquickstart%2Fconnect-providers.md%20and%20help%20me%20with%20this%20FunnelFizz%20topic%3A%20Connect%20providers "Open this page in Claude with context")[Open in ChatGPT](https://chat.openai.com/?q=Read%20https%3A%2F%2Fdocs.funnelfizz.com%2Fquickstart%2Fconnect-providers.md%20and%20help%20me%20with%20this%20FunnelFizz%20topic%3A%20Connect%20providers "Open this page in ChatGPT with context") # Connect providers **Providers** are external accounts FunnelFizz reads data from — social channels, search consoles, ad platforms. They power the **AWARENESS** stage. Without them, your impressions count is blank and you have to fill in numbers manually. This is optional. You can absolutely skip it and come back later, especially if you're not posting on social or running ads yet. ## What you can connect[​](#what-you-can-connect "Direct link to What you can connect") | Provider | What it tracks | Plan | | ------------------------- | --------------------------------------------------- | ----- | | **X (Twitter)** | Post impressions, likes, reposts, replies | FREE | | **YouTube** | Video views, subscribers, watch time | FREE | | **LinkedIn** | Post impressions, engagements, follower growth | FREE | | **Instagram** | Reach, impressions, engagement | FREE | | **TikTok** | Views, likes, shares, comments | FREE | | **Reddit** | Post views, upvotes, comments | HOBBY | | **Google Search Console** | Impressions, clicks, CTR, avg position, top queries | HOBBY | | **Google Ads** | Impressions, clicks, CTR, spend | HOBBY | All of them are read-only. FunnelFizz never posts, comments, or advertises on your behalf. ## How connecting works[​](#how-connecting-works "Direct link to How connecting works") It's the same OAuth dance you've done a hundred times: 1. Go to your funnel's **AWARENESS stage → Connect provider**, or **Settings → Integrations → \[Provider name]**. 2. Click **Connect \[Provider]**. 3. Provider's OAuth page opens. Sign in if needed. Review permissions (all read-only). Click **Allow**. 4. You get redirected back to FunnelFizz with a green checkmark. Done. Behind the scenes FunnelFizz: * Generates a one-time CSRF nonce for the `state` parameter. * Exchanges the OAuth code for access + refresh tokens. * Encrypts those tokens at rest (AES-256). * Schedules a background sync to pull your recent metrics. ## Workspace-wide, reusable across funnels[​](#workspace-wide-reusable-across-funnels "Direct link to Workspace-wide, reusable across funnels") A connected provider belongs to your **workspace**, not to a specific funnel. If you connect your YouTube account once, every funnel in the workspace can attribute YouTube impressions without re-authenticating. If you already have a provider connected and want to use it in a new funnel, you'll see a **"Reuse existing"** option on the connect screen — no re-OAuth required. ## Stage-scoped attribution[​](#stage-scoped-attribution "Direct link to Stage-scoped attribution") You choose *where* a provider feeds into your funnel. Most people wire social providers into **AWARENESS**, but you can also wire them into a specific split branch — e.g., "track YouTube impressions that led to TRIAL signups separately from generic social impressions." Each **FunnelStage** keeps a `connections[]` array of provider-integration pairs. Adding a provider to a stage takes two clicks. ## Rate limits & refresh[​](#rate-limits--refresh "Direct link to Rate limits & refresh") * **Social providers** refresh every \~30 minutes. * **Google Search Console / Google Ads** refresh every \~60 minutes (GSC has a 2-day data lag on their end, so numbers for "today" are always roughly approximate). * **Stripe** refreshes every 15 minutes (see [Connect Stripe](https://docs.funnelfizz.com/quickstart/connect-stripe.md)). If you need fresh numbers for a demo, click **Sync now** on any provider. There's no charge. ## Disconnecting or re-authenticating[​](#disconnecting-or-re-authenticating "Direct link to Disconnecting or re-authenticating") **Settings → Integrations → \[Provider] → Disconnect** revokes FunnelFizz's access and clears the stored tokens. The *historical* data stays in your funnel (we keep aggregated counts, not raw API responses), but new data stops flowing. If a provider's token expires (some providers rotate every 90 days) and auto-refresh fails, you'll see a red "Reconnect" banner on the integration. Click it, re-auth, done. ## What about providers that aren't listed?[​](#what-about-providers-that-arent-listed "Direct link to What about providers that aren't listed?") If you use a platform that isn't in the list above — Threads, Bluesky, Substack, ProductHunt, HackerNews — you have two options today: 1. **Manual entries.** Every stage has a "Manual" input field where you can paste daily impression numbers. Clunky but works. 2. **Mentions monitoring.** On PRO, [Mentions](https://docs.funnelfizz.com/features/mentions.md) scans 13+ sources (ProductHunt, HN, DEV.to, GoogleNews, StackExchange, Brave Search, GitHub…) for brand mentions. Not quite the same as a proper OAuth integration but covers a lot of ground. If there's a specific provider you need, email **** — we prioritize by request volume. *** **You're done!** Go back to the canvas and watch your funnel populate. Or keep reading — [Concepts → Stages](https://docs.funnelfizz.com/concepts/stages.md) explains what's actually happening under the hood. --- Copy as markdown[View .md](https://docs.funnelfizz.com/quickstart/connect-stripe "View the raw markdown for this page")[Open in Claude](https://claude.ai/new?q=Read%20https%3A%2F%2Fdocs.funnelfizz.com%2Fquickstart%2Fconnect-stripe.md%20and%20help%20me%20with%20this%20FunnelFizz%20topic%3A%20Connect%20Stripe "Open this page in Claude with context")[Open in ChatGPT](https://chat.openai.com/?q=Read%20https%3A%2F%2Fdocs.funnelfizz.com%2Fquickstart%2Fconnect-stripe.md%20and%20help%20me%20with%20this%20FunnelFizz%20topic%3A%20Connect%20Stripe "Open this page in ChatGPT with context") # Connect Stripe Stripe is how FunnelFizz tells the difference between "someone visited your site" and "someone is paying you." It's technically optional — you can track up through CONSIDERATION with just the tracking snippet — but without Stripe, your TRIAL and CUSTOMER stages will sit at zero. There are two ways to connect. Both produce the same thing: a read-only Stripe API key stored encrypted in FunnelFizz. ## Option A: Paste a restricted API key (recommended today)[​](#option-a-paste-a-restricted-api-key-recommended-today "Direct link to Option A: Paste a restricted API key (recommended today)") This takes about 60 seconds. ### 1. Create the key in Stripe[​](#1-create-the-key-in-stripe "Direct link to 1. Create the key in Stripe") In your Stripe dashboard: 1. Go to **Developers → API keys** (or [dashboard.stripe.com/apikeys](https://dashboard.stripe.com/apikeys)). 2. Click **Create restricted key**. 3. Name it `FunnelFizz (read-only)`. 4. Set these **seven** permissions to **Read**: * Customers * Charges and Refunds * Events * Products and Prices * Invoices * Subscriptions * Balance 5. Leave everything else as **None**. 6. Click **Create key**, then **Reveal key**. 7. Copy the key — it starts with `rk_live_...` (or `rk_test_...` if you're on a test account). :::warning Exactly these seven FunnelFizz validates every permission in parallel when you paste the key. If you miss one, you'll get a specific error like *"missing read access for: Subscriptions, Invoices."* Just add those scopes in Stripe and click "Update key." ::: ### 2. Paste it into FunnelFizz[​](#2-paste-it-into-funnelfizz "Direct link to 2. Paste it into FunnelFizz") In the onboarding wizard (or at any time from **Settings → Integrations**): 1. Click **Connect Stripe**. 2. Paste the `rk_live_...` key. 3. Click **Validate**. FunnelFizz pings each scope (`customers.list`, `subscriptions.list`, etc.), fetches your account name, and stores the key encrypted. You'll see a green checkmark with your Stripe account name. ### 3. Map your products[​](#3-map-your-products "Direct link to 3. Map your products") Once connected, FunnelFizz auto-detects your Stripe products and offers to wire them into your funnel's conversion points: * **Trial conversion** (CONSIDERATION → TRIAL) maps to Stripe subscriptions with `status="trialing"`. * **Paid conversion** (TRIAL → CUSTOMER) maps to Stripe subscriptions with `status="active"`. If you have multiple products, pick the one that represents your primary paid tier. You can always add more later — each product can define its own conversion. ## Option B: Install the Stripe App (one-click)[​](#option-b-install-the-stripe-app-one-click "Direct link to Option B: Install the Stripe App (one-click)") The **FunnelFizz Stripe App** is on the [Stripe Apps Marketplace](https://marketplace.stripe.com/). Under the hood it creates the same restricted key as Option A, but the workflow is simpler: 1. Search "FunnelFizz" in the Stripe Apps Marketplace. 2. Click **Install**. Stripe shows a consent screen with the seven read permissions already checked. 3. Click **Generate** — Stripe creates the key automatically. 4. The app's settings view appears with the new `rk_live_...` key. 5. Click the **Open FunnelFizz** deep link — the key is auto-forwarded and connected. The end state is identical to Option A. Pick whichever feels easier. ## What FunnelFizz does with your Stripe data[​](#what-funnelfizz-does-with-your-stripe-data "Direct link to What FunnelFizz does with your Stripe data") Once connected, FunnelFizz: * **Syncs every 15 minutes** — pulls customers, active subscriptions, trials, recent charges. * **Listens to webhooks** — Stripe pushes `checkout.session.completed`, `customer.subscription.updated`, `customer.subscription.deleted`, and `invoice.payment_failed` events so your funnel updates in near-real-time. * **Computes stage entry** — when a tracked visitor becomes a Stripe customer, a **journey event** is written linking their anonymous sessions to the paying customer record. * **Calculates churn** — using a trichotomy: *Retained* (still paying at your anchor tier), *Upgraded* (paying at a higher tier), or *Churned* (no active subscription anywhere). ## One Stripe account per workspace[​](#one-stripe-account-per-workspace "Direct link to One Stripe account per workspace") FunnelFizz enforces a 1:1 relationship between Stripe accounts and workspaces. If you try to connect a Stripe account that's already in another workspace, you'll get an error. This is deliberate — it prevents accidental double-attribution across multiple tenants. If you need to move a Stripe account between workspaces, disconnect it from the first workspace first, then reconnect in the new one. ## Using test mode[​](#using-test-mode "Direct link to Using test mode") For development, generate an `rk_test_...` key with the same seven scopes. FunnelFizz handles both live and test keys. Test-mode Stripe events flow to your funnel just like live ones, so you can dry-run your setup without real charges. Just be aware that the **Livemode** flag is visible in your dashboard and your team will see which mode is connected. ## Troubleshooting[​](#troubleshooting "Direct link to Troubleshooting") * **"Missing scopes" error.** Go back to Stripe → API keys → click your key → **Edit** → add the missing scopes → **Update key** → paste the key into FunnelFizz again. * **Key says "test" but my subs are live (or vice versa).** You generated the key in the wrong Stripe mode. Toggle to the right mode in Stripe and regenerate. * **No customers showing up.** Wait 15 minutes for the first sync, or click **Sync now** in **Settings → Integrations → Stripe**. * **Stripe webhook not firing.** FunnelFizz auto-configures webhooks when you connect via the Stripe App. For API-key mode, webhooks are optional — polling syncs every 15 minutes covers most use cases. If you need real-time, add a webhook endpoint in Stripe pointing to `https://funnelfizz.com/api/webhooks/stripe` and copy the signing secret into your FunnelFizz Stripe integration settings. *** **Next:** [Connect providers →](https://docs.funnelfizz.com/quickstart/connect-providers.md) --- Copy as markdown[View .md](https://docs.funnelfizz.com/quickstart/install-tracking "View the raw markdown for this page")[Open in Claude](https://claude.ai/new?q=Read%20https%3A%2F%2Fdocs.funnelfizz.com%2Fquickstart%2Finstall-tracking.md%20and%20help%20me%20with%20this%20FunnelFizz%20topic%3A%20Install%20tracking "Open this page in Claude with context")[Open in ChatGPT](https://chat.openai.com/?q=Read%20https%3A%2F%2Fdocs.funnelfizz.com%2Fquickstart%2Finstall-tracking.md%20and%20help%20me%20with%20this%20FunnelFizz%20topic%3A%20Install%20tracking "Open this page in ChatGPT with context") # Install tracking The tracking snippet is a small JavaScript file that reports pageviews, custom events, form submissions, scroll depth, and time-on-page back to FunnelFizz. Everything downstream — your CONSIDERATION stage counts, split-by-UTM, email identity — depends on it being installed. ## 1. Grab the snippet[​](#1-grab-the-snippet "Direct link to 1. Grab the snippet") When you hit step 2 of onboarding, the wizard shows you a code block that looks like this: ``` ``` `YOUR_TOKEN` is unique to your site. Don't share it publicly — it identifies where events get attributed. ## 2. Paste it into ``[​](#2-paste-it-into-head "Direct link to 2-paste-it-into-head") Paste the two tags as high as possible inside your `` — ideally before any other scripts so page-load events aren't missed. ### Framework-specific install[​](#framework-specific-install "Direct link to Framework-specific install") Next.js (App Router) In `app/layout.tsx`, add the snippet inside `` using `next/script`: ``` import Script from 'next/script'; export default function RootLayout({ children }) { return ( {children} ); } ``` Next.js (Pages Router) In `pages/_document.tsx`: ``` import { Html, Head, Main, NextScript } from 'next/document'; export default function Document() { return ( ``` WordPress Install any "Insert Headers and Footers" plugin, then paste the snippet into the **Scripts in Header** box. Or add it directly to your theme's `header.php` inside ``. Webflow / Framer / Squarespace Site-wide **Custom Code → Head** area. Paste both the `` and `