Complete Saathi documentation
Everything from first install to advanced WooCommerce selling, AI personas, memory and multilingual setup — with troubleshooting for every common issue.
System requirements
Before installing, make sure your environment meets the following minimums:
1 · Install & activate
There are two ways to install Saathi — upload and auto-install:
Method A — Upload (recommended)
- Download the plugin ZIPLog in to your Saathi dashboard, go to Downloads and click Download plugin ZIP. You need an active license (Free plan works).
- Upload to WordPressIn your WordPress admin, go to
Plugins → Add New → Upload Plugin, click Choose file, select thesathi-agentic-ai.zipyou downloaded, and click Install Now. - ActivateClick Activate Plugin. A new Saathi AI menu appears in the left sidebar of your WordPress dashboard.
- Run the setup wizardSaathi will prompt you to start the setup wizard. This walks you through AI provider, scan, and appearance in under 5 minutes.
Method B — FTP / cPanel
Unzip sathi-agentic-ai.zip and upload the sathi-agentic-ai/ folder to /wp-content/plugins/ via FTP or cPanel File Manager. Then activate from Plugins → Installed Plugins.
2 · Connect an AI provider
Saathi works with 15 AI providers. You bring your own API key — Saathi never charges per message and doesn't proxy your requests through its own servers.
How to connect:
- Get an API keySign up with your chosen provider (e.g. platform.openai.com for OpenAI, openrouter.ai for OpenRouter). Free tiers are available on most providers.
- Open AI Providers in WordPressGo to
Saathi AI → AI Providersin your WordPress dashboard. Select your provider from the dropdown. - Paste the API keyPaste the key in the field provided. For Ollama (local) or LM Studio, enter the base URL instead (e.g.
http://localhost:11434/v1). - Test the connectionClick Test connection. You'll see a green ✓ if the key works and the model responds.
- SaveClick Save provider. Saathi will now use this key for all chat conversations.
3 · Choose the right model
Different models have different strengths. Here's a quick guide for Saathi use cases:
| Model | Best for | Cost |
|---|---|---|
gpt-4o-mini | Fast everyday chat — great default | Very low |
deepseek-chat | Budget-friendly, high quality | Very low |
llama-3.3-70b (Groq) | Free, ultra-fast inference | Free tier |
claude-3-5-sonnet | Complex personas, long context | Medium |
gemini-1.5-flash | Good multilingual + fast | Low |
ollama/llama3.2 | 100% local, private, no API cost | Free (self-hosted) |
gpt-4o-mini (OpenAI) or deepseek-chat (DeepSeek) for chat. Both give excellent quality at very low cost — often under ₹1 per 100 conversations.4 · Scan your website
The website scan is what makes Saathi answer from your content instead of generic AI replies. It indexes all published pages, posts and WooCommerce products.
- Go to KnowledgeNavigate to
Saathi AI → Knowledgein your WordPress dashboard. - Click "Scan website"Saathi crawls all published posts, pages and products. Progress is shown in real time. A typical site (50–200 pages) takes 30–90 seconds.
- Review indexed contentAfter the scan, you'll see a list of indexed documents with word counts. You can remove individual pages from the knowledge base if needed.
- Re-scan after content changesWhenever you publish new pages, update product descriptions or change important content, run a new scan so the bot stays accurate.
5 · Personas & bot tone Pro Max
Personas let you define who your bot is — name, personality, tone, and scope of knowledge. You can create multiple personas for different pages or contexts.
- Go to PersonasNavigate to
Saathi AI → Personas. - Create a personaGive it a name (e.g. "Riya — Support Bot"), a short description, and write the system prompt. Example: "You are Riya, a friendly support agent for Acme Store. Answer only from the provided knowledge base. If unsure, ask the customer to clarify."
- Set the toneChoose from presets (Friendly, Professional, Formal, Energetic) or write a custom tone instruction in the system prompt.
- Assign a mascotPick one of the 8 mascots to go with this persona — each persona can have its own look.
- AI-built persona Max Click Generate with AI — describe your brand in a sentence and Saathi writes the full system prompt for you based on your scanned content.
6 · Appearance & mascot
Saathi's widget is fully themeable. Customize it to match your brand — visitors can also change the look themselves.
- Open AppearanceGo to
Saathi AI → Appearance. - Choose your brand colorPick from the palette or enter a hex code. The button, message bubbles and accents all update instantly.
- Select a mascot8 friendly mascots are included. Each has unique emotion states that animate during conversation.
- Set the welcome messageWrite the greeting the bot shows when first opened. Example: "Hi there! 👋 I'm Riya. Ask me anything about our products or your order!"
- Add suggested questionsPre-fill 3–5 question chips that appear in the chat — great for guiding visitors. Example: "What are your shipping options?", "Show me bestsellers".
- Preview & saveThe live preview on the right updates in real time. Click Save appearance when done.
7 · Placement & visibility
Control exactly where the Saathi launcher appears and on which pages it's shown or hidden.
- Corner positionChoose bottom-right (default), bottom-left, top-right or top-left. On mobile, an automatic offset keeps it clear of the browser navigation bar.
- Show / hide on specific pagesUnder Visibility rules, select "Show on all pages" or add URL patterns to show/hide the bot only on specific pages. Example: hide on
/checkout, show on/shop. - Auto-open delayOptionally set the bot to open automatically after X seconds on the page — useful for high-intent pages like pricing.
- Mobile settingsToggle mobile on/off separately from desktop. You can also set a smaller launcher size for mobile.
8 · WooCommerce selling Max
Saathi can showcase your WooCommerce products inside the chat — with images, prices, ratings — and let customers Add to Cart or go straight to checkout without leaving the conversation.
- Ensure WooCommerce is activeWooCommerce 6.0+ must be installed and active on the same WordPress site.
- Enable Commerce modeGo to
Saathi AI → Commerceand toggle Enable WooCommerce selling on. - Run a product scanUnder
Saathi AI → Knowledge → WooCommerce, click Sync products. This imports your live catalog — product names, descriptions, prices, images and categories. - Set product recommendationsChoose whether Saathi recommends by category match, price range, or bestsellers first. You can also write a recommendation rule in natural language.
- Test in chatOpen the chat widget and type "show me your bestsellers" or "I need a gift under ₹1000" — product cards appear inside the conversation.
9 · Memory & conversation context Pro Max
Saathi remembers key facts from past sessions — so returning visitors get a personalized experience, not a blank slate.
- On Pro/Max, memory is stored per user (logged-in) or per guest cookie (visitors). It persists across sessions.
- The bot remembers: customer name (if given), product interests, language preference, previous questions and resolved issues.
- Memory is surfaced in the system prompt automatically — you don't need to configure anything beyond enabling the feature.
- To clear a user's memory: go to
Saathi AI → Memoryand delete entries by user or guest ID. - Memory does not share between different visitors — each user/guest has an isolated memory store.
Saathi AI → Memory → Erase user data to permanently remove their memory records.10 · Multilingual setup
Saathi auto-detects the visitor's language from their message and replies in the same language — no plugin configuration needed. It supports 40+ languages including Hindi, Hinglish, Gujarati, Tamil, Bengali, Marathi, and all major European and Asian languages.
How it works:
- The AI model detects language from the first user message.
- Replies are in the same language, keeping your brand name, product names and prices unchanged.
- If a visitor switches language mid-conversation, the bot follows automatically.
- For code-mixed languages (Hinglish, Spanglish etc.), Saathi matches the user's own mix.
gpt-4o-mini or gemini-1.5-flash work very well at low cost.11 · License activation
- Choose a planGo to your pricing page and choose Free, Pro or Max. Free gives you a working chatbot with no time limit.
- Copy your license keyAfter checkout, log in to your Saathi dashboard. Your license key is shown on the License tab and in your confirmation email. It looks like:
SATHI-XXXX-XXXX-XXXX-XXXX. - Activate in WordPressIn your WordPress admin, go to
Saathi AI → License. Paste your key and click Activate. The plugin connects to the license server and confirms activation within a few seconds. - Check activation statusA green ✓ and your plan name confirm successful activation. You'll see plan features unlock immediately.
Saathi AI → License → Deactivate.12 · Updating the plugin
When a new version of Saathi is released, you'll see an update notice in your WordPress admin under Plugins → Installed Plugins. Updates are safe to apply — your settings, personas, knowledge base and conversation history are never affected by an update.
- Back up first (optional but recommended)Use a backup plugin (Updraft Plus, All-in-One WP Migration) before major version updates.
- Apply the updateClick Update now next to Saathi AI in the Plugins list. The plugin downloads and replaces the old files automatically.
- Check settingsAfter updating, visit
Saathi AI → AI Providersand confirm your key is still connected. Run a quick test conversation on the frontend.
13 · Privacy & GDPR
Saathi is built with privacy by default:
- No data sold or shared: Conversation data stays between your server, your visitor's browser, and your AI provider (which has its own privacy policy).
- Guest tracking: Visitors are identified by a secure, httpOnly cookie — never by IP address alone.
- GDPR consent gate: Enable the consent notice under
Saathi AI → Privacy. The chat won't start until the visitor accepts. - Data deletion: Use
Saathi AI → Memory → Erase user datato delete all stored memory for a user. Works with WordPress's built-in Personal Data Eraser tool too. - Conversation logs: Stored in your own database only. You can configure auto-deletion of logs older than N days under
Saathi AI → Privacy → Log retention. - No third-party tracking: The Saathi widget does not load any third-party analytics, ad scripts or fingerprinting code.
14 · Setup wizard tour
The first time you activate Saathi, the plugin steps out of the WordPress chrome and opens a full-screen setup wizard. It walks you through the decisions that matter, in the order they matter, so the bot is answering visitors before you ever touch the main admin pages. You can exit at any time and pick up later from Saathi AI → Setup wizard, or jump straight to wp-admin/admin.php?page=sathi-setup — the wizard renders the full flow even after it has been marked complete, so it doubles as a re-onboarding path for new team members.
Saathi AI → Settings (the sathi_license_enforce option) or by setting define('SATHI_LICENSE_ENFORCE', false); in wp-config.php.- AI providerPick which model brain answers your visitors. The dropdown lists every provider Saathi knows about with a "free tier available" marker next to the ones you can try without a card on file. Paste an API key, optionally override the model name, and the wizard fires a real
POST /sathi/v1/playground/chatagainst your key to confirm it works. An inline playground stays open below so you can send a few test prompts before moving on. Recommended: start withgpt-4o-minion OpenAI orclaude-haiku-4-5on Anthropic for cheap, fast chat. To change later, openSaathi AI → Settingsand scroll to the Providers card — same form, just embedded in the dashboard. - PersonaDecide who the bot is. The wizard offers five presets — Support Agent, Sales Advisor, Technical Expert, Concierge, and Brand Mascot — and an AI-assisted Generate with AI button that turns a one-line description like "friendly support bot for a yoga studio" into a full persona using your configured provider. The choice writes to
sathi_persona_nameandsathi_persona_text, which become the very first lines of the system prompt sent to the LLM, so phrasing matters. Recommended: pick the closest preset, then edit two or three sentences to mention your brand, the visitor types you expect, and one thing the bot must never do. Change it any time fromSaathi AI → Personas, which exposes a richer library than the wizard — both the six built-in mascots (Guru, Ninja, Buddy, Sage, Spark, Guardian, registered assathi_personaposts) and any custom personas you build yourself. - AppearanceMake the widget look like it belongs on your site. You get a mascot picker, a row of accent-colour swatches plus a native colour picker, and an upload slot for a custom avatar (PNG, JPEG, WebP, or SVG, capped at ~600KB). Settings flow into
sathi_widget_avatar,sathi_widget_avatar_custom, andsathi_accent_color. Recommended: match the accent colour to your primary brand colour and upload a 256×256 mascot if you have one — the bot feels far more "yours" with a custom face. On the Free plan only the first mascot (Sathi Guru) is unlocked; the others show a lock badge and open an upgrade prompt. To revisit, head back toSaathi AI → Settingswhere the same controls live alongside placement and behaviour options. - Knowledge scan (Max plan only)This step only renders when your license tier resolves to Max — Pro and Free sites skip straight to Launch. When it runs, Saathi crawls every public post, page, product, and custom post type into the
sathi_knowledge_chunkstable and queues embeddings in the background. The wizard fires a singlePOST /sathi/v1/knowledge/indexto start the crawl, then follows up withGET /sathi/v1/knowledge/statsso you can see indexed source and chunk counts before continuing. Recommended: let the first scan complete here — answers grounded in your own content are dramatically sharper than a bare LLM. To re-run later (or to drive a deep scan in finer slices for a large site), openSaathi AI → Knowledge Baseand use Index Site Now. Auto-reindex on everyPublishis already wired in via thesave_posthook, so day-to-day edits are picked up without you doing anything. - LaunchThe final screen confirms what was saved, flips the
sathi_setup_completeoption to1, and redirects you toSaathi AI → Dashboard. Under the hood the Exit/Finish button hits a nonced URL of the shapeadmin.php?sathi_setup=1&sathi_setup_action=complete&_wpnonce=…(nonce actionsathi_setup_complete) — useful if you ever need to programmatically mark a site as onboarded. From this point the floating widget is live on every front-end page that passes your placement rules. Recommended: open your site in an incognito tab and send the bot one real question before sharing the URL — five minutes of dogfooding catches more issues than a checklist ever will. Need to tour the wizard again later?Saathi AI → Setup wizardrenders the entire flow on demand.
define('SATHI_LICENSE_ENFORCE', false); into wp-config.php and the gate lifts, letting you explore the wizard without a key. Re-enable it before you go live.Saathi AI → Settings and Saathi AI → Personas in that order before turning the widget on.15 · Your first real conversation
The wizard is done and Saathi is waiting in the corner of your site. Before you announce the bot to anyone, spend ten minutes actually talking to it. This walk-through takes you from a cold open to a confident green light.
Step 1 — Open the live widget
Visit your homepage in incognito. The floating launcher should appear in the corner you picked during setup, carrying your accent color, mascot, and the greeting you set on the Settings page. Click it. The panel slides up with your widget title, the greeting bubble, and the input field. This is exactly what your visitors get — look at it as a stranger would.
Step 2 — Ask a real question
Skip "hi" and "test 123". Ask something a real visitor would ask on day one:
- "What does your business actually do?" — tests persona and site description.
- "How much does [your most popular product] cost?" — tests knowledge base retrieval.
- "What are your timings and where are you located?" — tests header/footer indexing.
- "Can you show me the pricing page?" — tests navigation tours.
- "मुझे संपर्क करना है" or your local-language equivalent — tests multilingual mirroring.
Step 3 — Watch the stream
The reply should type itself out one phrase at a time over a second or two. The widget hits /sathi-stream/<uuid>/ and Saathi paints tokens as they arrive. Three things to notice:
- First-token latencyThe gap between send and the first word should sit under two seconds. A longer pause usually means a cold-start on a free-tier model or a host that's buffering output.
- Token cadenceWords should feel like a steady typewriter, not arrive in a single thud at the end. A single thud means streaming was killed — output buffering, a caching plugin, or a CDN holding the response.
- Product cards and actionsIf your question was commerce-shaped, WooCommerce is active, and you've enabled product cards on the Settings page (the
sathi_product_cardstoggle), up to three product cards may appear under the reply. Navigation intents emit a suggestion button that scrolls or routes the visitor on click.
Step 4 — Read the reply like a critic
Resist being impressed it answered at all. Ask three questions:
- Is it grounded? Does it cite real prices, real product names, real contact details — or is it hand-waving in confident, generic language?
- Is it in the right voice and language? The reply should match your chosen persona and mirror the visitor's language and script (Hindi stays Hindi, Hinglish stays Hinglish).
- Are length and follow-ups sane? 2–4 short paragraphs feels right; an essay or a one-liner means your persona style hint needs adjustment. Max Tappable follow-up questions at the bottom should feel like things a visitor would actually tap next.
Step 5 — Retry with a different model
If the answer was weak, blame the model before blaming Saathi. The Playground (available as a step in the Setup wizard, and to admins via POST /sathi/v1/playground/chat) runs against your live persona and safety rules so you can compare providers head-to-head.
- Swap the modelTry
gpt-4o-miniagainstclaude-haiku-4-5, orgemini-2.5-flashagainstclaude-sonnet-4-6. Paste the same question and compare latency and token count. - Save your winnerSave the choice as your chat provider in
Saathi AI → Settingsunder the providers section. The widget picks it up on the next page load.
Saathi AI → Settings.Step 6 — Refresh and re-open
Close the panel, refresh the page, reopen the launcher. Your previous thread should still be there, carried by the sathi_guest cookie. If the history is empty, your browser is blocking cookies or the page is being served from a stale cache — both worth knowing before launch.
Sanity checklist before you launch
Run these six checks. If all six pass, you're ready for real traffic.
- Widget appears in incognitoLauncher visible on every page where you expect it, with the right color and mascot. If it's missing, check the placement controls on the Settings page and your caching plugin's exclusion list.
- Streaming feels liveTokens arrive one phrase at a time, not in a single dump. If they dump, disable output buffering or whitelist
/sathi-stream/on your CDN. - The bot knows your siteIt names your real products, prices in ₹, and your contact details. If it can't, go to
Saathi AI → Knowledge Baseand run a deep scan — the header/footer index is what powers contact answers. - Voice matches your brandThe reply sounds like the persona you wrote. Tighten the persona name and instructions on the Settings page, or pick a different mascot under
Saathi AI → Personas. - Language mirroring worksOne question in your most common visitor language (Hindi, Hinglish, Gujarati, English) comes back in the same language and script.
- Refresh keeps the conversationClose, refresh, reopen — the previous thread is still there.
16 · Abilities — tools the bot can call
Saathi is not just a chat box that hallucinates answers. It runs an agent loop: the AI receives your message plus a list of structured tools (called abilities) it is allowed to call, decides whether one of them would help, calls it with typed JSON arguments, sees the real result, and only then writes a reply. That is how the bot knows your live product stock, your current menu, the post the visitor is reading, or whether someone is logged in — none of that is in the model's training data, it comes from a real PHP call into WordPress.
Manage abilities from Saathi AI → Abilities. You will see a sortable table of every registered tool, grouped by category, with a one-click enable/disable toggle for each row and a bulk toggle for whole categories. Disabling an ability removes it from the agent's toolbox immediately — the AI will not see it on the next turn.
Built-in WordPress abilities Free
| Ability | What it does | Capability |
|---|---|---|
sathi_wp_search_posts | Keyword search across any public post type. Returns title, excerpt, URL, thumbnail, author. | read |
sathi_wp_get_menu | Fetch a navigation menu by location with full parent/child hierarchy. | read |
sathi_wp_get_user_info | Safe profile of the current visitor. Deliberately omits email and capability tokens. | read |
sathi_wp_get_site_info | WP version, theme, language, timezone, active plugin counts. Admin-only by design. | manage_options |
Built-in WooCommerce abilities
These self-register whenever WooCommerce is active on the site — no extra setup, no license tier required for the abilities themselves. On a non-store site they silently skip, so the abilities table stays clean and you do not see errors.
sathi_wc_search_products— keyword search, returns id, name, price, stock, SKU, image, sale state.sathi_wc_get_product— single product detail with full pricing and inventory.sathi_wc_get_cart— current cart contents (returns a friendly error in REST/cron contexts with no session).sathi_wc_get_orders— admins see every order, customers only see their own (scoped inside the callback).sathi_wc_check_stock— fast availability check for a specific product.sathi_wc_get_categories— product taxonomy tree.
CommerceController — is gated by the woocommerce and add_to_cart entitlements on the Max plan. The abilities pack above is separate and works on any plan as long as WooCommerce is installed.Register a custom ability
Any plugin or theme can add its own tool. Use the sathi_chat_tools filter to feed a definition into the agent loop. Each tool needs a name, a description (the AI reads this to decide when to call it), a JSON Schema for arguments, and a PHP callback.
add_filter( 'sathi_chat_tools', function ( $tools, $conversation ) {
$tools[] = [
'name' => 'lookup_order_status',
'description' => 'Look up the current shipping status of a customer order by order number.',
'parameters' => [
'type' => 'object',
'properties' => [
'order_id' => [
'type' => 'integer',
'description' => 'WooCommerce order ID',
],
],
'required' => [ 'order_id' ],
],
'callback' => function ( $args ) {
$order = wc_get_order( (int) $args['order_id'] );
if ( ! $order ) {
return [ 'error' => 'Order not found.' ];
}
return [
'status' => $order->get_status(),
'total' => $order->get_formatted_order_total(),
'tracking' => $order->get_meta( '_tracking_number' ),
];
},
];
return $tools;
}, 10, 2 );
For deeper integration — admin toggles, category grouping, capability gating — register through AbilityRegistry::instance()->register() instead. That route puts your tool in the Saathi AI → Abilities table alongside the built-ins.
Safety model
Every ability call is double-gated before the callback runs. First, WordPress checks current_user_can() against the declared capability — a logged-out visitor cannot trigger an admin-only tool even if the AI tries. Second, the admin disable toggle persisted in sathi_disabled_abilities is honoured: a disabled tool never appears in the agent's tool list, so the model cannot even attempt to call it.
default values but does not enforce types, required, or enum constraints. Re-sanitize arguments inside your callback — treat them as untrusted user input, because they are.AbilityRegistry::instance()->execute_chain(). Failures in one step do not stop the chain; each result comes back tagged with success and either data or error so the AI can decide what to do next.18 · Embedding the widget — shortcode, block & JS
Saathi gives you four ways to put the chat on a page. Most sites only need the first one. The others exist for fine control, layout-specific embeds, and non-WordPress surfaces like landing pages or staging mirrors.
Plans: Free Pro Max — every embed method works on every plan, with mascot and product-card features gated by tier.
a. Floating widget (auto-loaded)
The default. Activate Saathi, finish the setup wizard, and the launcher appears bottom-right on every front-end page. No code, no shortcode, nothing to paste. The widget is injected via wp_footer and respects the placement rules in Saathi AI → Settings.
- Turn it onToggle
Floating widgetinSaathi AI → Settings. It is on by default after the setup wizard. - Pick where it showsSet
Display modeto All pages, Include only, or Exclude, then list the post IDs inDisplay pages. You can also restrict by post type, or limit to logged-in users. - Style the launcherAccent color, position (bottom-right or bottom-left), launcher icon, mascot avatar, auto-open delay, and theme (light/dark/auto) all live under the same settings panel.
- Verify itOpen the front-end in an incognito window. The launcher should appear within a second of
DOMContentLoaded.
🔧 Launcher does not appear at all
- Confirm the license is active in
Saathi AI → Dashboard. The widget short-circuits silently when the license is inactive. - Check
Display mode— if it is set to Include only and the current page ID is not in the list, the floating widget is suppressed. - Make sure your theme calls
wp_footer()before</body>. Custom themes that omit it block all footer-injected scripts. - View source and search for
sathiConfig. If present, the script enqueue worked and the issue is CSS — usually a theme reset that hides#sathi-chat-root.
b. [sathi_chat] shortcode
Use the shortcode when you want the chat inside the page content instead of floating in a corner. Drop it into a Classic block, a Shortcode block, a page builder text widget (Elementor, Divi, Beaver, Bricks), or anywhere shortcodes run.
Minimal usage:
[sathi_chat]
Full example with all four attributes:
[sathi_chat persona="sathi-guru" position="embedded" width="100%" height="640px"]
| Attribute | Values | What it does |
|---|---|---|
persona | slug, e.g. sathi-guru | Override the global persona for this embed only. Useful for a support chat on /help and a sales chat on /pricing. |
position | embedded (default), floating | Inline panel in the page flow, or a floating panel (final layout depends on your theme's stacking rules). |
width | 100%, 480px, any CSS unit | Container width. Defaults to 100% so it fills its parent. |
height | 600px, 80vh, etc. | Container height. 600–720px reads well on desktop; 70–80vh fills the viewport on mobile. |
c. sathi/chat Gutenberg block
For block-theme sites and the modern post editor. Search for Saathi Chat in the block inserter and drop it where you want the chat to sit. The block accepts the same four attributes as the shortcode (persona, position, width, height) and exposes them as sidebar controls so non-technical editors can adjust each embed without touching shortcode syntax.
Inside the editor you get a static preview placeholder so the layout reflows correctly. Real streaming and the live launcher only activate on the published page — the editor does not boot the SSE endpoint.
sathi-chat-block editor script failed to load. Check the browser console for a 404 against /wp-content/plugins/sathi-agentic-ai/assets/ — usually a missing build after a manual file copy, or a hosting cache that has not invalidated yet.d. JS embed for non-WP pages
For a static landing page, a Webflow site, a Shopify store, or any surface that cannot run a WordPress shortcode, drop this snippet right before </body>. It loads the widget bundle from your WordPress install and boots a guest session against your existing Saathi backend.
<!-- Saathi AI — paste before </body> -->
<div id="sathi-chat-root"></div>
<script>
window.sathiConfig = {
restUrl: "https://yoursite.com/wp-json/sathi/v1",
streamUrl: "https://yoursite.com/sathi-stream/",
nonce: "", // leave empty for guest visitors
persona: "sathi-guru",
accentColor: "#7c3aed",
position: "bottom-right",
autoOpen: false,
autoOpenDelay: 5000,
guestId: "" // widget generates one if empty
};
</script>
<script type="module" src="https://yoursite.com/wp-content/plugins/sathi-agentic-ai/assets/chat-widget.js"></script>
| Key | Purpose |
|---|---|
restUrl | Absolute URL to /wp-json/sathi/v1 on the WordPress install that hosts Saathi. The widget calls /chat/send, /products, and /memory here. |
streamUrl | SSE base, ending with a slash. The widget appends the conversation UUID (or new) before POSTing. |
nonce | Leave empty for anonymous guest traffic on a non-WP host. For logged-in WordPress visitors you would pass the value from wp_create_nonce('wp_rest') — but in that case prefer the floating widget, which wires the nonce automatically. |
persona | Slug of a registered persona. Omit to use the site-wide persona. |
accentColor / position | Per-page overrides for the launcher color and corner. Omit either to inherit the site-wide value saved in Saathi settings. |
guestId | Pre-existing guest cookie to continue a session. Leave empty and the widget mints one. |
autoOpen / autoOpenDelay | Whether to pop the panel after N milliseconds. Pair with a low value (3000) on high-intent landers. |
Access-Control-Allow-Origin to home_url() only, so by default a Webflow site cannot stream from a separate WordPress install. Add a small must-use plugin that filters the CORS header for your landing-page domain before going live.Cloudflare and CDN caching
The widget's JS bundle is cache-safe — it is versioned by filemtime and your CDN can hold it forever. The boot config and SSE stream are not.
- Exclude the SSE endpoint from caching. Add a page rule (Cloudflare) or origin rule (Bunny, KeyCDN) for
/sathi-stream/*with cache level Bypass. Buffered SSE breaks token streaming. - Send
X-Accel-Buffering: noif you sit behind nginx. The plugin sets this header on the stream response, but a misconfigured proxy can strip it. - Do not full-page-cache the host page for too long. The boot config carries a WordPress nonce; cached HTML serves stale nonces and the SSE call fails with 403 rest_cookie_invalid_nonce. Either keep TTL short for logged-out visitors or use the guest path (empty
nonce) for cached pages. - Purge after persona or accent-color changes. The boot config is rendered inline. A long page-cache TTL will keep showing the old persona until the cache is busted.
text/event-stream with progressive chunks, not one big blob at the end.19 · Analytics & usage insights
Saathi keeps a running ledger of every AI call it makes — which provider answered, which model, how many tokens went in and out, what it cost, and which conversation it belonged to. There is no separate "Analytics" submenu — the dashboard surface lives at Saathi AI → Dashboard, hydrated from your own database. You get real numbers instead of guesses, and you can use them to tune your content, your persona, and your monthly spend.
Everything is logged into a custom table ({prefix}sathi_usage) by Saathi's usage tracker. No data leaves your WordPress install for this. No third-party trackers, no pixels, no external SaaS dashboards quietly listening in.
What gets tracked:
- Conversations — total chats started, message volume per chat, and which persona handled them (from
{prefix}sathi_conversations). - Tokens — input tokens, output tokens, and a running estimated cost per call, broken down by task type:
chat,embed,image, ormoderation. - Provider mix — which provider answered each call (OpenAI, Anthropic, Google, and any other configured backend) so you can see whether your task routing is actually doing what you set up.
- Visitor messages — every user message is stored on
{prefix}sathi_messageswith roleuser, so you can read what people are really asking. - Feedback signals — thumbs up / thumbs down submitted via
POST /wp-json/sathi/v1/chat/feedback, written into themetadata.feedbackJSON column of the conversation row. - WooCommerce intent — every
sathi_wc_search_productsorsathi_wc_get_producttool call lands in{prefix}sathi_messages.tool_calls, and every successful/wp-json/sathi/v1/cart/addis a clean add-to-cart signal you can correlate back to the conversation that drove it.
Where to view it:
- Open the dashboardHead to
Saathi AI → Dashboardin wp-admin. The Dashboard is a React-mounted page that hydrates from the REST endpoint and displays totals, per-provider breakdowns, and daily aggregates. - Pick a date rangeThe page reads
GET /wp-json/sathi/v1/settings/usage?range=Ndunder the hood (admin-only, gated onmanage_options). Pass7d,30d, or90dto switch windows. - Filter the conversation historyThe same data is reachable through
GET /wp-json/sathi/v1/chat/conversationswith asearchterm, astatusfilter (active,archived,deleted), and pagination — useful when you want to scope down to a campaign, a product launch, or only the conversations with negative feedback. - Inspect raw activityFor per-call detail, open
Saathi AI → Logs. The structured logger writes towp-content/uploads/sathi-debug.log, with the last 200 lines surfaced in the UI. Setsathi_log_leveltoinfoordebugwhen you want richer entries while you investigate something.
How to read the numbers:
| Metric | What it tells you | Watch for |
|---|---|---|
| Total conversations | Demand for the bot. | A flat line after a content change usually means a placement issue, not a copy issue. |
| Avg tokens per call | How much context the model is chewing on. | Climbing token counts on a small site usually mean an over-stuffed persona or very long RAG excerpts. |
| Estimated cost | Live spend, calculated from the built-in pricing table. | If the running total in the sathi_monthly_spend transient nears sathi_cost_cap_monthly, new requests stop until next month. |
| Provider mix | Which backend is doing real work. | Embeds quietly falling back to OpenAI when you intended Gemini means your sathi_embed_provider isn't being honoured. |
| Add-to-cart events | Commerce conversion attributable to chat. | Plenty of product searches but zero add-to-carts is usually a variable-product redirect issue, not a model issue. |
Turning insights into action:
- Scan visitor messages for repeating patternsSkim recent user messages and look for the same question phrased five different ways. Those are your missing FAQ entries — publish a page or post answering each, then run
Saathi AI → Knowledge Base → Index Site Nowso the bot can quote you directly. - Follow the thumbs-down trailOpen the conversations flagged with
rating: 'down'in feedback and read the assistant reply. Most of the time it's one of three failures: missing context, wrong tone, or a hallucinated link. Each maps to a different fix — index more content, adjust the persona, or tightensathi_strict_scope. - Tune the personaIf replies trend too long or too formal, edit your persona text in
Saathi AI → Personas. A few sentences here outperform any model swap. - Right-size the modelIf your usage is mostly short FAQ-style chats, the per-provider breakdown will show plenty of headroom to switch from a flagship model (say
gpt-4o) to a cheaper sibling (gpt-4o-mini) without visitors noticing. - Cap before you scaleSet
sathi_cost_cap_monthlyto a comfortable ceiling (for example ₹2,000 / month). The cap is checked before each provider call, so you can't accidentally run up a bill while you are away.
sathi_monthly_spend). If you flush your object cache mid-month the running total resets to zero and the cap stops triggering until enough new calls rebuild it. On hosts with aggressive cache flushing, lean on the daily aggregates in the usage table for the authoritative number.20 · Content moderation & safety
Saathi runs a two-layer moderation pipeline plus a non-overridable safety block in the system prompt. The ContentModerator screens what the visitor types before it reaches the LLM, then screens what the LLM hands back before it streams to the browser.
On top of that, PromptComposer appends a safety block to every system prompt so the model itself refuses sensitive asks — that's where jailbreak resistance lives, not in the regex layer. Free Pro Max
What ContentModerator actually does. The moderator is a regex engine, not a classifier. It does not run toxicity scoring, jailbreak detection, or PII redaction on its own. It pattern-matches against two lists, blocks on one, warns on the other, and hands the decision to the caller. Deeper toxicity or jailbreak filtering needs either a custom rule set (via the filters below) or a hosted moderation provider wired through the routing slot.
- Visitor sends a messageThe stream endpoint persists the message row to
{prefix}sathi_messagesfirst, then callsContentModerator::moderate_input(). The row exists in the database even if the turn is rejected — useful when you're reviewing a block after the fact. - Input pattern check (hard block)Regex pass for SQL injection (
UPDATE … SET,INSERT INTO … VALUES), shell injection (;,&&, backticks), and XSS payloads. A match stops the turn. - Input pattern check (warn)Profanity, US-shaped Social Security numbers, credit-card-like sequences, and bare email addresses are flagged as warnings on the response struct rather than blocked outright.
- Provider callIf the input passes, the message goes to whichever provider
sathi_chat_providerresolves to via the Factory. - Output pattern check
ContentModerator::moderate_output()scans the assistant reply for external URLs that don't matchhome_url(),wikipedia, orgithub— useful for catching link hallucinations. - Stream or refuseBlocked input triggers an SSE
errorevent and aborts the turn; passing output streams token by token to the widget.
Turn the whole pipeline on or off from Saathi AI → Settings (the sathi_moderation_enabled option). The default regex set is intentionally conservative — check the option value on your install before assuming a state, because sites that handle support tickets discussing SQL or developer chats will hit false positives until they tune the patterns.
UPDATE orders SET status = 'paid' and now my dashboard is broken" will be blocked. If your audience writes SQL in chat, narrow the blocked list before flipping the toggle on.Bringing your own rules. Two filters replace or extend the default regex arrays. Both run on every moderate_input() call, so you can add patterns, remove patterns, or wire in a third-party toxicity scanner without forking the plugin.
sathi_moderation_blocked— array of regex patterns that cause a hard block.sathi_moderation_warnings— array of regex patterns that flag a warning but pass the message through.
Bypassing moderation for trusted users. Both filters receive the live pattern array, so a role-aware bypass is a few lines. Hook sathi_moderation_blocked (and optionally sathi_moderation_warnings) and return an empty array when the current user holds a capability you trust — edit_posts for editors, manage_options for admins, or a custom capability for your CSM team. Anonymous visitors still hit the default rules; your team gets an open channel. The check runs per request, so a logged-out shared device falls back to default moderation automatically.
Provider-side moderation. A routing slot exists at sathi_moderation_provider with a matching Factory::for_task('moderation') resolver, so a developer or add-on can route an extra pass through a hosted moderation model (OpenAI, Mistral, etc.) without touching the regex layer. The core ContentModerator does not invoke that call itself in the default install — treat it as a wiring point for custom code, not a turnkey feature.
Reviewing the log. Open Saathi AI → Logs to see the last 200 lines from wp-content/uploads/sathi-debug.log, newest first. Moderation events are written via the shared Logger at whichever level sathi_log_level is set to — drop it to info or debug during tuning so rejections show up at all. To investigate a specific block, note the log timestamp (UTC), then cross-reference it against the conversation row in {prefix}sathi_conversations by updated_at and the persisted user message in {prefix}sathi_messages.
PromptComposer so even a permissive custom persona inherits them — and this is the layer doing the real jailbreak resistance, not the regex moderator.🔧 Legitimate messages are being blocked
The default blocked list is aggressive on developer and database vocabulary. Inspect Saathi AI → Logs to find the matched pattern, then:
- Filter
sathi_moderation_blockedand remove the offending regex. - Or downgrade it to a warning by moving it into
sathi_moderation_warnings. - Re-test by opening the chat widget in an incognito window and resending the original message.
🔧 The bot is sending links to unrelated sites
Output moderation flags external URLs but doesn't strip them. Tighten the model side instead: enable the Strict scope toggle in Saathi AI → Settings, keep Saathi AI → Knowledge well-indexed so answers are grounded in your own content, and add competitor or off-topic domains to your blocked patterns to escalate them from warning to hard block.
21 · Performance & caching
Saathi is built to sit on small-to-mid WordPress sites without dragging Core Web Vitals down. The widget loads asynchronously, the heavy work happens server-side on cron, and everything that touches the visitor's first paint is deliberately small. This section covers the real numbers, what to tell your caching plugin, and how to keep the database lean as conversations pile up.
Front-end footprint
- The
sathi-chat-widgetbundle ships as an ES module (type="module") at roughly 18 KB gzipped, enqueued inwp_footerso it never blocks the document head. - Launcher icon, accent color, mascot avatar, greeting text, and the REST nonce are passed through a single localized
sathiConfigobject so the bundle does not fan out to additional requests before the user clicks. - Embedded usage via the
[sathi_chat]shortcode or thesathi/chatGutenberg block enqueues the same handle on demand, so page templates that never render the widget pay zero front-end cost. - Mascot frames and the custom avatar (data URI capped at ~700 KB by
sanitize_custom_avatar) are inlined into config rather than fetched, eliminating an extra round trip on first paint.
Server-side caching
Saathi caches the expensive work, not the cheap work. Embeddings are computed once per chunk and stored as JSON in {prefix}sathi_knowledge_chunks.embedding, with a SHA-256 checksum column so unchanged posts skip re-embedding entirely on save. The background embedder runs every minute via the sathi_knowledge_generate_embeddings cron, processing up to sathi_embed_batch_size (default 20) chunks per tick, so large catalogues never block a single request.
License status (sathi_license_status) and the premium directive (sathi_premium_grant) live in 24-hour transients, and the monthly spend counter (sathi_monthly_spend) is also a transient consulted before each provider call. These three are internal caches — you do not touch them directly. The monthly spend cap that the counter is checked against is set via the sathi_cost_cap_monthly option in Saathi AI → Settings, and a full re-crawl that warms the embedding cache is triggered from Saathi AI → Knowledge.
Streaming over SSE
The chat endpoint at /sathi-stream/{uuid}/ is a Server-Sent Events route, not REST. On handle_stream Saathi calls @set_time_limit(0), raises the memory limit, and force-closes every active output buffer so tokens reach the browser the moment the provider emits them. Each chunk is a JSON line (token, actions, products, metadata) terminated by [DONE].
X-Accel-Buffering: no on the stream route and disable proxy buffering, otherwise visitors will see tokens arrive in one large blob after the provider finishes.Caching plugin compatibility
Saathi works cleanly with WP Rocket, W3 Total Cache, and LiteSpeed Cache once the streaming endpoint is excluded from page caching. Apply these rules in the plugin's exclusion panel.
| Plugin | Exclude from cache | Minify / defer | Notes |
|---|---|---|---|
| WP Rocket | /sathi-stream/.*, /wp-json/sathi/.* | Defer JS: keep sathi-chat-widget excluded from delay-JS | Turn off “Optimize CSS delivery” on pages with the inline widget config |
| W3 Total Cache | Page cache: never cache the stream and REST paths | Minify is safe; do not combine sathi-chat-widget with other modules | Disable object cache on sathi_license_status if you see stale entitlements |
| LiteSpeed Cache | Do-not-cache URI: sathi-stream | ESI is fine; leave the localized sathiConfig inline (do not externalize) | QUIC.cloud image optimization is unaffected; mascot frames are inlined |
wp_create_nonce('wp_rest'), which breaks the next streamed turn until the page is regenerated.Database growth and cleanup
Each turn writes two rows: one user message and one assistant message in {prefix}sathi_messages. A typical ten-turn conversation produces about 20 message rows plus one conversation row, with optional tool-call JSON inline. Memory facts land in {prefix}sathi_memory_entries at one row per extracted key. Knowledge chunks scale with site content, not traffic — usually a few hundred to a few thousand rows total.
- Trim soft-deleted conversations.Saathi marks conversations as
status='deleted'rather than hard-deleting. Schedule a monthly job that purges rows wherestatus='deleted' AND updated_at < NOW() - INTERVAL 30 DAY; the foreign key withON DELETE CASCADEwipes their messages automatically. - Prune expired memory.
MemoryStore::prune_expired()deletes rows past theirexpires_atin one SQL. Wire it to thesathi_memory_prunecron hook if your install does not already. - Cap the debug log.
wp-content/uploads/sathi-debug.lognever rotates. Setsathi_log_leveltowarning(default) in production and clear the file fromSaathi AI → Logswhen it crosses a few megabytes. - Re-embed only when needed.Avoid running
generate_all_embeddingson a schedule. The per-minute background embedder already fills gaps; manual full re-embeds are only needed after switching embedding providers.
Cloudflare APO and edge networks
Cloudflare Automatic Platform Optimization is compatible as long as the streaming and REST routes bypass the edge cache. Add a Cache Rule that sets Bypass cache for URIs matching /sathi-stream/* and /wp-json/sathi/*, and disable Rocket Loader on pages that render the widget so the ES module is not rewritten. APO will still cache the rest of the page HTML, which is where the real savings live.
/sathi-stream/new/ with curl -N and confirming tokens arrive line by line.22 · Security & hardening
Saathi runs entirely on your WordPress install. Your provider keys, your visitor conversations, your knowledge base — all of it lives in your database, on your host, under your control. This section covers the protections Saathi ships with, the places where the responsibility sits with you, and the steps to take if something does go wrong.
API keys are encrypted at rest. Every provider API key you paste into Saathi AI → Settings is encrypted before it touches the wp_options table. Saathi uses AES-256-CBC via PHP's openssl_encrypt, with the encryption material derived from wp_salt('auth') — the same salt WordPress already uses to sign your auth cookies. Encrypted values are stored with an enc: prefix so the plugin can tell encrypted blobs from legacy plaintext on read. Round-tripping a config (read then write) does not double-encrypt, because the writer skips values that already carry the prefix.
openssl is enabled in phpinfo() before pasting production keys.License verification uses RS256 signed envelopes. The license check between your site and saathi.neermedia.com is signed end to end. The server returns a JWT-style payload signed with an RSA private key; the plugin ships with the corresponding public key and verifies every response via openssl_verify. A man-in-the-middle, a spoofed DNS record, or a mirror server cannot mint a valid response without the private key. Each license is also bound to your home_url() host (lowercased), so a leaked key copied to another domain will fail activation with a domain-mismatch reason. If the license server is briefly unreachable, the last verified entitlement keeps premium features alive for up to fourteen days before falling back to inactive.
Guest identity is cookie-scoped, not fingerprinted. Anonymous chat visitors get a SHA-256 random identifier stored in the sathi_guest cookie. It is set HTTPOnly, SameSite=Lax, with a one-year lifetime, and is the only piece of state Saathi keeps about a guest. The cookie scopes their conversation history and memory entries to their browser — no email, no fingerprint, no IP logged into the memory table. Public memory routes deliberately ignore any client-supplied guest_id and rebind to the server-derived cookie value to prevent ID-swap attacks.
REST API protections. The sathi/v1 namespace is layered: admin routes are capability-gated, mutating commerce routes are nonce-protected, and public chat routes are scoped server-side.
- Capability gates. Every admin-side route under
/wp-json/sathi/v1/— settings, knowledge index, license, playground, persona writes, memory admin — requiresmanage_options. Deep-scan and embedding endpoints also require the Max-tierdeep_scanentitlement on top of that. - Nonce verification on cart mutations.
POST /wp-json/sathi/v1/cart/addrequires a validX-WP-Nonceheader against thewp_restaction; an invalid nonce returns 403. The widget receives a fresh nonce on every page render. - CORS whitelist on the stream. The SSE stream endpoint at
/sathi-stream/{uuid}/setsAccess-Control-Allow-Originto yourhome_url()only. Cross-origin browsers are blocked at the response header. - API key masking on read. When the settings UI reads a stored provider config, the key is returned masked via
Helpers::mask_key. On write, if the incoming value is empty or contains the mask characters*or•, the stored encrypted key is preserved — so saving the form without re-typing the key will not accidentally wipe it.
/chat/send, /chat/conversations, /chat/feedback) intentionally use a permissive permission callback so anonymous visitors can talk to the widget. Access is bounded server-side: queries are scoped by the verified user_id or the cookie-derived guest_id, content moderation runs on every input, and license gating short-circuits the provider call when the site is inactive. Treat conversation UUIDs as unguessable secrets and serve your site over HTTPS so they cannot be sniffed.File permissions for self-hosted variants. If you are running a custom build with a config.local.php override file, keep it outside wp-content/uploads/ and set permissions to 0600 (owner read/write only). The Saathi debug log at wp-content/uploads/sathi-debug.log should be 0640 at most, and the directory itself should not be web-listable. On Apache, drop a one-line .htaccess with Require all denied next to the log file; on Nginx, add a location ~ sathi-debug\.log { deny all; } block. Saathi never rotates this log on its own, so audit and rotate it from your host's logrotate config if you run with sathi_log_level set to debug.
What to do if a key leaks. Pasted in a screenshot, committed to a public repo, exposed by a compromised admin account — keys do leak. The recovery sequence is:
- Revoke at the providerLog into the provider dashboard (OpenAI, Anthropic, Google, OpenRouter, etc.) and revoke the leaked key immediately. This is non-reversible and stops billing fraud at the source.
- Generate a fresh keyIssue a new key from the same provider. Scope it down if your provider supports scoped or restricted keys.
- Re-paste in WordPressOpen
Saathi AI → Settings, paste the new key into the relevant provider card, and save. Saathi will encrypt and store it; the old encrypted blob is overwritten in the same option row. - Rotate your WordPress saltsIf you suspect database compromise (not just a key paste-bin leak), regenerate the salts in
wp-config.php. Because encryption is derived fromwp_salt('auth'), this invalidates every stored encrypted key on the site, so you will need to re-paste each provider key once. - Audit usageCheck the provider's own usage dashboard for the spike window, and pull aggregate numbers from
GET /wp-json/sathi/v1/settings/usage?range=7d. Set a monthly cap via thesathi_cost_cap_monthlyoption so future leaks have a ceiling.
Threat model — what Saathi does not do. Saathi is a direct-to-provider plugin. It does not relay your API calls through NEER Media servers, it does not phone home with chat content, and it does not ship telemetry.
- No key proxying. Your provider keys go directly from your WordPress server to OpenAI, Anthropic, Google, or whichever provider you configured. NEER Media never sees them.
- No conversation telemetry. Chat transcripts, memory entries, and knowledge chunks stay in your database. The only outbound traffic Saathi originates on its own is the daily license check, which sends your license key and domain — nothing else.
- No third-party analytics. Saathi does not load Google Analytics, Mixpanel, Segment, or any tracker. The widget bundle is served from your own host.
- No silent updates. Plugin updates flow through the standard WordPress updater. Saathi does not pull code or prompts from a remote server at runtime.
ollama or lmstudio from Saathi AI → Settings. Conversations never leave your network, and the only remaining outbound call is the daily license check.23 · Cost & token budget
Saathi never charges you per message. The plugin itself runs entirely on your WordPress server, the widget streams from your own domain, and the only AI cost is whatever your provider bills for the tokens it processes. That makes Saathi predictable in a way most SaaS chat tools are not, and predictable means you can budget, cap, and scale on your own terms.
What does a real conversation cost? A short 6-turn chat with gpt-4o-mini — modest persona prompt, a couple of knowledge-base snippets, brief replies — typically bills around 2,000 tokens end-to-end (roughly 1,500 input including the system prompt and RAG context, plus 500 output). Based on provider list prices as of mid-2026 that lands near ₹0.40 per conversation as a conservative upper-bound that already accounts for the prompt-and-context overhead Saathi adds on top of the raw user message. A longer support session with rich RAG context and detailed answers can climb to 10,000 tokens, and a premium model like gpt-4o or claude-sonnet-4-6 can run ~10× to ~15× the cost of mini for the same dialogue — the same row delta you'll see in the table below. Knowing this up front lets you match the model to the job instead of overpaying for casual greetings.
Here is how the same 2,000-token conversation looks across six providers Saathi supports out of the box. Numbers assume the same ~1,500 input / 500 output split, so if your average chat is heavier or lighter you can scale the rupee figure proportionally.
| Model | Provider | ~₹ per 2k-token chat | Best for |
|---|---|---|---|
gemini-2.5-flash | ₹0.10 | Cheapest hosted option, multilingual | |
gpt-4o-mini | OpenAI | ₹0.40 | Sensible default for most sites |
claude-haiku-4-5 | Anthropic | ₹1.20 | Warm tone, polished writing |
gemini-2.5-pro | ₹2.50 | Long-context reasoning | |
gpt-4o | OpenAI | ₹4.00 | Complex tool calling, vision |
claude-sonnet-4-6 | Anthropic | ₹6.00 | Premium answers, agent loops |
llama3.1 (via Ollama) | Self-hosted | ₹0 | Dev, staging, privacy-first |
Set a spend cap and stop runaway costs. Open Saathi AI → Settings and set a monthly spend cap. Internally this writes to the sathi_cost_cap_monthly option. Once your accumulated cost for the calendar month touches that ceiling, UsageTracker::is_cap_reached() starts returning true and the chat handler refuses further provider calls until you raise the cap or the month rolls over.
- Pick a number you can live with.Start with the cost of one small SaaS subscription — ₹1,000 or ₹2,500 per month is plenty for a small-business chatbot running on the cheaper models.
- Save it under Settings.Saathi logs every chat, embed, and image call into the
wp_sathi_usagetable and caches the running total in thesathi_monthly_spendtransient, so the cap check is fast on every request. - Watch the dashboard.The admin REST endpoint
GET /wp-json/sathi/v1/settings/usage?range=30dreturns totals, per-provider breakdown, and a daily series — exactly what the Dashboard chart consumes.
Switch to a cheaper model under pressure. Because Saathi routes per task, you don't need one model for everything. Under Saathi AI → Settings → Providers the chat, embedding, image, and moderation providers are configured independently. Watch the running monthly total against your cap on the Dashboard — when it climbs faster than expected (a viral landing page, a stuck retry loop on a premium model, an unusually chatty week), that is the cue to swap. A common pattern is to use gemini-2.5-flash or gpt-4o-mini for everyday chat and reserve a premium model only for the agent loop, where tool calling actually benefits from sharper reasoning. Swapping the chat provider takes effect on the very next turn — existing conversations keep their history but the next reply comes from the new model.
Fallback chain: cheap first, escalate if blocked. Saathi doesn't auto-rotate providers, but it makes the manual rotation trivial because every provider is configured side by side and chat routing is a single option. The pattern that works for most sites is: keep two providers warm at all times, watch the cap, and flip the switch when the cheap one hits a wall.
- Configure two providers up front.Add API keys for both your default cheap provider (say Google Gemini) and a premium fallback (OpenAI or Anthropic) under
Saathi AI → Settings → Providers. Keys stay encrypted at rest, so there's no penalty for keeping a second one armed. - Monitor the cap and error log.Check the Dashboard usage chart weekly and skim
Saathi AI → Logsfor repeated rate-limit or quota errors. Both are early signals that the cheap provider is straining or the cap is about to fire. - Swap the chat provider when needed.Change the chat provider dropdown on the Settings page — this updates the
sathi_chat_provideroption and the very next message routes to the new model. No restart, no cache flush. Saathi's friendly-error path turns provider failures into actionable hints inside the widget rather than user-facing 500s, so the switch is invisible to visitors.
update_option('sathi_chat_provider', 'fallback_key'), but for most sites a manual flip once or twice a month is the right balance of control and effort.Run dev and staging on a local model — free. For development, staging, demos, and privacy-sensitive workloads, point Saathi at a local model and pay nothing per token. Both ollama (http://localhost:11434/v1) and lmstudio (http://localhost:1234/v1) are first-class providers with needs_key=false — no API key required. You get the full chat, RAG, and agent loop running against a model on your own hardware, identical wire format to the hosted providers.
localhost, so they only work when the PHP process can reach the model server on the same machine. Great on a laptop or a VPS where Ollama runs alongside WordPress; not viable on shared hosting like Hostinger, where the PHP runtime cannot dial back to your computer. Use them for dev and switch to a hosted provider in production.Watching what you actually spend. Every provider call is logged into wp_sathi_usage with the model name, input and output tokens, estimated cost in USD, conversation ID, and user. UsageTracker::get_stats($from, $to) returns totals, per-provider breakdown, and a daily series ready for charting, while get_pricing_table() exposes the per-1K-token rates Saathi uses for estimation so you can verify the math against your provider invoice. For unknown or newly released models the tracker falls back to a conservative $0.002 input / $0.01 output per 1K tokens default — treat the dashboard number as a reliable trend rather than as an accounting source of truth.
24 · Use cases & playbooks
Saathi is a generalist out of the box, but the difference between a chatbot that visitors tolerate and one that converts is configuration. The five playbooks below are tested recipes — each one tells you what persona to write, which knowledge sources to scan, what follow-up questions to seed, and which model to point at. Copy any recipe into Saathi AI → Personas (or Saathi AI → Settings → Persona for the simple one-persona case) and Saathi AI → Knowledge, adjust the brand specifics, and you have a domain-shaped assistant live in well under an hour.
{company}, {city}, and {service type} — replace these manually with your real values before pasting. The plugin does not auto-substitute custom braces.1 · E-commerce store on WooCommerce Pro Max
Use this when the bot's main job is helping shoppers find products, answer pre-purchase questions, and drop items straight into the cart. WooCommerce abilities and product cards both unlock at the Max tier.
Persona prompt snippet (paste into Saathi AI → Personas as the system_prompt, or into Saathi AI → Settings → Persona for the single-persona case):
- "You are the shop assistant for {company}. Help visitors find the right product, compare options, and check stock. Quote prices in ₹ exactly as listed. When a shopper sounds ready, offer to add the item to their cart. If a product is out of stock, suggest the closest in-stock alternative from the same category. Never invent SKUs, discount codes, or shipping promises."
Knowledge sources to scan (in Saathi AI → Knowledge → Deep scan):
- All published products — descriptions, short descriptions, SKUs, categories.
- Shop pages: shipping policy, returns, size guide, FAQ.
- Header and footer — captures contact number and address so the bot can answer "where are you based?" without searching.
Four follow-up questions to seed (add these to the persona instructions so the LLM offers them inside its <followups> block, and reference them near the greeting in Saathi AI → Settings):
- Show me your bestsellers
- What is your return policy?
- Help me find a gift under ₹2,000
- Track my order
Ideal model: gpt-4o-mini for chat — the cheapest tier in the OpenAI pricing table for chat tasks and accurate at product matching. Use text-embedding-3-small for knowledge. Toggle Product Cards on under Saathi AI → Settings (option key Settings::KEY_PRODUCT_CARDS) so matching SKUs render inline as cards with buy-now links.
2 · SaaS product marketing site Free Pro
For SaaS landing sites, the bot's job is qualifying visitors, answering pricing and integration questions, and routing serious buyers to a demo. Knowledge is small and stable, so the Free tier is usually enough.
Persona prompt snippet:
- "You are the product specialist for {company}. Visitors are evaluating our software. Answer technical questions about features, integrations, security, and pricing using only the documentation you have been given. If a visitor is comparing us to a competitor, stay factual about our capabilities and never disparage the other tool. When a question is outside the docs or the visitor sounds ready to buy, offer to book a 20-minute demo and share the contact page."
Knowledge sources to scan:
- Homepage, features pages, pricing page, integrations page.
- Docs site or knowledge base (re-index when you ship a new feature).
- Security and compliance pages — SOC 2 status, data residency, DPA download link.
- Changelog or "what's new" — so the bot knows what shipped recently.
Four follow-up questions to seed:
- How is your pricing structured?
- Do you integrate with Slack and HubSpot?
- Is my data encrypted at rest?
- Book a demo
Ideal model: claude-haiku-4-5 for chat — it handles structured technical answers cleanly and writes well-formed comparison tables when asked. Use text-embedding-3-small or Cohere's embed-multilingual-v3.0 for embeddings.
3 · Local service business with appointment FAQs Free
For clinics, salons, repair services, and consultancies, the bot answers "when are you open?", "how much does X cost?", and "can I book?". Volume is low, knowledge is tiny, and the Free tier with a local model handles this comfortably.
Persona prompt snippet:
- "You are the receptionist for {company}, a {service type} based in {city}. Be warm and brief. Answer questions about services, pricing in ₹, working hours, location, and how to book. If the visitor wants to book or asks for something you do not have written down, share the phone number and WhatsApp link, and offer to open the contact page. Never quote prices that are not in your knowledge."
Knowledge sources to scan:
- Services page with each treatment, package, or visit type and its ₹ price.
- About page, team page, location page with map embed.
- FAQ page — opening hours, parking, walk-in vs appointment, payment methods.
- Header and footer for phone, WhatsApp, and address.
Four follow-up questions to seed:
- What are your timings today?
- How much for a {top service}?
- Where are you located?
- Book an appointment
Ideal model: gpt-4o-mini if you want zero infra worry, or llama3.1:8b via ollama on a small VPS if you want free forever. Embeddings can use nomic-embed-text locally — the knowledge base is small enough that semantic search runs in milliseconds.
4 · Edtech and online courses Pro
For course creators and edtech platforms, the bot doubles as a pre-sales advisor and a curriculum guide. It answers "is this course right for me?", "what does week three cover?", and "do you offer EMI?".
Persona prompt snippet:
- "You are the learning advisor for {company}. Help prospective students understand the curriculum, decide which cohort or course fits them, and answer questions about prerequisites, certificates, time commitment, and fees in ₹. Use encouraging, honest language. If a learner seems underprepared for a course, say so kindly and suggest a starter track. Never promise placement or salary outcomes you cannot verify from the materials."
Knowledge sources to scan:
- Each course landing page, syllabus, and module breakdown.
- Instructor bios.
- FAQ on fees, EMI, refund policy, certificate, and scholarship.
- Testimonials and case studies — pulled in so the bot can quote real learner outcomes.
Four follow-up questions to seed:
- Which course is right for a beginner?
- What does the curriculum cover?
- Do you offer EMI or scholarships?
- Talk to a counsellor
Ideal model: claude-sonnet-4-6 when curriculum advice needs nuance and longer answers, or gemini-2.5-flash for high-volume cohorts where cost matters more than tone. For embeddings, text-embedding-3-small is the sensible default; reach for text-embedding-3-large only if syllabi run very long and you need fine-grained chunk matching — it is the heavier, more expensive option and not the routine pick.
5 · B2B agency support bot Pro Max
For digital agencies, dev shops, and consultancies, the bot handles inbound leads, qualifies budget and timeline, and answers process questions before a sales call.
Persona prompt snippet:
- "You are the new-business contact for {company}, a {agency type}. Qualify inbound visitors by understanding their project type, timeline, and rough budget in ₹ before suggesting a call. Speak the language of the visitor — switch to Hindi or Hinglish if they do. Share case studies that match their industry when relevant. Never quote a fixed price for a custom project; always route to a discovery call."
Knowledge sources to scan:
- Services pages, case studies, portfolio entries with industry tags.
- Process or methodology page — discovery, design, build, handover.
- About page, team page, leadership bios.
- Pricing guidance page or "starting from" reference, if you publish one.
Four follow-up questions to seed:
- Do you have case studies in my industry?
- How does your engagement process work?
- What is the rough budget for a project like mine?
- Book a discovery call
Ideal model: claude-sonnet-4-6 for chat — it qualifies leads conversationally without sounding like a form. Enable the Memory module so returning visitors are remembered across sessions, and register a custom book_discovery_call ability via AbilityRegistry::instance()->register(). The callback you write inside that ability is where you wire up integrations — for example, generating a Calendly link or creating a HubSpot deal once qualification thresholds are met. None of that is built in; the ability layer just gives the LLM a clean function to call.
_sathi_exclude post meta on any page that should never reach the model.25 · Migrating from other chatbots
Moving to Saathi from Tidio, Crisp, HubSpot Chat, WP Live Chat, or AI Engine is usually a one-evening job. The trick is to lift the parts that carry real value (your knowledge content, persona, and a handful of canned answers) and let Saathi rebuild the rest from your live site through Saathi AI → Knowledge. One expectation to set up front: Saathi is an autonomous AI chatbot grounded in your site content. It answers 24x7 from the knowledge base, but it does not route conversations to a human inbox. If you're coming from a tool with live agents, plan for the bot to become the front line.
Saathi AI → Knowledge → Index Site Now. Most of what your old chatbot "knew" already lives in your pages and products. You only need to migrate what isn't on the public site: persona, canned answers, and private FAQ documents.From Tidio (including Lyro AI)
- Export your knowledgeTidio's Lyro Knowledge panel offers a CSV export of Q&A pairs, and Settings → Data exports conversations as JSON. Save both locally.
- Map Q&A pairs into pagesSaathi indexes WordPress content, not raw CSV. Create one FAQ page (or several category FAQs), paste each question as an
h3with the answer below, publish, and re-runSaathi AI → Knowledge → Index Site Now. - Recreate the personaCopy Lyro's role, tone, and instructions into
Saathi AI → Personas. The closest built-in matches are Sathi Buddy (friendly support) and Sathi Guru (mentor). - Disable Tidio firstDeactivate the Tidio plugin under Plugins → Installed Plugins before enabling Saathi. Tidio injects its launcher via JS; if both load, you'll see two floating bubbles.
- Side-by-side testingUse the widget display controls under
Saathi AI → Settings, set the display mode to Exclude, and list a single staging page ID. Saathi disappears there so Tidio can be tested in isolation.
From Crisp (including MagicReply / Bot)
- Export the HelpdeskCrisp Dashboard → Helpdesk → ··· → Export gives you a ZIP of Markdown articles and images. It's the cleanest export of any vendor on this list.
- Import as WordPress contentBulk-import the Markdown using any Markdown-to-post importer, or paste manually for small libraries. Saathi's crawler auto-indexes all public post types, so a custom
helpdeskCPT works fine. - Carry across the bot scenariosCrisp Bot scenarios don't have a direct equivalent — Saathi is a single conversational agent, not a flowchart. Most scenarios collapse into one or two lines inside the persona's instructions (e.g. "If a visitor asks about pricing, recommend the consultation page and the ₹2,100 starter package").
- Disable Crisp cleanlyDeactivate the Crisp plugin and remove any inline
window.$crispsnippet from your theme. Caching plugins like WP Rocket and LiteSpeed sometimes hold the old snippet — purge cache after deactivation. - Run both during handoverSet Crisp's chatbox to "hidden by default" under Settings → Chatbox → Availability. Let Saathi handle bot conversations while you triage the last Crisp threads from the Crisp inbox during handover.
From HubSpot Chat (Chatflows)
- Export chatflows and snippetsHubSpot doesn't expose a clean chatflow export — screenshot each flow. Knowledge Base articles export from Service → Knowledge Base → Actions → Export. Copy canned snippets out of Conversations → Inbox → Snippets.
- Translate flows into persona instructionsA HubSpot flow ("ask for email → qualify → book a meeting") becomes a few lines in the persona. Saathi's
open_contactclient action handles the "show the contact form" step automatically — see the Navigation section. - Move snippets into the knowledge basePaste each snippet into your FAQ page as a Q&A pair so hybrid search picks them up.
- Disable the HubSpot widgetIf you only used Chat, deactivate the plugin entirely. To keep the CRM and forms, go to Settings → Conversations → Chatflows and set every flow to "Off" — that kills the widget while leaving the CRM live.
- Parallel runHubSpot Chat is page-targeted — point every chatflow at a single hidden test URL while you validate Saathi everywhere else.
From WP Live Chat (3CX, Tawk.to, classic plugins)
- Pull conversation historyLegacy live-chat plugins store transcripts in custom tables (
wp_3cx_messages,wp_wplc_chats). Export via the plugin's Tools page or a phpMyAdmin CSV dump. Saathi won't replay these — they're for your records. - Mine transcripts for FAQsSkim the last 200 chats and write down the ten questions that repeat. Those become your first FAQ page. This is the single highest-leverage migration step on a busy site.
- Choose a personaWP Live Chat is human-only, so there's no bot personality to carry over. Pick the closest match to your team's voice — Sathi Guardian for security niches, Sathi Sage for advisory work, Sathi Spark for D2C and creative brands.
- Deactivate the old plugin fullyLive-chat plugins often leave database rows and scheduled crons behind. After deactivating, inspect
wp_optionsdirectly (or use Advanced Database Cleaner) and remove orphaned rows prefixed with the old plugin's slug. - Set the right expectationSaathi answers 24x7 from the knowledge base but doesn't route to humans. If you still need human coverage during business hours, keep the legacy plugin scoped to those pages via its scheduler, and use Saathi's display Exclude list to stay out of its way.
From AI Engine (Meow Apps)
- Export the embeddings libraryAI Engine → Embeddings → Push → Export as JSON or CSV, one row per chunk with its source URL.
- Re-index, don't re-importAI Engine's vectors are sized for a different model. Confirm every source URL in the export is still a published page, then run Saathi's deep scan —
{prefix}sathi_knowledge_chunksrebuilds with embeddings sized for your configured provider. - Carry over the system promptAI Engine's chatbot Instructions field maps one-to-one onto Saathi's persona text. Strip any AI Engine placeholders like
{CONTENT}— Saathi injects current-page context automatically. - Migrate function calls and MCP toolsPort custom AI Engine functions to Saathi's
AbilityRegistry::instance()->register(...)API — the shape is similar (name, description, JSON Schema, callback, capability). See the Abilities section. - Swap the shortcodesReplace
[mwai_chatbot]and[mwai_discussions]with[sathi_chat]in your pages. Set every AI Engine chatbot's "Site-Wide" to off before deactivating, or you'll get a flash of the old widget during the reload.
Universal cleanup checklist
- Purge your caching plugin (WP Rocket, LiteSpeed, W3 Total Cache) after deactivating the old chatbot — stale snippets are the top cause of "the old widget is still showing".
- Update your privacy policy: remove the old vendor's cookie list and use Saathi's auto-injected wording at
Settings → Privacy → Privacy Policy. - If you loaded the old chat through Google Tag Manager, remove that tag before going live to avoid a duplicate load.
- Check
Saathi AI → Logsafter the first 24 hours — provider auth and rate-limit errors surface here before visitors complain.
{prefix}sathi_conversations, {prefix}sathi_messages, and {prefix}sathi_memory_entries (substitute your site's table prefix). You can read them via the REST endpoints under sathi/v1/chat/conversations and sathi/v1/memory, or pipe them into your CRM with a small custom integration. Memory-extracted facts (name, email, location, preferences) are already structured for that handoff.26 · Backup & data portability
Saathi keeps every conversation, memory entry, knowledge chunk, and usage record inside standard WordPress database tables — no external store, no proprietary file format. That means any backup plugin, host snapshot, or wp db export dump already covers your AI history without extra configuration.
Where the data lives
Six tables (all prefixed with your site's $wpdb->prefix, usually wp_) plus one flat log file:
| Table | What it stores | Notes |
|---|---|---|
sathi_conversations | One row per chat thread — UUID, user/guest ID, persona, provider, status, title, message count, JSON metadata. | Parent of sathi_messages. |
sathi_messages | Every user, assistant, and tool turn — role, content, tool calls, tool results, token count. | ON DELETE CASCADE against the parent conversation. |
sathi_memory_entries | Per-user long-term facts — key slug, value, importance 1–10, TTL, source conversation. | Survives across conversations. |
sathi_knowledge_chunks | RAG index — chunked content, token count, checksum, embedding JSON, status, source URL. | Largest table; embeddings are heavy. |
sathi_usage | One row per LLM call — provider, model, task, tokens in/out, estimated cost per call. | Powers the dashboard charts. |
sathi_personas | Custom personas you've created or edited — slug, role, tone, avatar, colour, system prompt. | Holds your custom personas only. The six predefined mascots live in the sathi_persona CPT (wp_posts), seeded once on activation. |
Debug output is written to wp-content/uploads/sathi-debug.log instead of a table. Include that file when you want full request traces in a backup; skip it if you only care about chat content. Because the log can capture real prompt and reply text, treat it as sensitive — exclude it from public backups or sanitise before sharing for support. Plugin settings, encrypted provider keys, and license state live under sathi_* rows in wp_options — already in every backup, no separate step needed.
Automatic coverage with UpdraftPlus, WP-CLI & host snapshots
- UpdraftPlus / BlogVault / BackupBuddy — the default "all tables" mode picks up every
sathi_*table. If you've narrowed the allowlist to WP core only, add the Saathi tables explicitly. - WP-CLI —
wp db export saathi-backup.sqlcaptures everything. For an AI-only dump in a POSIX shell (macOS, Linux, WSL, Git Bash), runwp db export saathi-only.sql --tables=$(wp db tables 'sathi_*' --format=csv). On plain Windowscmd.exe, runwp db tables 'sathi_*' --format=csvfirst and paste the comma-separated list into the--tables=flag manually. - Host snapshots (Hostinger, SiteGround, Kinsta) — these run at the filesystem and MySQL layer, so chat history is captured automatically alongside posts and pages.
Manual export from the admin
- Per-visitor export (GDPR)Open
Tools → Export Personal Datain WordPress core, enter a user email, and confirm. Saathi's privacy exporter dumps that user's conversations, messages, and memory entries into the standard WP personal-data ZIP — perfect for DSAR responses. - Whole knowledge baseFrom
Saathi AI → Knowledge Base, confirm chunk and token totals, then export withwp db export --tables=wp_sathi_knowledge_chunks. Re-importing that dump on a sister site skips a fresh crawl entirely. - Memory onlyBrowse and delete entries one at a time under
Saathi AI → Memory. For a portable copy, runwp db export --tables=wp_sathi_memory_entries.
Importing into a new site
- Install Saathi firstActivate the plugin on the destination so the schema, rewrite rules, and option defaults exist. Skip the setup wizard for now.
- Match the table prefix before importIf the source and destination use different prefixes (e.g.,
wp_vswp4k_), rewrite the dump before importing —wp search-replaceonly operates on a live database, not a.sqlfile. A quicksed -i 's/`wp_sathi_/`wp4k_sathi_/g' saathi-backup.sqlon the dump file handles this in one pass. - Restore the dumpRun
wp db import saathi-backup.sqlor restore via UpdraftPlus. After the import lands, you can usewp search-replaceagainst the live tables for any URL or domain rewrites that remain. - Flush rewritesVisit
Settings → Permalinksand save. This re-registers the/sathi-stream/SSE endpoint on the new host. - Re-check the licenseThe license is bound to
home_url(). On a new domain you'll need to deactivate on the old site and re-activate atSaathi AI → Settings.
wp_salt('auth'), so the rows copy cleanly but won't decrypt on a different site. Keep provider keys in the destination's .env (or a secrets manager) and re-enter them under Saathi AI → Settings after the import.sathi_knowledge_chunks table can grow to hundreds of MB on content-heavy sites because embeddings are stored as JSON. If your backup tool has a size cap, export that table separately, or skip it entirely and let the destination run a fresh deep scan from Saathi AI → Knowledge Base.27 · WP-CLI commands
If your build ships the optional CLI bridge, the wp saathi namespace gives you a terminal-friendly handle on most operations the admin UI exposes. WP-CLI is the right tool for nightly cron jobs, staging-to-production sync, bulk re-indexing after a migration, or unblocking a site whose wp-admin is throwing a fatal. WP-CLI runs without a logged-in user by default, so current_user_can() checks in handlers like KnowledgeController::check_deep_scan will fail closed — pass --user=<id|login> to impersonate an account that holds manage_options (and the Max-tier deep_scan entitlement for crawl commands).
sathi_ (one a) — option keys, cron hooks, and DB tables all use the short form. Either spelling will find these docs.sathi_knowledge_crawl event fires mid-way through a manual wp saathi knowledge scan, both processes write rows into {prefix}sathi_knowledge_chunks, and the manual scan's terminal prune_orphans step (which only runs when done === true) can soft-delete chunks the cron just inserted in another batch. Pick one path per environment, or unschedule the cron while a manual scan is running.Knowledge base
- wp saathi knowledge scan [--force] [--batch=20]Drives a full deep scan by repeatedly calling
KnowledgeManager::scan_slice()untildone === true. The--forceflag re-indexes every chunk regardless of checksum.--batchsets the slice size (1–25, default 20). Prints a live progress bar ofprocessed/totaland ends with the total chunks written plus orphans pruned. - wp saathi knowledge clear [--yes]Soft-deletes every active row in
{prefix}sathi_knowledge_chunksviaKnowledgeManager::clear_index(). Confirmation is required unless--yesis passed. Embeddings on stale and deleted rows are retained — callInternalVectorStore::clear_all(true)separately for a hard vector reset.
Example
$ wp saathi knowledge scan --batch=10 --user=admin Indexing 248 sources... [====================] 248/248 (100%) Wrote 1,914 chunks. Pruned 23 orphans. Pending embeddings: 412. Success: Knowledge base refreshed in 47.2s.
Memory
- wp saathi memory clear --user=<id|email>Calls
MemoryStore::forget_all($user_id, null)for the matched WordPress user. Pass an email and the command resolves it to an ID. Returns the row count deleted. Add--guest=<guest_id>instead to wipe the matchingsathi_guestcookie owner when a visitor's prior chats are leaking stale facts into a new session. - wp saathi memory pruneEquivalent to invoking
MemoryStore::prune_expired()— deletes every row whoseexpires_atis in the past. Safe to wire to a system cron alongside the built-insathi_memory_prunehook if you don't trust wp-cron on the host.
Personas
- wp saathi persona list [--active] [--format=table]Reads
PersonaRegistry::get_all()and prints slug, name, role, avatar,is_predefined, andis_active. Supports the standard WP-CLI--formatvalues (table, csv, json, yaml). The six built-in mascots (sathi-guru,sathi-ninja,sathi-buddy,sathi-sage,sathi-spark,sathi-guardian) always sort first.
Licensing
- wp saathi license activate <key>Wraps
LicenseManager::activate(). The key is encrypted withHelpers::encryptbefore it lands in thesathi_license_keyoption. Prints the resolved plan, expiry, allowed domains, and entitlements on success — or a server reason (not_found,domain,expired,product) on failure. - wp saathi license status [--refresh]Reads the cached envelope from the
sathi_license_statustransient (24h TTL). Pass--refreshto force a remote re-verify and bypass the cache. The output includesplan,expires,domains,max_domains,enforced, and agracedflag when the plugin is running on the 14-day offline fallback.
Providers
- wp saathi provider test <key>Equivalent of the admin's "Test connection" button. Sends a 10-token reply with OK probe through the configured adapter and prints latency, model, tokens consumed, and the raw reply. Failures are classified into one of
input,config,auth,model,rate_limit,context,network,request,empty, orunknownwith a localized hint.
Example
$ wp saathi provider test anthropic Provider: anthropic Model: claude-sonnet-4-5 Latency: 742ms Tokens: 12 in / 4 out Reply: OK Success: Connection verified.
Cron events
| Hook | Schedule | Purpose |
|---|---|---|
sathi_knowledge_crawl | Daily (configurable) | Batched site re-index, reads sathi_knowledge_crawl_interval. |
sathi_knowledge_generate_embeddings | Every minute | Background embedder, drains up to sathi_embed_batch_size rows per tick. |
sathi_memory_prune | Daily | Deletes expired memory rows past their expires_at. |
sathi_license_check | Daily | Re-verifies the license envelope and refreshes the premium directive. |
Trigger any of these manually with the core WP-CLI cron runner. The daily knowledge rescan, for example:
$ wp cron event run sathi_knowledge_crawl Executed the cron event 'sathi_knowledge_crawl' in 4.183s.
DISABLE_WP_CRON in wp-config.php), point a system cron at wp cron event run --due-now every minute. Saathi's per-minute embedding hook depends on consistent ticks to drain pending chunks.sathi_knowledge_crawl_interval does not reschedule an already-armed event. After updating the option, run wp cron event unschedule sathi_knowledge_crawl and let the plugin re-register it with the new schedule on the next request.Troubleshooting
🔧 "Error: 'saathi' is not a registered subcommand"
The CLI bridge boots from Plugin::instance()->boot(), which only fires when WP-CLI loads the plugin. Confirm the plugin is active (network-active on multisite), and that you're not invoking WP-CLI with --skip-plugins or --skip-plugins=sathi-agentic-ai, which would explicitly exclude it from the boot pass.
🔧 wp saathi knowledge scan exits with "Allowed memory size exhausted"
Page-builder fallbacks fetch the live URL per source via wp_remote_get, which can push memory well past the default 256M ceiling on builder-heavy sites.
- Lower
--batchto 5 so fewer sources hydrate at once. - Raise the CLI memory limit explicitly:
php -d memory_limit=512M $(which wp) saathi knowledge scan. - Run over SSH rather than a shared-hosting browser terminal, which often inherits stricter PHP-FPM limits than the SSH PHP binary.
- Elementor and Divi sites typically need roughly 1.5× the memory of a comparable
the_content-only run.
🔧 wp saathi provider test returns "auth" on a key that works in the admin playground
The admin playground decrypts the stored value via Settings::get_provider_config() with this site's wp_salt('auth') in hand. If you exported the option from another site, Helpers::encrypt on the new host can't decrypt it, and the adapter receives an empty key. Re-enter the key from the Saathi settings screen, or call Settings::set_provider_config() directly from a one-off PHP script — that path re-encrypts with the current site's salt.
28 · REST API reference
Every public-facing surface in Saathi — the chat widget, the admin dashboards, the setup wizard, even the model playground — is driven by a single REST namespace: sathi/v1. Eight controllers fan out from RestServer::register_routes(), and the same endpoints power both the bundled React widget and any custom front-end you choose to build.
This page documents every endpoint, its authentication model, and a representative request and response. Use it as a reference when wiring Saathi into a mobile app, an external dashboard, or a headless storefront.
https://your-site.tld/wp-json/sathi/v1. All write endpoints expect Content-Type: application/json. Endpoints marked admin require the caller to be authenticated as a WordPress user with manage_options capability — log in to wp-admin first or send an X-WP-Nonce generated by wp_create_nonce('wp_rest').Authentication model at a glance
| Group | Permission | Notes |
|---|---|---|
/chat/* | Public | No auth required. Visitors and guests can call it. |
/memory/* (visitor routes) | Public (server-rebinds identity) | Saathi ignores client-supplied guest_id and uses the server cookie to prevent cross-visitor access. |
/personas (GET) | Public | Read-only listings are open; create/update/delete require manage_options. |
/knowledge/search | Public | Indexing, embedding generation, and clearing are admin-only; deep scan also requires the Max-tier deep_scan entitlement. |
/cart/add | Nonce required | Must send X-WP-Nonce with action wp_rest. |
/settings/*, /license/*, /playground/*, /persona/generate | Admin only | manage_options capability enforced. |
/sathi-stream/{uuid}/ is not part of the REST namespace. It is a custom rewrite rule that serves Server-Sent Events. CORS on that route is restricted to Access-Control-Allow-Origin: home_url(), so cross-origin front-ends on a different domain need a reverse proxy. Every endpoint under /wp-json/sathi/v1/* follows standard WordPress REST CORS behavior — no Saathi-specific headers are added or stripped. The SSE rewrite is the only Saathi-specific CORS rule in the plugin.Chat Free
- POST
/chat/send— Send a message, receive a full assistant reply, conversation id, tool calls, and token usage. - GET
/chat/conversations— Paginated history. Query params:status,search,page,per_page. - GET
/chat/conversations/{uuid}— Load one conversation with its messages. - PATCH
/chat/conversations/{uuid}— Rename. Body:{title}. - PUT
/chat/conversations/{uuid}/archiveand/unarchive— Toggle archive state. - DELETE
/chat/conversations/{uuid}— Soft-delete (setsstatus='deleted'). - POST
/chat/feedback— Body:{message_id, rating: "up"|"down", conversation_id}.
curl example — send a message
curl -X POST https://your-site.tld/wp-json/sathi/v1/chat/send \
-H "Content-Type: application/json" \
-d '{
"message": "What are your store hours?",
"persona": "sathi-guru",
"guest_id": "anon_8b3f...",
"temperature": 0.7
}'
Response (HTTP 200):
{
"success": true,
"reply": "We are open Monday to Saturday, 10am to 8pm IST.",
"conversation_id": "550e8400-e29b-41d4-a716-446655440000",
"tool_calls": [],
"tokens": 142,
"model": "gpt-4o-mini"
}
tokens count, but some return totals nested under a usage object — always check for both when consuming the response programmatically.success: true and a friendly_error hint baked into the reply. Always inspect the success field in the JSON body — do not rely on HTTP status codes alone.Settings
All settings endpoints require manage_options.
- GET
/settingsand POST/settings— Read or write the registered settings keys exposed viaSaathi AI → Settings. - GET
/settings/mascots— Returns the avatar pack with frames and labels for the picker. - GET
/settings/providers— Lists every configured provider with masked API keys. - POST
/settings/providers/{provider}— Save API key, model, temperature, base URL. - POST
/settings/providers/{provider}/test— One-shot "reply with OK" probe to validate credentials. - GET
/settings/providers/{provider}/models— Returns{models, source: "live"|"fallback"|"none"}. - GET
/settings/usage?range=7d— Token and cost aggregates fromUsageTracker.
api_key field empty (or containing only * / bullet characters) to preserve the stored key. Saathi encrypts keys at rest with an enc: prefix.Personas Free
Read routes are public; write routes require manage_options. The all_mascots license entitlement only affects the widget's avatar picker, not REST access.
- GET
/personas— List all active personas (public). - GET
/personas/{slug}— Fetch one persona (public). - POST
/personas— Create a custom persona (admin). - PUT
/personas/{slug}— Update fields (admin). - DELETE
/personas/{slug}— Soft-delete custom personas only; the six built-in mascots are protected (admin).
Knowledge — search Free
- GET
/knowledge/search?q=&mode=hybrid&limit=5— Public. Modes:keyword,semantic,hybrid.
Example response from /knowledge/search:
{
"results": [
{
"id": 482,
"score": 0.91,
"matched_by": "both",
"excerpt": "Standard shipping takes 3 to 5 business days...",
"source_url": "https://your-site.tld/shipping-policy/",
"source_type": "page",
"tokens": 187
}
],
"mode": "hybrid",
"count": 1
}
Knowledge — indexing Max
Stats requires manage_options. Indexing and embedding routes additionally require the Max-tier deep_scan entitlement.
- GET
/knowledge/stats— Chunk counts, embedding progress, last crawl. - POST
/knowledge/index— Body:{offset, batch}(batch 1–25). Poll for progress. - POST
/knowledge/index/{post_id}?force=true— Re-index a single post or product. - POST
/knowledge/embeddings/generate— Body:{max_chunks}. Returns embedded and pending counts. - GET
/knowledge/chunks/{source_id}— Inspect per-source chunks for diagnostics. - DELETE
/knowledge/clear— Soft-clear the entire index.
Memory Free
Visitor routes are public and server-rebind identity. Admin routes require manage_options.
- GET
/memory— Visitor's own stored facts. - GET
/memory/profile— LLM-generated three-sentence profile. - DELETE
/memory— Wipe everything the assistant remembers about the caller. - DELETE
/memory/{key}— Forget one fact (e.g.user_email). - POST
/memory/extract— Admin. Run extraction on a conversation by UUID (loads up to 100 messages). - GET
/memory/entries— Admin. Paginated table forSaathi AI → Memory. - DELETE
/memory/entries/{id}— Admin. Delete a single entry by its numeric database id. - GET
/memory/stats— Admin. Totals and top keys.
Commerce Max
- GET
/products?q=&limit=3— Returns product cards or{available: false, locked: true, upgrade: "max"}on lower tiers. - POST
/cart/add— Body:{product_id, quantity}. RequiresX-WP-Nonceheader.
X-WP-Nonce returns HTTP 403. Variable products return success: false with a redirect to the product page so the shopper can pick options.License
All license endpoints require manage_options.
- GET
/license/status?refresh=true— Force a remote re-check. - POST
/license/activate— Body:{key}. Envelope includesplan,expires,domains,max_domains. - POST
/license/deactivate— Clears the local key.
Playground
All playground endpoints require manage_options.
- POST
/playground/chat— Body:{provider, model, message, history, temperature}. Returns reply, token count, latency in ms, or a classified failure stage with a hint. - POST
/persona/generate— Body:{description, answers}. AI-drafted{name, persona}JSON used by the setup wizard. Shares the playground controller but lives under/persona/, not/playground/.
/playground/chat first. It runs the production PromptComposer, returns latency and token counts, and classifies every failure into stages like input, auth, model, rate_limit, context, or network with a localized hint. Visit Saathi AI → Setup wizard for the same flow in a guided UI.sathi_chat_tools to add function-calling tools, sathi_chat_actions to surface client-side action buttons in the widget, and sathi_rest_routes_registered to register your own routes alongside the bundled controllers.29 · Hooks & filters for developers
Free Pro MaxSaathi exposes WordPress actions and filters at six seams that map one-to-one with the subsections below: lifecycle and cron, chat and agent, knowledge and RAG, navigation, personas, abilities, providers, and safety, GDPR, licensing. Drop these into a small mu-plugin or your theme's functions.php to bend behavior without forking the plugin.
sathi_agent_max_rounds is the one filter that's genuinely captured once — it's read inside AgentManager::__construct and cached for the life of that instance, so register it on plugins_loaded at priority 11 or earlier. Most other filters (sathi_personas, sathi_provider_{key}, sathi_chunk_size, etc.) re-evaluate per call, so timing is more forgiving — but plugins_loaded is still the safest default.The PHP snippets below use dark-themed <pre> blocks — same styling as the Logs admin page — because no other established pattern exists for multi-line code in this doc.
Action hooks (lifecycle and cron)
| Hook | When it fires | Use case |
|---|---|---|
sathi_booted | After the plugin wires all 16 services on each request. | Register custom abilities or REST routes once core is ready. |
sathi_init | Companion action immediately after sathi_booted. | Late-init subscribers that want a distinct hook from boot. |
sathi_activated / sathi_deactivated | Plugin activation / deactivation. | Schedule or unschedule your own crons tied to Saathi's lifecycle. |
sathi_rest_routes_registered | After all eight REST controllers register routes. | Append routes to the sathi/v1 namespace. |
sathi_knowledge_crawl | Batched site-crawl cron (interval set by sathi_knowledge_crawl_interval). | Call do_action('sathi_knowledge_crawl') to trigger a crawl manually. |
sathi_knowledge_generate_embeddings | Every-minute cron that fills pending embeddings. | Force an immediate embedding batch from an admin tool. |
sathi_memory_prune | Saathi-scheduled cron that expires old memory rows (cleared on Activator::deactivate). | Call MemoryStore::prune_expired() from custom maintenance code or reschedule it on your own cadence. |
sathi_license_check | Daily license revalidation cron. | Notify Slack/email when the license enters its 14-day grace window. |
sathi_stats_event | Inside the WP 7 AI bridge for before_prompt, after_prompt, error. | Funnel AI client lifecycle events into your analytics layer. |
Chat and agent filters
sathi_chat_tools — signature apply_filters('sathi_chat_tools', array $tools, Conversation $conv). Fires in both ChatManager::send_message and the SSE stream. Use it to add provider-agnostic tools the LLM can call mid-turn.
add_filter('sathi_chat_tools', function ($tools, $conv) {
$tools[] = [
'name' => 'lookup_order',
'description' => 'Fetch a WooCommerce order by ID for the current visitor.',
'parameters' => [
'type' => 'object',
'properties' => ['id' => ['type' => 'integer']],
'required' => ['id'],
],
'callback' => fn($args) => wc_get_order((int) $args['id'])->get_data(),
];
return $tools;
}, 10, 2);
sathi_chat_actions receives the assistant's reply content and returns clickable buttons rendered with the message. Heads up: unlike sathi_chat_tools, this filter is invoked with the content string only — no $conv argument — so you can't branch on the current conversation, user, or persona inside the callback. Anything you need has to be inferred from the content itself or pulled from request globals.
add_filter('sathi_chat_actions', function ($actions, $content) {
if (str_contains(strtolower($content), 'book a call')) {
$actions[] = ['label' => 'Book now', 'url' => '/book/'];
}
return $actions;
}, 10, 2);
sathi_system_prompt rewrites the composed prompt last-mile — useful for forcing a tone, language, or boilerplate. Safety rules still win on conflict because they're appended after persona text upstream.
add_filter('sathi_system_prompt', function ($prompt, $conv) {
return $prompt . "\n\nAlways reply in formal British English.";
}, 10, 2);
sathi_should_display_widget hides the floating launcher per page, role, or country. sathi_agent_max_rounds caps the tool-calling loop in AgentManager (default 5).
add_filter('sathi_should_display_widget', function ($show) {
if (function_exists('is_cart') && (is_cart() || is_checkout())) {
return false;
}
return $show;
});
add_filter('sathi_agent_max_rounds', fn() => 10);
Knowledge and RAG filters
sathi_chunk_size(default 512 tokens) andsathi_chunk_overlap(default 50) — tune the chunker for long-form content.sathi_knowledge_post_types— widen the auto-indexer beyond the default['post','page','product'].sathi_embed_batch_size/sathi_knowledge_batch_size(both default 20) — pace cron throughput on small hosts.sathi_vector_search_cap(default 2000) — bound brute-force cosine recall on large indexes.
add_filter('sathi_chunk_size', fn() => 1024);
add_filter('sathi_chunk_overlap', fn() => 128);
add_filter('sathi_knowledge_post_types', function ($types) {
$types[] = 'course'; // include LearnDash courses
$types[] = 'docs'; // and a custom docs CPT
return $types;
});
Navigation, personas, abilities
sathi_allowed_selectors extends the CSS-selector allowlist the in-chat tour guide is allowed to scroll to or highlight — needed for theme-specific sections. sathi_personas registers a persona purely in code, no DB row required (re-evaluated on every PersonaRegistry::get_all() call). sathi_execute_batch_calls swaps a concurrent runner into AbilityRegistry::execute_batch().
add_filter('sathi_allowed_selectors', fn($s) =>
array_merge($s, ['.my-pricing-grid', '#hero-cta'])
);
add_filter('sathi_personas', function ($personas) {
$personas['brix-helper'] = [
'name' => 'Brix Helper',
'role' => 'Logistics concierge',
'tone' => 'professional',
'avatar' => '📦',
'color' => '#144685',
];
return $personas;
});
Providers
sathi_provider_{key} registers a fully custom adapter under a provider key that's not in ProviderCatalog. The factory checks this filter only when the catalog has no matching entry, so it cannot shadow built-ins like openai. The filter runs each time Factory::make() resolves the unknown key — instances are then memoized per request.
add_filter('sathi_provider_myllm', function ($adapter, $config) {
// $adapter is the current adapter (typically null for unknown keys);
// returning a ProviderInterface instance overrides it.
return new \My\Plugin\MyLlmAdapter($config);
}, 10, 2);
Safety, GDPR, licensing
sathi_require_consent— returntrueto render the GDPR consent banner inwp_footeruntil the visitor accepts.sathi_moderation_blocked/sathi_moderation_warnings— replace or extend the regex pattern arrays used byContentModerator.sathi_license_server_url— point activation at a staging mirror.sathi_license_enforce— disable license gating in local dev (pair with theSATHI_LICENSE_ENFORCEconstant for redundancy).sathi_cost_cap_check— block over-budget WP 7 AI prompts before they hit the provider. Heads up: this is a Phase-9 stub that passes through by default, so the budget logic is yours to write — the filter just gives you the seam.sathi_module_script_handles— opt your own JS bundle into the ES-module rewriter sotype="module"is added automatically.
add_filter('sathi_require_consent', '__return_true');
add_filter('sathi_cost_cap_check', function ($prevent, $request) {
$monthly = (float) get_option('my_monthly_spend', 0);
if ($monthly >= 50.0) {
return true; // block the prompt
}
return $prevent;
}, 10, 2);
sathi_system_prompt runs after Saathi's non-overridable safety rules are appended. You can prepend instructions, but you cannot strip the safety footer — and you shouldn't try. A persona that says "answer anything" will still refuse passwords, OTPs, and PII.has_filter('sathi_chat_tools') if you need to detect whether another plugin is already extending the same seam, and prefer array_merge over reassignment to play nicely with the rest of the ecosystem.30 · Roadmap & changelog
Saathi ships small, useful updates rather than big-bang rewrites. Each release is documented in the plugin's readme.txt and surfaced on the Saathi AI dashboard, so you always know what changed before you click Update. Here's a quick look at what shipped recently and what's on the workbench next.
Recently shipped
Versions are listed newest first. Update from Plugins → Installed Plugins in WordPress the same way you'd update any other plugin.
- 2.1.3 — WordPress 7 AI bridgeSaathi-configured OpenAI, Anthropic, and Google keys now appear as native WordPress 7 Connectors, and knowledge search plus site navigation register as first-class WP Abilities.
- 2.1.2 — Setup wizard polish and playgroundThe first-run wizard gained a live AI playground at the Provider step, a mascot picker, accent-colour swatches, and custom avatar upload up to ~600KB.
- 2.1.1 — Hybrid retrieval and multilingual intentKnowledge search now blends FULLTEXT keyword matching with embedding-based cosine similarity, with Hindi (Devanagari), Gujarati, and Romanized-Hindi intent detection built in.
- 2.1.0 — Personas, memory, and tool registryShipped six built-in mascot personas, per-user long-term memory with LLM-powered fact extraction and TTL expiry, and an Abilities registry for toggling individual AI tools from
Saathi AI → Abilities.
sathi_conversations, sathi_messages, and sathi_memory_entries are the most active), but a fresh backup is cheap insurance.Coming soon
The roadmap below is what we're actively designing and prototyping — direction is firm even when dates are not.
- Voice mode. A speak-and-listen layer for the chat widget so visitors can ask questions out loud and hear the answer back. Browser-native speech APIs first, with a provider-backed option for sites that want higher fidelity.
- Image input. Let visitors upload a photo and ask about it — "match this fabric to a saree", "identify this part number on a spare", "is this the right cartridge for my printer?" — gated by the same capability and moderation rules as text.
- Ticket export. One-click export of a stuck conversation into a support ticket — CSV, JSON, or direct push to Freshdesk, Zoho Desk, and HubSpot. The handoff carries the full transcript, the visitor's memory profile, and the page they were on.
- Slack handoff. A live "send this to a human" button that pings a chosen Slack channel with the transcript and lets your team reply right inside Slack while the visitor keeps chatting on the site.
- Deeper WooCommerce upsell flows. Today's
sathi_wc_search_productstool returns matching products; we're working on guided flows that bundle cross-sells, surface coupons the visitor qualifies for, and remember abandoned carts across sessions. - Multilingual persona pairs. Pair a single persona with two or three language voices so the assistant stays on-brand whether the visitor types in English, Hindi, or Gujarati. Useful for sites serving multiple regions from one storefront.
Saathi AI → Dashboard. We read every request and the roadmap shifts based on what site owners actually ask for.Want the unabridged version? The full changelog lives in the plugin's readme.txt and is visible on the WordPress plugin update screen — every shipped change is there with the date and upgrade notes if any manual step is required.
Troubleshooting
🤐 Bot not replying at all
Go to Saathi AI → AI Providers and click Test connection. Common causes:
- Invalid API key: Re-paste the key — no spaces before/after. Make sure it hasn't been revoked in your provider dashboard.
- Out of credits: Check your provider billing page. Most providers send an email when credits run low.
- Server blocked outbound: Some shared hosts block external HTTP. Check with your host that cURL is enabled and that
api.openai.com(or your provider's domain) is reachable. - Wrong base URL: For custom/Ollama endpoints, double-check the URL format (e.g.
http://localhost:11434/v1).
🌐 Widget not visible on the frontend
- Check
Saathi AI → Appearance → Visibility rules— ensure the page you're testing isn't in the hidden list. - Check if a caching plugin (WP Rocket, W3 Total Cache) is serving a stale page. Clear all caches and hard-refresh.
- Check for JavaScript conflicts: open browser DevTools (F12) → Console tab → look for red errors. Temporarily deactivate other plugins to isolate the conflict.
- Check that the plugin is activated (not just installed) in
Plugins → Installed Plugins.
🤔 Answers are generic / bot doesn't know my products
- Run a fresh website scan:
Saathi AI → Knowledge → Scan website. If you've published new content since the last scan, it won't be in the bot's knowledge base. - Improve your content: add detailed product descriptions, an FAQ page, and a clear "About" page. Thin content gives thin answers.
- Check the Knowledge list to confirm your key pages were indexed (word count should be > 50 words per page).
- For WooCommerce products, ensure they have meaningful descriptions — not just titles.
🌍 Bot replies in English even when I write in Hindi
- This is usually a model limitation. Switch to
gpt-4o-mini,gpt-4oorgemini-1.5-flashwhich have strong multilingual support. - Check your persona's system prompt — if it says "Always reply in English", that overrides language detection. Remove the language constraint.
💳 License shows "Invalid" after activation
- Confirm the key is copy-pasted exactly — no leading/trailing spaces, no missing characters.
- Check if the key is already activated on another domain. Go to your dashboard → License → Activations to see active domains and deactivate if needed.
- Confirm your server can reach
saathi.neermedia.com— some locked-down hosts block outbound HTTPS. Contact your host to whitelist the domain.
🐌 Chat responses are slow
- Switch to a faster model:
llama-3.3-70bon Groq is the fastest (free tier available), followed bygpt-4o-mini. - If using Ollama/LM Studio locally, response speed depends on your machine's hardware.
- Check your server's PHP execution time — if it's below 60 seconds, streaming may time out on slow models.
🛒 WooCommerce product cards not showing
- Confirm you are on the Max plan — WooCommerce selling requires Max.
- Re-run the product sync:
Saathi AI → Knowledge → WooCommerce → Sync products. - Ensure products are published and not out of stock (out-of-stock products are excluded by default).
- Try a more explicit query in the chat: "Show me products in the [category name] category".
🔧 Streaming stops mid-reply or freezes after a few words
The chat widget starts typing, then halts before the full answer arrives. This is almost always server-side buffering or a hard timeout cutting the Server-Sent Events stream short, not the AI provider itself.
- Add the response header
X-Accel-Buffering: noat the Nginx level, or disable proxy buffering for the/sathi-stream/path so tokens flush in real time. - Behind Cloudflare, switch the orange-cloud setting to grey for the streaming route, or enable the Enterprise streaming feature — Cloudflare buffers SSE on default proxy settings.
- Raise PHP
max_execution_timeto 120 seconds and bump the PHP-FPM / Apache request timeout — the plugin callsset_time_limit(0)but the web server still enforces its own ceiling. - Disable page-cache plugins (WP Rocket, LiteSpeed, W3 Total Cache) for the stream endpoint, and turn off minifiers that start their own output buffer — the SSE handler force-closes buffers but caching layers above PHP can still swallow chunks.
- Visit
Settings → Permalinksonce to flush rewrite rules. If/sathi-stream/{uuid}/404s, the SSE route never registered.
🔧 Knowledge base scan stuck at the same percentage
The progress bar under Saathi AI → Knowledge Base stalls mid-crawl or never reaches 100 percent. This usually means a builder page is timing out the loopback fetch or the slice is hitting a memory ceiling on a single post.
- Lower the slice batch size in the admin UI from 8 down to 3 or 4 so each request finishes inside your host's PHP timeout — large Elementor pages can take 10+ seconds to render via the internal loopback.
- Whitelist the
SaathiBot/1.0user-agent in Wordfence, Cloudflare WAF, or any bot-blocker. The crawler fetches your own live URLs to pull builder-rendered HTML, and bot firewalls will return login pages instead. - If you use HTTP basic-auth on staging, the loopback can't pass credentials — temporarily disable auth, run the scan, then re-enable.
- Click Clear Index first, then rerun the deep scan from offset 0. The orphan-prune step only runs on a clean deep scan, not on the daily cron, so stale chunks can accumulate.
- If a single post keeps failing, mark it with
update_post_meta($id, '_sathi_exclude', '1')to skip it.
🔧 Memory is not updating after conversations
The bot keeps forgetting visitor names and preferences even though Memory is enabled. The most common causes are guest cookie loss, TTL expiry, or the extraction LLM call silently failing.
- Open
Saathi AI → Memoryand confirm rows actually exist for the user or guest you tested. If the table is empty, fact extraction never ran — check the Logs page for provider errors. - Verify the
sathi_guestcookie is being set in the browser. Aggressive cookie banners or Safari ITP can wipe it between visits, which makes every session look like a brand-new guest. - Guest memory and logged-in user memory live on separate rows. A visitor who chatted as a guest, then logged in, will not see their old facts surface — this is by design, not a bug.
- If
sathi_memory_ttl_daysis set low, old facts are auto-deleted. Set it to0in Settings to keep memory forever, or raise it to 365. - For non-English conversations, the regex fallback only catches ASCII names. Ensure your chat provider is configured and reachable so the LLM extraction path runs first — Devanagari, Gujarati, and Tamil names are picked up only by the LLM extractor, not the fallback.
🔧 REST API returns 401 or "Invalid nonce" on the front end
The widget loads, but every message returns an authentication error. This almost always traces back to a stale nonce served from a cached page, not a real permission problem.
- Exclude the page hosting the widget from full-page cache, or shorten the cache lifetime to under 12 hours. WordPress nonces expire after 24 hours, and cached HTML can serve a dead nonce to fresh visitors.
- If you use WP Rocket or LiteSpeed, disable HTML minification for inline scripts — the
sathiConfigobject embedded in the page must stay intact. - For the
/cart/addendpoint specifically, theX-WP-Nonceheader is required. Other public chat routes do not require a nonce, so a 401 on add-to-cart but a 200 on send-message points at a nonce issue, not a provider issue. - Confirm your front end is on the same origin as
home_url(). The stream endpoint setsAccess-Control-Allow-Originto home URL only, so headless setups on a different domain will be blocked by CORS. - If the issue only affects admin REST calls, log out and back in to refresh the
wp_restnonce in the admin bundle.
🔧 Embeddings stay in "pending" forever
The Knowledge dashboard shows thousands of chunks but the embedded count never climbs. The background embedder is either misconfigured, throwing on every batch, or pointing at the wrong model for the active provider.
- Open
Saathi AI → Settings → Providersand confirm an embedding provider is selected with a valid API key. If no provider is configured, semantic search silently degrades to keyword-only and the pending queue never drains. - If you switched embedding providers (for example, OpenAI to Gemini or Cohere), reset the embedding model. The plugin auto-corrects the common OpenAI defaults, but stale values like
text-embedding-ada-002on Gemini will 4xx every call. - Vectors are stored at a fixed dimension set at construction (1536 by default). Switching from
text-embedding-3-smalltotext-embedding-3-largewithout clearing existing embeddings leaves the new vectors silently rejected — clear all embeddings, then regenerate. - Click Generate Embeddings with a small
max_chunksvalue (50 to 100) to force a synchronous batch. If that fails, the Logs page will show the exact provider error. - Confirm WP-Cron is firing. Run
wp cron event run sathi_knowledge_generate_embeddingsover WP-CLI, or hitwp-cron.phpin a browser. Hosts that disable WP-Cron need a real system cron pointed at it.
🔧 License is valid but the bot says "not activated yet" after moving the site
This happens after a domain change, migration to production, or staging-to-live promotion. The license is bound to the original home_url() host, and the new domain is consuming a fresh activation slot.
- On the old domain (if still reachable), go to
Saathi AI → Dashboard → Licenseand click Deactivate before migrating. This frees the activation slot cleanly on the license server. - If the old domain is already gone, log in to your account at
saathi.neermedia.comand remove the orphaned domain from the key's activation list, then re-activate on the new site. - For local development or staging, set
define('SATHI_LICENSE_ENFORCE', false);inwp-config.phpso the gate is bypassed without burning an activation slot. - On production, click Refresh Status with the
refresh=trueflag to bypass the 24-hour cached transient — directive changes on the server take up to a day to propagate otherwise. - If the response says "License is bound to a different domain," confirm
home_url()matches the activated host exactly. Trailing slashes are stripped, butwwwversus apex counts as two different hosts.
Plugins → Installed Plugins), your AI provider, and a description of the issue. Screenshots help a lot.FAQ
Which AI providers are supported? +
Saathi supports 15 providers: OpenAI, Anthropic Claude, Google Gemini, xAI Grok, DeepSeek, Mistral AI, Perplexity, Cohere, OpenRouter, Groq, Together AI, Fireworks AI, Ollama (local), LM Studio (local), and any custom OpenAI-compatible endpoint. You bring your own API key — Saathi never charges you per message.
Does Saathi read my entire site? +
Only published content is indexed — published pages, posts and WooCommerce products. Drafts, trashed content, private posts, password-protected pages and admin-only content are automatically excluded. You control exactly what the bot knows.
Where do I get my license key? +
Log in to your Saathi dashboard, go to the License tab. Your key is displayed there and also emailed to you at checkout. Paste it in Saathi AI → License in WordPress to activate.
Will Saathi slow my website? +
No. The widget loads asynchronously after the page is fully interactive. Core Web Vitals (LCP, FID, CLS) are not affected. The script is ~18 KB gzipped and deferred.
Can I use Saathi on multiple websites? +
Each license key activates on one domain. For additional sites, purchase additional licenses from your dashboard. License keys are domain-locked and cannot be transferred without support.
Does it work with WooCommerce? +
Yes — WooCommerce product showcase, Add to Cart and Buy Now inside chat are available on the Pro and Max plans. Saathi reads your live product catalog, shows product cards with images and prices, and can send visitors directly to checkout.
What happens if I run out of AI credits? +
Saathi itself has no usage limits — you pay only your AI provider. If your API key runs out of credits, the bot will show a connection error. Simply top up your account with your provider and the bot resumes automatically.
Is GDPR / privacy compliance handled? +
Yes. Saathi supports a GDPR consent gate — the chat window can be configured to show a consent notice before starting a conversation. Guest conversations are identified by a privacy-safe cookie, never by IP address alone. No conversation data is shared with third parties.
Can I have multiple personas? +
Pro and Max plans support multiple AI personas — each with its own name, tone, system prompt and mascot. You can switch personas per page or let the AI build custom ones based on your content.
Does the bot remember past conversations? +
Yes. The memory feature (Pro/Max) stores key facts from past sessions — customer preferences, previous questions, product interests — and surfaces them in future conversations. Guests and logged-in users both get memory support.
Can I train Saathi on PDFs or external documents? +
Saathi's Knowledge module indexes WordPress content directly — posts, pages, WooCommerce products, any public custom post type, plus the theme's header and footer. PDFs aren't ingested as binaries today, but the practical workaround is to paste the extracted text into a WordPress page (private or marked with _sathi_exclude to hide from search) and let the indexer chunk it into wp_sathi_knowledge_chunks like any other source. For external docs, push them into a custom post type and add the slug to the sathi_knowledge_post_types filter so they're re-indexed automatically on save.
How do I add my own AI-callable tools or functions? +
Register them with AbilityRegistry::instance()->register($name, $definition) from any plugin or theme. Each ability needs a label, description, JSON Schema parameters, a PHP callback, a required capability, and a category. Once registered, your tool appears under Saathi AI → Abilities with a per-row enable/disable toggle, and the agent loop picks it up automatically via to_agent_tools(). Failed calls inside the callback are caught and surfaced back to the model as {error, details} rather than crashing the chain — your bot can apologise and recover.
Does Saathi support voice or image input? +
Not in the bundled widget today — the SSE pipe at /sathi-stream/{uuid}/ carries text tokens only. If your selected provider supports multimodal input (GPT-4o, Claude Sonnet 4, Gemini 2.5 with image attachments), the underlying adapter can accept image data via a custom front-end built on the public REST endpoints. Voice in/out (Whisper transcription, TTS playback) is on the roadmap; for now we recommend pairing Saathi with a dedicated voice plugin if that's a hard requirement.
How good is the multi-language support? +
The system prompt instructs the assistant to mirror the visitor's language and script — Hindi, English, Hinglish, Gujarati, Marathi, Tamil, and more — while preserving brand and technical terms verbatim. The Knowledge module's intent detection also recognises English, Hindi (Devanagari), and Gujarati phrases for common queries like price, contact, and phone number, and boosts the right chunk type accordingly. Quality at the LLM layer depends on the model: GPT-4o, Claude Sonnet, and Gemini 2.5 handle Indic scripts confidently, while smaller open-source models may need explicit persona instructions to stay in-script.
How does Saathi prevent jailbreaks and unsafe responses? +
Safety rules are appended last in the prompt and explicitly told to override the persona on conflict — even an owner persona that says answer anything will still refuse passwords, OTPs, full card or CVV numbers, bank/UPI PINs, Aadhaar, SSN, passport numbers, and API keys. Prompt-injection patterns like ignore previous instructions are called out in the rules. The sathi_strict_scope setting (on by default) keeps the bot on your site's topics and refuses general trivia or competitor questions, and Saathi AI → Settings → Content moderation blocks SQL, shell, and XSS-shaped inputs before they reach the provider.
Can I hand off a chat to a human agent? +
There is no built-in live-agent inbox in Saathi today. Practical patterns work well: register a custom ability called request_human_handoff that emails your team or opens a ticket in your helpdesk, and have the persona instructions invoke it on phrases like talk to a person. You can also use the sathi_chat_actions filter to surface a Book a call or WhatsApp us button when the assistant's reply contains a trigger phrase. The user message and assistant reply are already persisted in wp_sathi_messages, so a human picking up the thread later has full context.
How long are conversations stored, and can I export them? +
Conversations and messages live in wp_sathi_conversations and wp_sathi_messages indefinitely — delete_conversation() is a soft status flip, not a hard purge, so plan a separate cleanup job if you need bounded retention. Saathi registers a WordPress core personal-data exporter, so any logged-in user can pull their conversations, messages, and memory entries through Tools → Export Personal Data, and the matching eraser is wired into Tools → Erase Personal Data for GDPR right-to-erasure. Memory entries also expire automatically based on sathi_memory_ttl_days (default 90).
Can I customize colors, fonts, and CSS beyond the settings panel? +
The settings panel covers accent colour, theme (light/dark/auto), position, launcher icon, mascot, greeting, widget title, and auto-open delay — most sites never need more. Beyond that, the widget is a regular React component mounted into wp_footer, so CSS rules targeting .sathi-chat-widget from your theme or a child theme stylesheet will override defaults. For deeper changes, you can dequeue sathi-chat-widget, enqueue your own bundle, and talk to the SSE endpoint at /sathi-stream/{uuid}/ directly using the sathiConfig nonce and guest_id.
Can I run Saathi fully on-premise without sending data to a cloud LLM? +
Yes. The provider catalogue ships with two local adapters out of the box: ollama at http://localhost:11434/v1 and lmstudio at http://localhost:1234/v1, both with needs_key=false. Pair them with a local embedding model like nomic-embed-text (Saathi auto-selects it when the embed provider is local) and your entire pipeline — chat, embeddings, RAG, memory extraction — stays on your own hardware. Note that on shared hosting like Hostinger, the PHP process usually can't reach a localhost LLM, so on-prem in practice means a VPS, a dedicated server, or running WordPress and the model on the same box.
Can I embed the chat outside WordPress, e.g. in a separate React app? +
The SSE stream endpoint sets Access-Control-Allow-Origin to home_url() only, so cross-origin front-ends on a different domain are blocked by CORS. For a same-domain SPA — a headless front-end served from the same host as WordPress — the REST endpoints under /wp-json/sathi/v1/ and the /sathi-stream/{uuid}/ SSE route work as-is, provided you carry a valid X-WP-Nonce for mutations like cart add. For genuinely external apps, you'd need a small filter to expand the allowed origin or a server-side proxy that holds the nonce on the visitor's behalf.
How do I capture leads or push chat data into a CRM or Zapier? +
The cleanest path is a custom ability: register a tool like capture_lead with a JSON Schema for name, email, and intent, and have the callback POST to your Zapier catch hook or CRM REST endpoint. You can layer in the sathi_chat_tools filter to expose more tools per conversation and sathi_chat_actions to render Send to CRM buttons after specific replies. For low-code use, the Memory module already auto-extracts user_email, user_phone, and user_name into wp_sathi_memory_entries — a tiny cron job can scan that table and forward new contacts to your stack of choice.
Can agencies white-label Saathi for client sites? +
Persona name, avatar, accent colour, widget title, greeting, and a fully custom mascot upload (PNG, JPEG, WebP, or SVG up to ~600KB) cover most client-facing branding without code. The plugin's own admin menu still says Saathi AI by default, so true admin-level white-labelling (renaming the menu, replacing the plugin header) needs the Agency tier of the licence — that tier resolves to max in LicenseManager::can() and unlocks every premium entitlement. For reseller pricing and terms, write to hello@neermedia.com.
Need a hand getting set up?
Our team typically replies within a few hours. Or try the live bot right now — it's powered by Saathi itself.
Saathi