Skip to content
prod e051e98
Browse

3 · Support (9C)

Objective — give customers a way to get help by standing up two customer-facing systems — a live chat widget and a help center / knowledge base — picking one tool per row and recording the choice.

Two customer-facing systems close out Phase 9, each with several alternatives in the reference set:

  • Live chat widget — multiple alternatives (hosted vs self-hosted, free vs paid). Choose by budget and data-residency needs.
  • Help center / knowledge base — several alternatives. A searchable KB deflects repetitive support tickets before they’re filed.

Check whether a chat widget is already installed, then pick a provider that fits your budget and needs.

  1. Check for an existing chat widget. Open the live site → DevTools (F12) → Console and run:

    console.log('Crisp:', typeof $crisp);
    console.log('Tawk:', typeof Tawk_API);
    console.log('ThriveDesk:', typeof ThriveDesk);

    Also grep your layout files:

    Terminal window
    grep -rE "crisp|intercom|tawk|thrivedesk" resources/views/ --include="*.blade.php"
    • ✅ You know whether a chat widget is already present.
  2. Pick a provider using the decision table.

    First, compare the providers on features and business capabilities, then use the recommendation table below.

    Support features:

    ProviderPriceAI chatbotKnowledge baseMobile app
    CrispFree / $25+Pro onlyPro onlyYes
    Intercom$74+/moYesYesYes
    Tawk.toFreeBasicBasicYes
    ThriveDeskLTD / $25+LimitedYesYes
    Tiny TalkLTDYes (AI-first)NoNo
    LeadsterLTDLimitedNoNo

    Business features:

    | Provider | CRM | Analytics | White-label | API | |---|---|---|---| | Crisp | Yes | Yes | Pro | Yes | | Intercom | Yes | Yes | Yes | Yes | | Tawk.to | Limited | Yes | Paid | Limited | | ThriveDesk | Yes | Yes | Yes | Yes | | Tiny Talk | Limited | Yes | Yes | Yes | | Leadster | Yes | Yes | No | Limited |

    Your situationRecommended provider
    Zero budgetTawk.to (free)
    Starting out, want featuresCrisp (free tier)
    Have a lifetime deal budgetThriveDesk
    Want an AI-first chatbotTiny Talk
    Lead-capture focusLeadster
    Enterprise, larger budgetIntercom
    • ✅ A provider is chosen and noted.
  3. Record the decision in _CUSTOMIZATIONS.md (which you chose and why).

    • ✅ Chat provider decision is documented.

Pick the help-center / knowledge-base tool that fits your team’s effort and budget.

  1. Compare the options and pick one.

    OptionEffortCostBest for
    GitBookMediumFree (public) / $8/moDeveloper docs, Git sync
    NotionLowFreeQuickest setup
    ThriveDesk KBLowIncluded if using ThriveDesk chatChat widget auto-suggests articles
    GuidejarMediumLTD availableVisual step-by-step guides
    • ✅ A help-center tool is chosen and noted.
  2. Record the decision in _CUSTOMIZATIONS.md.

    • ✅ Help-center decision is documented.

Once you’ve picked a tool, the setup is short — the detail that blocks people is the CNAME target for the docs/help subdomain.

  1. Point the docs subdomain at your tool’s CNAME target, then verify it resolves.

    ToolSubdomainCNAME targetNotes
    GitBookdocs.<DOMAIN>hosting.gitbook.ioSpace → Settings → Domains; Cloudflare DNS only (gray cloud).
    Notion via Super.sodocs.<DOMAIN>cname.super.soPublish the Notion page first, then connect in Super.
    ThriveDesk KBhelp.<DOMAIN>kb.thrivedesk.comBest if ThriveDesk is also the chat tool.
    Terminal window
    dig docs.<DOMAIN> CNAME +short
    curl -I https://docs.<DOMAIN>
    # Expected: CNAME target resolves and TLS returns 200
    • ✅ The docs/help subdomain resolves to the tool target and loads over HTTPS.
  2. Add the help link to the app — navbar and footer at minimum.

    <a href="https://docs.<DOMAIN>" target="_blank" rel="noopener" class="nav-link">Docs</a>
    • ✅ The help center is reachable from the app’s primary navigation.

