</>Developer docs

Build Mentoor into your product.

Install the SDK, mark the controls that matter, and decide when Mentoor should guide or act. Every example below is copy-paste ready.

  • SDK installscript tag or npm
  • Product maproutes + stable anchors
  • Triggersauto-fire guides
  • Privacylearn by default, lock down surfaces
Quickstart

Get Mentoor running in one pass.

Create a project in Studio, allow the URL where Mentoor will run, then install the browser SDK. The defaults point at mentoor.ai — you only need to set apiBase if your team self-hosts a private Mentoor instance.

  1. 1
    Create a project in Studio

    Studio gives you the project ID, public browser key, API host, and current privacy mode.

  2. 2
    Allow your product URL

    Production SDK calls should come from known origins. Localhost is only for development.

  3. 3
    Load Mentoor once in the app shell

    Use the script tag for any website, or npm when the SDK belongs in your bundle.

ExampleScript tag - no npm required
<script src="https://mentoor.ai/sdk.js"></script>
<script>
  window.Mentoor.init({
    projectId: 'your-project-id',
    publicKey: 'pk_live_public_browser_key',
    getRouteContext: () => ({
      route: window.location.pathname + window.location.hash,
      title: document.title,
    }),
  })
</script>
ExampleNPM package
npm install @mentoor/inline-guide

import { Mentoor } from '@mentoor/inline-guide'

Mentoor.init({
  projectId: 'your-project-id',
  publicKey: 'pk_live_public_browser_key',
  user: currentUser ? { id: currentUser.id } : undefined,
  traits: { plan: 'trial' },
  getRouteContext: () => ({
    route: window.location.pathname + window.location.hash,
    title: document.title,
  }),
})
Use the values from Studio.

The public key is safe for the browser. Guide edits, publishing, provider keys, and private project settings stay behind authenticated Studio.

Client setup

Initialize Mentoor once, where your app starts.

Mentoor runs in the browser because it points at live UI. Initialize it once when your app loads, identify the user when you can, and clean it up when your app unmounts.

ExampleReact
import { useEffect } from 'react'
import { Mentoor, type MentoorAssistant } from '@mentoor/inline-guide'

export function MentoorProvider({ userId }: { userId?: string }) {
  useEffect(() => {
    const mentoor: MentoorAssistant = Mentoor.init({
      projectId: 'your-project-id',
      publicKey: 'pk_live_public_browser_key',
      user: userId ? { id: userId } : undefined,
      getRouteContext: () => ({
        route: window.location.pathname + window.location.hash,
      }),
    })

    return () => mentoor.destroy()
  }, [userId])

  return null
}
ExampleNext.js client component
'use client'

import { usePathname } from 'next/navigation'
import { useEffect } from 'react'
import { Mentoor } from '@mentoor/inline-guide'

export function MentoorClient({ userId }: { userId?: string }) {
  const pathname = usePathname()

  useEffect(() => {
    const mentoor = Mentoor.init({
      projectId: 'your-project-id',
      publicKey: 'pk_live_public_browser_key',
      user: userId ? { id: userId } : undefined,
      getRouteContext: () => ({
        route: window.location.pathname + window.location.hash,
      }),
    })

    return () => mentoor.destroy()
  }, [pathname, userId])

  return null
}
ExampleVue
<script setup lang="ts">
import { onBeforeUnmount, onMounted } from 'vue'
import { Mentoor, type MentoorAssistant } from '@mentoor/inline-guide'

let mentoor: MentoorAssistant | null = null

onMounted(() => {
  mentoor = Mentoor.init({
    projectId: 'your-project-id',
    publicKey: 'pk_live_public_browser_key',
    getRouteContext: () => ({
      route: window.location.pathname + window.location.hash,
    }),
  })
})

onBeforeUnmount(() => mentoor?.destroy())
</script>
Route context

Tell Mentoor what screen it is looking at.

Mentoor learns routes and page families. Include hashes when your app routes with hashes, and include the real URL when support or debugging needs it.

Route context is how Mentoor avoids giving a user instructions for the wrong part of a large app. If your product has dynamic IDs like /checkout/cs_test_a1b2 or/listings/12345, Mentoor can group those into a route family and still use the visible labels on the current screen.

ExampleRoute context
getRouteContext: () => ({
  route: window.location.pathname + window.location.hash,
  title: document.title,
  url: window.location.href,
})
Targets

Add stable anchors to important controls.

Mentoor can learn from labels, roles, and layout. Stable anchors make the important paths resilient when copy changes, rows reorder, or the same button appears in many places.

Use anchors like product semantics, not CSS selectors.

Good: checkout-pay, invite-teammate, billing-upgrade. Avoid: generated class names, database IDs, or row-specific values.

ExampleRecommended anchors
<button
  data-mentoor="checkout-pay"
  data-mentoor-label="Pay now"
>
  Pay $49
</button>

<input
  data-mentoor="checkout-coupon"
  aria-label="Coupon code"
/>
Guides and chat

Choose how users start help.

Mentoor can stay quiet until your app triggers a guide, or show a small chat launcher so users can ask where something is. Both paths use the same live product targets.

Triggered guideStart from onboarding, release notes, empty states, or a help link.
ChatLet users ask a question, then Mentoor opens the right guide in the UI.
Suggested guideMentoor drafts guides from usage, but Studio review controls what goes live.
ExampleControls
await mentoor.ask('Where do I invite a teammate?')
await mentoor.startGuide('invite teammate')
await mentoor.identify({
  id: 'your-user-id',
  traits: { plan: 'growth', teamSize: 12 },
})
await mentoor.setTraits({ onboarding: 'started' })
mentoor.setConsent({
  storage: true,
  productLearning: true,
  visitorMemory: true,
  guideMemory: true,
  personalization: true,
})
mentoor.destroy()
ExampleOptional custom launcher
const mentoor = Mentoor.init({
  projectId: 'your-project-id',
  publicKey: 'pk_live_public_browser_key',
  chatEnabled: false,
})

document.querySelector('[data-help]')?.addEventListener('click', () => {
  mentoor.open()
})
Agent actions

Let Mentoor do safe UI steps.

Agent mode is opt-in. Mentoor uses the same planner and learned targets, verifies the live UI before acting, and stops before sensitive controls unless your app allows them.

Agent mode should feel like a careful user.

It can click safe controls, type explicit values, pair nearby fields with submit buttons, and wait for the page before continuing.

ExampleExperimental agent mode
const mentoor = Mentoor.init({
  projectId: 'your-project-id',
  publicKey: 'pk_live_public_browser_key',
  agent: {
    enabled: true,
    allowClicks: true,
    allowTyping: true,
  },
})

await mentoor.act('Apply coupon WELCOME10')

// Sensitive actions are blocked by default. Explicitly allow the agent
// to touch one with data-mentoor-agent="allow".
<button
  data-mentoor="send-invoice"
  data-mentoor-label="Send invoice"
  data-mentoor-agent="allow"
>
  Send invoice
</button>
Privacy

Let Mentoor learn. Lock down what shouldn't be touched.

Mentoor activates more users the more it sees. The default install lets it watch, learn, and improve from real product use — exactly what the home page promises. Locking signals down is opt-in, not the recommendation.

Default: Balanced mode

Balanced mode is what you get out of the box. Mentoor observes safe UI signals — labels, roles, anchors, guide outcomes — drafts guides where users stumble, and improves future suggestions. No screenshots, no form values, no database.

This is the install we recommend for almost every team. The whole point of Mentoor is that it gets better the more it sees; gating that on the way in slows activation instead of lifting it.

ExampleDefault install — Mentoor learns and improves
// Default: Mentoor learns from the live UI and improves guide suggestions.
const mentoor = Mentoor.init({
  projectId: 'your-project-id',
  publicKey: 'pk_live_public_browser_key',
  // privacy defaults to 'balanced' — product learning ON, guide memory ON.
})

// Lock specific signals down if your product has sensitive surfaces.
mentoor.setConsent({
  productLearning: true,   // watch where users get stuck (default: true)
  guideMemory: true,       // remember what worked per user (default: true)
  visitorMemory: true,     // identify returning visitors (default: true)
  personalization: true,   // tailor guides by trait (default: true)
  storage: true,           // cache locally for speed (default: true)
})

What Mentoor uses by default

Route, page title, visible labels, element roles, stable data-mentooranchors, and guide events (started, completed, dismissed). It also remembers which guides a visitor has already seen so it doesn't show them twice.

What Mentoor never touches

Raw screenshots, typed field values, passwords, database access, your app's destructive actions. Your app still owns authentication, permissions, forms, and anything that can change real state without explicit human action.

When to dial it down

Sensitive admin, billing, or PHI screens: mark private regions with data-mentoor-ignore. Mentoor skips them entirely — no observation, no guides, no agent actions.

Regulated industries (healthcare, finance): if compliance demands it, switch to Essential mode (privacy: 'essential') — Mentoor runs reviewed guides only and turns off passive learning. You give up the learning loop but keep the in-product guidance.

Most SaaS products: leave the defaults. Mentoor pays for itself in activation lift only when it can see what users actually do.

Mentoor gives you the controls; your team writes the privacy policy and decides whether consent is required for your jurisdiction.

Fixes

Common install checks.

SDK not seenCheck the project ID, public key, environment, and that the page URL is on your allowed origins list in Studio.
Chat does not showChat can be disabled per project. Guides and triggered help can still run.
Guide misses a targetAdd a stable `data-mentoor` anchor and repair or republish the guide from Studio.
SPA route changedReturn the route from `getRouteContext`, including hashes when your app routes there.