Pick one tool per row, embed it, and record the decision so it’s traceable later.

  1. Pick a live chat widget by budget and data-residency needs, then embed it.

    • ✅ A chat widget is wired and reachable on the live site.
  2. Pick a help center / knowledge base and seed it with starter articles.

    • ✅ A searchable KB is live and deflects common questions.
  3. Record the choices. Note which tool you picked (and why) in _CUSTOMIZATIONS.md.

    • ✅ Both choices are documented for later traceability.

If none of the hosted tools fit, the structure and first articles matter more than the platform.

  1. Create the category structure: Getting Started · Features · Billing & Payments · Troubleshooting · API (if applicable).

  2. Write the 5 priority starter articles.

    #ArticleDeflects
    1Getting Started”How do I begin?“
    2Account SettingsProfile/preferences questions
    3Billing FAQPlan/payment/refund questions
    4Password ResetThe single most common ticket
    5Contact Support”How do I reach a human?”
  3. Use one article template for consistency:

    ## Overview
    One or two sentences: what this article covers and who it's for.
    ## Steps
    1. First action with exact UI label/path.
    2. Next action.
    ## Common Issues
    - Symptom → cause → fix.
    ## Related Articles
    - Link to the next logical article.
  4. Link from the app and test search.

    • ✅ The KB has starter articles, is reachable from the app, and search finds each article.

Each provider follows the same shape — one .env key, one config/services.php entry, one consent-gated Blade block before </body>, plus an optional authed identify block. All three ship user identity to a third party, so the embed must sit behind your Phase-6 consent gate (same condition the engagement page uses for ProductLift). Never commit a literal widget/website/app ID inline — read it from config.

  1. Crisp — free tier, good default when starting out.

    .env:

    Terminal window
    CRISP_WEBSITE_ID=

    config/services.php:

    'crisp' => [
    'website_id' => env('CRISP_WEBSITE_ID'),
    ],

    Before </body> in the main layout:

    {{-- Crisp — loads only after functional/analytics consent (Phase 6 §6 gate). --}}
    @if (config('services.crisp.website_id') && (function_exists('consent') ? consent('analytics') : request()->cookie('consent_analytics') === 'yes'))
    <script type="text/javascript">
    window.$crisp = [];
    window.CRISP_WEBSITE_ID = @json(config('services.crisp.website_id'));
    (function () {
    var d = document, s = d.createElement("script");
    s.src = "https://client.crisp.chat/l.js";
    s.async = 1;
    d.getElementsByTagName("head")[0].appendChild(s);
    })();
    </script>
    @auth
    <script>
    $crisp.push(["set", "user:email", [@json(Auth::user()->email ?? '')]]);
    $crisp.push(["set", "user:nickname", [@json(Auth::user()->name ?? '')]]);
    $crisp.push(["set", "session:data", [[
    ["user_id", @json((string) Auth::id())],
    ["plan", @json(Auth::user()->plan ?? 'free')]
    ]]]);
    </script>
    @endauth
    @endif
    • CRISP_WEBSITE_ID set; widget loads only after consent; an authed user’s email/name reach the Crisp dashboard.
  2. Tawk.to — fully free, the zero-budget pick.

    .env:

    Terminal window
    TAWK_PROPERTY_ID=
    TAWK_WIDGET_ID=

    config/services.php:

    'tawk' => [
    'property_id' => env('TAWK_PROPERTY_ID'),
    'widget_id' => env('TAWK_WIDGET_ID'),
    ],

    Before </body> in the main layout:

    {{-- Tawk.to — consent-gated (Phase 6 §6). --}}
    @if (config('services.tawk.property_id') && (function_exists('consent') ? consent('analytics') : request()->cookie('consent_analytics') === 'yes'))
    <script type="text/javascript">
    var Tawk_API = Tawk_API || {}, Tawk_LoadStart = new Date();
    @auth
    Tawk_API.onLoad = function () {
    Tawk_API.setAttributes({
    'name': @json(Auth::user()->name ?? ''),
    'email': @json(Auth::user()->email ?? ''),
    'userId': @json((string) Auth::id()),
    'plan': @json(Auth::user()->plan ?? 'free')
    }, function (error) {});
    };
    @endauth
    (function () {
    var s1 = document.createElement("script"),
    s0 = document.getElementsByTagName("script")[0];
    s1.async = true;
    s1.src = 'https://embed.tawk.to/'
    + @json(config('services.tawk.property_id')) + '/'
    + @json(config('services.tawk.widget_id'));
    s1.charset = 'UTF-8';
    s1.setAttribute('crossorigin', '*');
    s0.parentNode.insertBefore(s1, s0);
    })();
    </script>
    @endif
    • ✅ Both Tawk IDs set; widget loads only after consent; authed identity flows to the Tawk dashboard.
  3. Intercom — enterprise / larger budget.

    .env:

    Terminal window
    INTERCOM_APP_ID=

    config/services.php:

    'intercom' => [
    'app_id' => env('INTERCOM_APP_ID'),
    ],

    Before </body> in the main layout:

    {{-- Intercom — consent-gated (Phase 6 §6). --}}
    @if (config('services.intercom.app_id') && (function_exists('consent') ? consent('analytics') : request()->cookie('consent_analytics') === 'yes'))
    <script>
    window.intercomSettings = {
    api_base: "https://api-iam.intercom.io",
    app_id: @json(config('services.intercom.app_id')),
    @auth
    user_id: @json((string) Auth::id()),
    name: @json(Auth::user()->name ?? ''),
    email: @json(Auth::user()->email ?? ''),
    created_at: {{ optional(Auth::user()->created_at)->timestamp ?? 'null' }},
    plan: @json(Auth::user()->plan ?? 'free'),
    @endauth
    };
    </script>
    <script>
    (function () { var w = window; var ic = w.Intercom; if (typeof ic === "function") { ic('reattach_activator'); ic('update', w.intercomSettings); } else { var d = document; var i = function () { i.c(arguments); }; i.q = []; i.c = function (args) { i.q.push(args); }; w.Intercom = i; var l = function () { var s = d.createElement('script'); s.type = 'text/javascript'; s.async = true; s.src = 'https://widget.intercom.io/widget/' + @json(config('services.intercom.app_id')); var x = d.getElementsByTagName('script')[0]; x.parentNode.insertBefore(s, x); }; if (document.readyState === 'complete') { l(); } else if (w.attachEvent) { w.attachEvent('onload', l); } else { w.addEventListener('load', l, false); } } })();
    </script>
    @endif
    • INTERCOM_APP_ID set; widget loads only after consent; authed user identity (including created_at) reaches Intercom.

Run the Phase 9 gate — the app is “polished” only when discoverability, engagement, and support all pass.

  1. Walk the phase gate and confirm each line.

    • ✅ Valid sitemap + robots; meta/schema/titles in place.
    • ✅ Search Console verified, sitemap submitted, PageSpeed pass run.
    • ✅ Onboarding + changelog + feedback path exists.
    • ✅ Chat widget + help center chosen and wired.

Do not mark this step done until every box below is checked.

  • 👤 Chat decision — provider assessed and chosen; recorded in _CUSTOMIZATIONS.md.
  • 👤 Help center decision — approach chosen; recorded in _CUSTOMIZATIONS.md.
  • 🤖 SEO foundation — valid sitemap + robots; meta/schema/titles in place.
  • 👤 Search Console — verified, sitemap submitted, PageSpeed pass run.
  • 🔀 Engagement — onboarding + changelog + feedback path exists.
  • 👤 Support — chat widget + help center chosen and wired.