# ObieChat User Guide

Welcome to **ObieChat** — the AI chat assistant that captures leads 24/7,
speaks Bahasa Malaysia natively, and pings your phone (via PWA Web Push)
when a conversation actually needs you. The bot does not send WhatsApp
messages on your behalf — visitors get a "Continue on WhatsApp" button
they can tap to message you direct, and you can WhatsApp captured leads
from the console anytime.

This guide covers everything: signup, every settings field, every
feature, and the answers to questions you'll have along the way.

> **Tip:** This guide is written for the website console
> (`https://obiechat.obieonline.com/console`). If you're new, jump to
> **[Getting started](#getting-started)** below.

---

## Table of contents

(*Multiple websites + viewer roles* section added — see
[Multiple websites + viewer roles](#multiple-tenants).)

- [Getting started](#getting-started)
- [Installing the widget](#installing-the-widget)
  - [Plain HTML / any website](#plain-html-or-any-site)
  - [WordPress (plugin)](#wordpress-plugin)
  - [WordPress (manual)](#wordpress-manual)
- [Knowledge base](#knowledge-base)
  - [Manual editor](#knowledge-manual-editor)
  - [Scan your website](#knowledge-website-scanner)
  - [Upload PDF / Word](#knowledge-ingest)
  - [Smart starter questions](#smart-starter-questions)
- [Settings — every field explained](#settings)
  - [Business](#settings-business)
  - [Contact](#settings-contact)
  - [Widget appearance](#settings-widget-appearance)
  - [Languages](#settings-languages)
  - [Lead capture behaviour](#settings-lead-capture)
  - [Booking link](#settings-booking)
  - [Webhooks](#settings-webhooks)
  - [Allowed websites](#settings-allowed-websites)
  - [Escalation rules](#settings-escalation-rules)
  - [Analytics opt-in](#settings-analytics)
- [Leads](#leads)
  - [The leads list](#leads-list)
  - [AI triage — heat, summary, suggested reply](#leads-ai-triage)
  - [Lead detail + transcript + images](#leads-detail)
  - [Filter + export](#leads-filter-export)
- [Live conversations](#live-conversations)
  - [The live console](#live-console)
  - [Taking over a chat](#live-takeover)
  - [Escalation alerts](#live-escalation)
- [Vision Q&A — visitors share photos](#vision)
- [Inline booking — Cal.com / Calendly](#booking)
- [Mobile (PWA) install + push notifications](#pwa)
- [Daily digest email](#daily-digest)
- [Insights](#insights)
- [Credits + billing](#billing)
- [Refunds](#refunds)
- [Privacy + data export](#privacy)
- [FAQ](#faq)
- [Troubleshooting](#troubleshooting)

---

## Getting started

1. **Sign up** at <https://obiechat.obieonline.com/signup>. The
   free 30-day demo gives you **20 credits** for the full window —
   no card required. After 30 days the chatbot pauses on your site
   until you subscribe; your knowledge, leads, and any top-up
   balance carry over.
2. **Verify your email** (link arrives in 1-2 minutes).
3. **Set up your business knowledge** — either type it in the editor
   or click *Scan website* and we'll draft it from your existing site.
   (See [Knowledge base](#knowledge-base).)
4. **Tune your Settings** — WhatsApp number, brand colour, languages
   the bot should speak. (See [Settings](#settings).)
5. **Copy your site key** (Settings → "Site key" — it's the short slug
   like `my-shop`). You'll paste this into the widget snippet next.
6. **Install the widget** on your website. (See
   [Installing the widget](#installing-the-widget).)
7. **Test it.** Open your site in an incognito tab and chat. Verify
   the bot is answering correctly. Tune the knowledge as needed.

That's it. You're ready to take leads 24/7.

---

<a id="multiple-tenants"></a>

## Multiple websites + viewer roles

ObieChat supports one user account belonging to many tenants. Each
tenant is one website's chatbot — its own knowledge, leads, settings,
brand colour, languages, conversations.

If you run multiple websites (say `cafe.com` and `salon.com`), you
have two separate tenants but **one login**. Same email, same
password, one place to manage both.

### The tenant-switcher

In the console header, just to the right of the ObieChat logo,
you'll see a dropdown showing the **active tenant** for your session.
Click it to switch to a different tenant. Every console page —
Dashboard, Leads, Conversations, Insights, Knowledge, Settings —
swaps to the data for that tenant immediately.

If you only have ONE tenant, the dropdown is replaced by a static
label. No clutter.

### Owner vs Viewer roles

Each user has a role per tenant:

- **Owner** — full read + write. Edit settings, knowledge, take over
  chats, manage team, billing. This is the default for the person who
  signed up.
- **Viewer** — read-only. Can see leads, conversations, insights, the
  daily digest. Cannot edit settings, change knowledge, take over a
  chat, send messages, or manage other users.

A small **"Viewer"** badge appears next to the tenant name in the
switcher when the active role is viewer. Bring in a family member or
staff this way without giving them billing or destructive controls.

### Push notifications across tenants

Each tenant's `/console/live` page has its own **Enable phone
notifications** card. Subscribe per tenant — so you can keep your
marketing-site tenant loud (every hot lead pings you) but mute a
low-traffic experimental one.

When a push arrives, the notification is **labelled with the tenant
name** — `🔥 ObieOnline: Hot conversation` vs `🔥 ObieChat: Hot
conversation` — so you can tell which website fired it on your phone's
lock screen without opening.

Tap the notification and the PWA opens the conversation directly. If
your active tenant differs from the conversation's tenant, ObieChat
silently switches your active tenant on the way in — you don't have
to navigate to the switcher first.

### Adding a new tenant or another user (today)

We don't yet have a self-service "Add new website" or "Invite member"
button in the console UI. To set up a second tenant or bring someone
in as a viewer, **WhatsApp us** and we'll do it for you — usually
within an hour during KL business hours.

A self-service flow lands in a future console update.

---

## Installing the widget

ObieChat ships as a **one-line script tag** that loads a sandboxed
iframe on your site. It does **not** affect your site's CSS, layout, or
SEO — everything renders inside the iframe.

### Plain HTML or any site

Paste this just before `</body>` on any page where you want the chat
bubble:

```html
<script src="https://obiechat.obieonline.com/widget.js"
        data-site="YOUR-SITE-KEY"
        defer></script>
```

Replace `YOUR-SITE-KEY` with your real site key (Settings → Site key).

### WordPress (plugin)

1. WordPress admin → **Plugins → Add New** → search **"ObieChat"**.
2. **Install Now** → **Activate**.
3. Go to **Settings → ObieChat** and paste your site key.
4. Save. Refresh your homepage — the bubble appears bottom-right.

The plugin is a thin wrapper that injects the same `<script>` tag in
the footer. No business logic in PHP; everything still runs in the
visitor's browser + on `obiechat.obieonline.com`.

### WordPress (manual)

If you don't want the plugin:

1. WordPress admin → **Appearance → Theme File Editor** (or your child
   theme's `footer.php`).
2. Add the snippet just before `</body>`:

   ```html
   <script src="https://obiechat.obieonline.com/widget.js"
           data-site="YOUR-SITE-KEY"
           defer></script>
   ```

3. Save the file.

> **Tip:** Use a child theme. If you put it in the main theme's
> `footer.php`, a theme update will overwrite it.

---

## Knowledge base

The knowledge base is what the bot uses to answer questions. Everything
your bot says comes from here (or honestly says "I don't know").

### Knowledge manual editor

Open **Console → Knowledge**. You'll find five sections:

#### Business profile

| Field | Used for |
|---|---|
| **Introduction** | A short paragraph the bot uses as its mental model of your business. |
| **Booking URL** | Optional — moved to **Settings → Contact**. Leave blank here. |
| **Phone** | Public business phone (shown when asked "what's your number?"). |
| **Email** | Public business email (secondary — owner prefers WhatsApp). |
| **Response promise** | E.g. *"We reply within 1 hour during business hours."* — bot quotes this verbatim. |
| **Address** | Physical store/office address (shown when asked "where are you?"). |
| **Map URL** | Optional Google Maps link the bot can hand out. |

#### Services

A list of services / products your business offers. For each:

| Field | Used for |
|---|---|
| **Name** | e.g. *"Monthly IT Support"* |
| **Tagline** | One-liner (optional) |
| **Summary** | 2-3 sentences — what's included, who it's for |
| **Included** | Bullet-list (e.g. *"8 hours support, same-day response, quarterly check-in"*) |
| **Ideal for** | Who should buy this (helps the bot recommend correctly) |
| **FAQs** | Q&A pairs specific to this service |

> **Pro tip:** Add 2-3 FAQs per service. The bot uses them verbatim when
> visitors ask — far more accurate than freeform generation.

#### Pricing tiers

Each row is one pricing option:

| Field | Used for |
|---|---|
| **Name** | e.g. *"Business Care"* |
| **Price** | e.g. *"RM 480"* |
| **Unit** | e.g. *"per month"* — bot reads `price + unit` together |
| **Tagline** | Optional one-liner |
| **Features** | Bullet-list of what's included |

> **Important:** If you don't want to publish exact prices, leave them
> blank. The bot will honestly say *"the owner quotes case-by-case"* and
> capture the lead for follow-up instead of guessing.

#### General FAQ

Q&A pairs that don't fit under a specific service. Common ones:

- *"What's your refund policy?"*
- *"Do you serve the whole of Selangor?"*
- *"What are your business hours?"*
- *"What payment methods do you accept?"*

The bot uses these answers verbatim. Spend 15 minutes filling these out
properly — it dramatically improves bot quality.

### Knowledge website scanner

If you already have a website with your business info on it, you can
auto-draft your knowledge base from it.

1. **Console → Knowledge** → top panel **"Import from your website"**.
2. Paste your homepage URL → click **Scan**.
3. We crawl up to **20 pages** of your site, prioritising those that
   look like services, pricing, FAQ, about, or contact pages. Takes
   about 30-60 seconds.
4. The scanned content opens as a **draft** — review it.
5. Click **Save knowledge** to apply, or **Discard knowledge** to keep
   your previous version.

What we scrape:

- Page titles, headings
- Services + their descriptions
- Pricing tables
- Contact info (phone, email, address)
- FAQ-style Q&A blocks

What we **don't** scrape:

- Blog posts (too much noise)
- Image content (not yet — coming later)
- PDFs (use the **PDF / Word ingest** below for those)
- Pages behind a login

<a id="knowledge-ingest"></a>

### Upload PDF / Word — instant knowledge

If you already have a menu, brochure, services catalogue, FAQ doc, or
policy document in PDF or Word format, you can upload it directly
instead of typing the content into the editor by hand.

1. **Console → Knowledge** → top panel **"Knowledge tools"** →
   **Upload PDF / Word**.
2. Pick the file (max **20 pages** for PDF, **5 MB** for either).
3. Wait ~5–15 seconds while we distil it into the structured knowledge
   shape (services / pricing / FAQs / business profile).
4. Review the **preview**: it shows exactly what will be added,
   changed, or removed, plus the token cost.
5. Click **Save** to commit, or **Discard** if it looks wrong.

The bot **never sees the raw extracted text** — only the distilled
structured knowledge. That keeps the prompt tight (your bot stays
fast and cheap) and citations clean.

### Import / export JSON

The "Knowledge tools" panel also has:

- **Import JSON** — upload a JSON file matching the knowledge schema.
  Useful if you maintain your knowledge in another editor, share a
  knowledge base across tenants, or restore from a previous export.
  Replaces the entire language doc on save.
- **Export `<lang>.json`** — download your current knowledge as a
  versioned JSON file. Use as a backup before a big edit, or to take
  your data off the platform if you ever leave.
- **Roll back last save** — restore the document to the state right
  before your most recent upload. One level of undo, no second-level
  history.

The exported format:

```json
{
  "version": 1,
  "exportedAt": "2026-05-27T08:30:00Z",
  "language": "en",
  "document": { /* the KnowledgeDocument */ }
}
```

### Knowledge token cap + tiers

The bot loads your knowledge into its working memory on every chat,
so we cap the size to keep replies fast + costs predictable. The cap
is **per language** — a trilingual tenant gets 3× the per-language
cap of total content because each chat only loads the matching
language.

| Tier | Per-language cap | Add-on price |
|---|---|---|
| **Standard** (included) | 10,000 tokens | — |
| **Plus** | 25,000 tokens | +RM 20/month |
| **Pro** | 50,000 tokens | +RM 50/month |

The token meter on `/console/knowledge` shows your current usage per
language. At 80% it turns amber; at 100% new saves (manual, PDF
ingest, JSON import) are rejected until you trim or upgrade.

Upgrade on `/console/credits` → **Knowledge tier** card. Add-ons are
**line items on your existing base subscription** — you keep getting
**one invoice, one charge per month**, just with the add-on amount
added. Stripe charges a prorated amount immediately for the
remainder of your current billing period, then the full add-on on
every cycle after that.

Removing an add-on: click **Remove add-on** on the current tier card.
Stripe credits the unused portion of the current period to your next
invoice. Your knowledge stays where it is — only new saves above the
Standard cap (10,000 tokens) are blocked.

Upgrading from Plus to Pro: the Plus item is removed and Pro added
in the same flow — net result is one ongoing add-on, prorated
adjustment on the next invoice.

### Smart starter questions

When a visitor opens the chat panel with no history, the bot shows up
to **3 clickable starter questions** to break the ice (e.g. *"What are
your pricing tiers?"*, *"Do you serve KL?"*, *"What are your hours?"*).
Visitors who click a starter convert ~30-50% more often than ones who
type from scratch — the pill removes the "what do I ask?" friction.

The **Suggested questions** panel at the bottom of `/console/knowledge`
lets you manage them three ways:

- **Auto-generated** (default) — when you first save your knowledge,
  the AI generates 3 starters from your services, pricing and FAQ. A
  background sweep also fills in any tenant whose pills are empty
  but who has knowledge to read from.
- **Manual edit** — type your own three questions (each up to 80
  characters). Empty inputs are simply skipped, so you can show 1, 2
  or 3 pills depending on how many you fill in. Click **Save changes**
  to persist.
- **Replace with AI suggestions** — overwrite the inputs with three
  fresh AI-generated ideas based on your current knowledge. Clicking
  this also flips the master toggle on (the action implies intent).

The panel also has a **"Show suggested questions"** master toggle. When
off, visitors see an empty chat input instead of any pills — useful if
you prefer a minimal "ask me anything" feel or your visitors are
sophisticated enough to skip the priming.

A small **Preview** strip under the inputs shows exactly how visitors
will see the pills, so you don't need to test in the widget to check
tone or length.

### Knowledge gaps — the self-learning loop

Most chatbot products are **set-and-forget**: you spend an hour writing
FAQs on day one, then never touch them. Six months later your bot is
stale and confused, and you can't tell why.

ObieChat flips that. Every week, the platform scans the conversations
where the bot **couldn't confidently answer** (it used hedge phrases
like *"I'm not sure"* or *"let me check"*, or the visitor gave a 👎),
clusters them into topics, and **drafts a candidate FAQ Q+A** for each
topic — in your tenant's primary language, grounded in your existing
knowledge tone. You review at **Console → Knowledge → Knowledge gaps**
(`/console/knowledge/gaps`).

A typical entry looks like:

> **East Malaysia shipping** — *asked 4×*  *👎 2*
>
> **Q:** Do you deliver outside KL / Selangor?
> **A:** Yes — nationwide via Pos Laju. East Malaysia takes 3–5 working
> days; West Malaysia 1–2 days. Add RM 8 surcharge for Sabah / Sarawak.
>
> [ Approve & add to knowledge ]  [ Edit first ]  [ Dismiss ]

You can edit the question and answer before approving — the draft is a
starting point, not a finished product. Approving appends the FAQ to
your knowledge document; the very next chat starts answering Sabah
delivery questions confidently.

**How detection works.**
- Hedge phrases in the bot reply (`"I'm not sure"`, `"let me check"`,
  `"I don't have that info"`, …).
- Visitor thumbs-down on the bot reply.
- Either signal makes the preceding visitor question a candidate.

**How clustering works.**
- One AI pass per tenant per week. It groups similar questions
  together (so *"deliver to Sabah?"*, *"east malaysia shipping?"*, and
  *"send to KK?"* become one cluster, not three rows).
- It **skips** topics already covered by your existing FAQs, so you
  don't get noise from things the bot already handles.
- A maximum of 5 clusters per week — actionable, not overwhelming.

**Why it matters.** Five minutes a week, your bot gets measurably
smarter. The AI compounds your knowledge base from real customer
language, not your best guesses on day one. Most owners say this is
the moment ObieChat justifies the subscription.

The Monday morning digest email links straight to the gaps page when
there are new suggestions waiting — so you don't have to remember to
check.

---

## Settings

Open **Console → Settings**. Every field below is documented in the
order you'll see it.

<a id="settings-business"></a>

### Business

| Field | What it does |
|---|---|
| **Business name** | Shown in the chat header and used everywhere the bot refers to "us". |
| **Owner name** | If set, the bot mentions you by name (e.g. *"Adrian will follow up on WhatsApp"*). Leave blank to use generic phrasing. |
| **Service area / region** | E.g. *"Kuala Lumpur & Selangor"*. Helps the bot answer geographical questions. |

<a id="settings-contact"></a>

### Contact

| Field | What it does |
|---|---|
| **Preferred contact channel** | Pick one of **WhatsApp** (default), **SMS / iMessage**, **Telegram**, **Signal**, **Viber**, or **Phone call**. Drives the brand icon + CTA copy in the chat header, the "Open" buttons on each lead row, and the link the bot uses when offering a direct contact in a reply. |
| **Contact number** | Digits-only with country code (e.g. `+60123456789`). The chosen channel above is what opens when the visitor taps the icon — `wa.me/<number>` for WhatsApp, `sms:+<number>` for SMS, `tel:+<number>` for Call, etc. |
| **Lead email** | Where new lead notifications are sent. Defaults to your account email. |
| **Booking link** | Cal.com or Calendly URL. When set, the bot can offer **inline booking** inside the chat — visitor picks a slot without leaving the conversation. See [Inline booking](#booking). |

> **About each channel.** WhatsApp covers most of Asia, Latin America,
> Europe and MENA. SMS / iMessage is the universal fallback (best in
> the US). Telegram fits Russia / CIS. Signal fits privacy-conscious
> audiences. Viber serves Eastern Europe and the Philippines. **Phone
> call** opens the visitor's dialler directly — usually highest-intent,
> ideal for service trades (plumbers, lawyers, consultants).
>
> Channels that need a username instead of a phone number (LINE,
> KakaoTalk, WeChat, Facebook Messenger) aren't supported in v1.

<a id="settings-widget-appearance"></a>

### Widget appearance

| Field | What it does |
|---|---|
| **Brand colour (hex)** | The chat header colour. Defaults to ObieChat navy `#0c1e3a`. |
| **Widget position** | `bottom-right` (default) or `bottom-left`. |
| **Greeting** | Optional first message shown when the chat opens (e.g. *"Hi! How can we help today?"*). Leave blank for a generic greeting. |

<a id="settings-languages"></a>

### Languages

Check the languages your bot should respond in. Ten supported:

**Malaysia / SEA**
- **English** — default
- **Bahasa Malaysia** — Malaysian Malay (distinct from Indonesian)
- **Bahasa Indonesia** — distinct anchor words from Malay
- **Chinese (Simplified)** — standard written Chinese
- **Tamil** — Malaysian Tamil

**Global**
- **Spanish** — covers Latin America + Spain + US Hispanic
- **Portuguese (Brazil)** — Brazil's WhatsApp-dominant market
- **French** — France + Quebec + francophone Africa
- **German** — DACH region
- **Arabic** — MENA, with right-to-left widget direction handled
  automatically (`dir="rtl"` flips the panel when an Arabic-speaking
  visitor lands)

The bot **detects the visitor's language automatically** from their
browser locale + first message, and replies in the matching language.
If a visitor's language isn't in your enabled list, the widget UI
strings fall back to English (the bot's own replies may still come
back in the visitor's language — the underlying AI is fluent in 100+ languages
regardless of UI configuration, so the widget chrome and the bot
text can occasionally mismatch in fallback cases).

> **Pro tip:** Enabling Bahasa Malaysia adds explicit anchor-word
> guidance so the bot stays in Malay (not Indonesian) — *sesuai* vs
> *cocok*, *boleh* vs *bisa*, *bisnes* vs *bisnis*. This matters for
> credibility with Malaysian visitors.

**Authoring per-language knowledge.** The bot will *translate* your
primary-language knowledge on the fly for visitors in other languages
— good for casual use, but the phrasing isn't always how a native
speaker would say it. For native-quality replies, open
**Console → Knowledge** and you'll see a language tab per enabled
language. The primary tab is where you write the original; secondary
tabs have a **Generate translation from primary** button that drafts
the parallel doc using AI, in your tenant's tone. You
review the draft, edit anything that feels off, then Save. From that
point on, the bot reads the language-specific doc directly when a
visitor in that language chats — no live translation.

When you edit the primary doc again, secondary tabs get a small
*Stale* badge until you regenerate (or hand-edit) them — so your
translations don't silently drift behind the source of truth.

<a id="settings-lead-capture"></a>

### Lead capture behaviour

| Field | What it does |
|---|---|
| **Phone-first** | Default **ON**. The bot asks for a WhatsApp number BEFORE answering pricing/availability questions. Off = bot answers freely, captures contact only when offered. |

Turn phone-first OFF if you want a low-friction info chatbot that
converts to leads later (e.g. content sites, top-of-funnel marketing).
Keep it ON if you're capturing sales-qualified leads.

<a id="settings-booking"></a>

### Booking (Cal.com / Calendly)

Setting your **Booking link** (under Contact above) does two things:

1. **Inline booking in chat.** When a visitor asks about scheduling
   ("can we have a call?", "when can I come in?"), the bot renders the
   booking widget INSIDE the chat. Visitor picks a slot, gets a
   confirmation email from Cal.com / Calendly directly.
2. **Bot mentions booking** when relevant ("would you like to book a
   time? I can show open slots right now").

Supported providers (v1): **Cal.com**, **Calendly**.

How to get a URL:

- **Cal.com**: free at <https://cal.com>. Create an event type
  ("15-min discovery call"), copy the public URL
  (e.g. `https://cal.com/your-handle/15min`).
- **Calendly**: free tier at <https://calendly.com>. Create an event
  type, copy the public URL.

Both providers' default public URLs work as embeds — no SDK, no API
key. Booking confirmation emails are sent by them, not us.

<a id="settings-webhooks"></a>

### Webhooks (Zapier / Make / n8n / custom)

When set, every **qualified lead** is POSTed as JSON to your webhook
URL. Useful for:

- Auto-creating contacts in your CRM (HubSpot, Pipedrive, Zoho)
- Sending Slack / Telegram / Discord notifications
- Triggering email sequences
- Custom workflow automation (Zapier, Make, n8n)

#### How it works

1. Paste a URL in **Settings → Webhook URL** (must be `https://`).
2. Save. We **auto-generate a signing secret** (160-bit hex).
3. The signing secret is shown read-only below the URL field. Copy it.
4. Set up your receiver to verify the `x-obiechat-signature` header
   (HMAC-SHA256 of the raw request body using your secret).
5. From now on, every qualified lead fires a POST request to your URL.

#### Payload format

```json
{
  "event": "lead.captured",
  "occurredAt": "2026-05-24T08:14:23.123Z",
  "tenantId": "abc-uuid-...",
  "siteKey": "my-shop",
  "data": {
    "leadId": "def-uuid-...",
    "name": "Adrian",
    "phone": "+60123456789",
    "email": null,
    "business": null,
    "enquiry": "Asking about monthly IT support",
    "capturedAt": "2026-05-24T08:14:22.456Z",
    "sessionId": "...",
    "consoleUrl": "https://obiechat.obieonline.com/console/leads/def-uuid-..."
  }
}
```

#### Headers

| Header | Value |
|---|---|
| `Content-Type` | `application/json` |
| `x-obiechat-signature` | HMAC-SHA256 of body, hex |
| `x-obiechat-event` | `lead.captured` |
| `x-obiechat-delivery` | Unique delivery id (use for idempotency) |
| `User-Agent` | `ObieChat-Webhook/1.0` |

#### Retries

If your receiver returns non-2xx, we retry with **exponential backoff**:
1 minute, then 5 minutes, then 30 minutes. After all 4 attempts (initial
+ 3 retries) we give up. 4xx responses (except 429) skip retries
immediately — they signal a permanent caller-side error.

#### Verifying the signature (Node.js example)

```js
import { createHmac, timingSafeEqual } from "node:crypto";

function verify(body, header, secret) {
  const expected = createHmac("sha256", secret).update(body).digest("hex");
  const a = Buffer.from(header);
  const b = Buffer.from(expected);
  return a.length === b.length && timingSafeEqual(a, b);
}
```

> **Important:** Use the **raw body bytes**, not the parsed JSON. JSON
> re-serialisation reorders keys → signature mismatch.

#### Testing without a real backend

Use <https://webhook.site> — paste their URL in Settings, capture a
test lead, see the payload + headers within seconds.

<a id="settings-allowed-websites"></a>

### Allowed websites

Comma- or newline-separated list of origins where the widget is
allowed to load. Examples:

```
https://obieonline.com
https://www.obieonline.com
https://staging.obieonline.com
```

**Empty list = block-all** (after your first save). When empty AND your
tenant is less than 60 seconds old (just-signed-up), the widget loads
anywhere — a grace window so you can test on localhost before
configuring origins.

> **Important:** Always add **both** the `www.` and non-`www.` versions
> of your domain. Browsers treat them as separate origins.

<a id="settings-escalation-rules"></a>

### Escalation rules

Controls when the bot wakes you up. Set business hours, a score
threshold, what happens after hours, and keywords that always ping
you regardless of time or score.

**Business hours.** Two dropdowns (start hour, end hour) and a set
of weekday checkboxes. Defaults to Mon–Fri 09:00–18:00 in your
configured timezone. End hour is *exclusive* — `09–18` means
09:00–17:59 is in hours, 18:00 starts after-hours. The window can
wrap midnight (e.g. `22–06` for a late-night business). An empty
weekdays array means every day is treated as after-hours.

**Escalation threshold (0–100).** The 0–100 score the bot assigns
to each conversation (see [Escalation alerts](#live-escalation)).
We push when the score meets or exceeds this number. Default
**60**. Raise it for fewer alerts; lower it to catch more.

**After-hours behaviour.** What we do with an alert that would
fire outside business hours:

| Option | What happens |
|---|---|
| **Push anyway** | The phone rings 24/7. The pre-2026-05-27 default. |
| **Queue for morning digest** (recommended) | Hold the alert until the next 08:00 digest email. You sleep, the bot remembers. **This is the default.** |
| **Mute outside hours** | No alert at all. The conversation is still saved; you'll see it in `/console/conversations`. |

**Always-push keywords.** Free-form list (one pattern per line) of
words that *always* ping you immediately — regardless of time,
score, or after-hours setting. Uses **SQL-LIKE syntax** so it's
predictable:

| Pattern | What it matches | Example |
|---|---|---|
| `cat` | the word "cat" exactly | "the cat sat" ✓ &nbsp; "catalog" ✗ |
| `cat%` | words starting with cat | "catalog" ✓ &nbsp; "category" ✓ &nbsp; "scatter" ✗ |
| `%cat` | words ending with cat | "wildcat" ✓ &nbsp; "bobcat" ✓ &nbsp; "catalog" ✗ |
| `%cat%` | anywhere | "catalog" ✓ &nbsp; "scatter" ✓ &nbsp; "vacate" ✓ |

Case-insensitive. Patterns with embedded `%` or regex
metacharacters are rejected on save. Reasonable starting set for
most businesses:

```
price%
%urgent%
cancel
refund
```

> **Why this matters.** Before 2026-05-27, every conversation
> crossing score 60 fired a push 24/7. After: outside business
> hours, alerts queue overnight by default. Existing tenants were
> migrated to the new default with a one-time banner on the
> console dashboard. Want the old behaviour? Flip *After-hours
> behaviour* to **Push anyway** here.

<a id="settings-analytics"></a>

### Analytics opt-in

Default **OFF**. When OFF, the Insights page only shows numbers
derived from real chats and captured leads — no separate tracking
pixel, no events fired on widget load.

When ON, the widget also fires two pre-chat events:
- **Loaded** — widget JS booted on the page (visitor saw the bubble).
- **Opened** — visitor clicked the bubble (engagement intent).

Together with the chat / lead data we already collect, this gives you
a four-stage funnel visible at `/console/insights`:

| Stage | Defined as | Source |
|---|---|---|
| Loaded | Sessions where the widget JS ran | `widget_events` |
| Opened | Sessions where the bubble was clicked | `widget_events` |
| Messaged | Sessions where the visitor sent at least one message | `conversations` |
| Captured | Sessions where a qualified lead was created | `leads` |

Each stage is counted as **distinct browser session**, so a refresh by
the same visitor doesn't double-count. The funnel card shows counts +
a "% of previous stage" conversion figure per row, plus visual bars
normalised to the Loaded count so it looks like a funnel.

> **Privacy.** Pre-chat events store only the tenant ID, session ID,
> event type, and timestamp. **No IP, no user agent, no page URL, no
> personally identifying data** is captured. The widget uses
> `navigator.sendBeacon` for the `loaded` event so it survives an
> instant tab-close — which means the event fires even if the visitor
> bounces immediately. If you turn the toggle off later, we stop
> capturing immediately; old rows stay until you delete them or your
> account is closed.

Analytics are tenant-private — RLS-isolated like every other row.

---

## Leads

<a id="leads-list"></a>

### The leads list

Open **Console → Leads**. Every visitor who shared a contact detail
(phone or email) lands here.

Columns:

| Column | Meaning |
|---|---|
| **Heat** | AI-scored 1-5 🔥 (`🔥🔥🔥🔥🔥` = urgent hot lead). Empty until the scoring pass completes (~5 seconds after capture). |
| **When** | Capture timestamp (local time). |
| **Name** | If the visitor shared it. |
| **Phone** | Click to open WhatsApp (`wa.me/...`). |
| **Email** | Click to open mail client. |
| **Enquiry / AI summary** | The AI's 1-line summary of what the lead wants (falls back to raw enquiry until the score lands). |
| **Status** | `Emailed ✓` (we sent you the alert), `Qualified` (passed the spam filter), or `Spam-flagged` (looks like spam — saved but no email). |

<a id="leads-ai-triage"></a>

### AI triage

Every fresh lead is **scored by AI** within ~5 seconds.
You get:

- **Heat (1-5)** — how urgent the owner should call back
- **Summary** — 1-line explanation of what they want, urgency, fit
- **Suggested WhatsApp reply** — copy-paste-ready, addresses them by
  name, refers specifically to what they asked about

Open any lead to see the **AI triage panel**:

- 🔥 meter at the top
- Summary block
- Suggested reply with a **Copy** button (1.5s flash on success)

> **The point:** owners drown in low-quality leads. The AI tells you
> *"Lead #4 is hottest — buying intent: high, ready this week"* so you
> can spend your 5 minutes on the right lead. Score is based on intent
> language, urgency signals, fit to your services, and contact quality.

<a id="leads-detail"></a>

### Lead detail + transcript

Click any lead to see:

- **AI triage** (above)
- **Details** — name, phone (WhatsApp link), email, business, enquiry,
  qualified yes/no, emailed yes/no
- **Full transcript** — every visitor and bot turn. Includes
  **inline image thumbnails** if the visitor uploaded photos. Click an
  image to open full-size.
- **Delete lead** button (top right) — soft-deletes; consult PDPA
  exports first if you need a copy.

<a id="leads-filter-export"></a>

### Tracking lead outcomes

Every captured lead has an **Outcome** chip — set it from the lead
detail page (Console → Leads → click a row). Four states:

| Status | When to use |
|---|---|
| **New** | Default — haven't followed up yet. |
| **Contacted** | Reached out (WhatsApp, call, email) — no answer yet, or in conversation. |
| **Won** | Became a customer. You can optionally enter the deal value in RM — that number rolls into the monthly ROI email. |
| **Lost** | Not a fit, or didn't convert. |

The leads list shows the outcome badge in its own column, and a
new filter chip strip lets you slice by outcome ("show me everything
still New" / "show me last quarter's Won deals").

#### The monthly ROI email

On the **1st of every month at 08:00 in your local timezone**, you
get a one-screen ROI email aggregating last calendar month:

> 📊 *May 2026 ROI for ObieOnline*
> - **37** leads captured
> - **12** contacted
> - **8** won — ~**RM 12,400** in tracked pipeline
> - **3** lost

The "tracked pipeline" number comes from the deal values you recorded
on Won outcomes. Even if you don't enter values, the **count** of
wins still tells you whether the chatbot is paying for itself. Months
with zero captured leads are skipped silently — no sad empty emails.

If the cron is down on the 1st, the dispatcher catches up on the
next 08:00 firing within the month. Once per calendar month per
tenant, no duplicates.

### Filter + export

The leads list has a **filter bar** above the table:

- **Period** — last 7 / 30 / 90 days, or all time. Default 30d.
- **Status** — qualified (default), spam-flagged, or all.
- **Outcome** — any / new / contacted / won / lost.
- **Search** — free-text match across name, phone, email, business,
  enquiry. ILIKE.
- **Clear filters** when any are active.

The result line ("Showing X leads from the last 30 days") updates as
filters change.

#### CSV export

Top-right **Download CSV**. RFC-4180-compliant CSV with a UTF-8 BOM
(Excel + Numbers both open it cleanly). Columns: timestamp, name,
phone, email, business, enquiry, qualified.

---

## Live conversations

Open **Console → Live**. This is the real-time view of every active
chat happening on your site right now.

<a id="live-console"></a>

### The live console

Two sections:

- **Active now** — conversations with activity in the last 15 minutes.
- **Recent** — older conversations still on the visible list.

Each row shows:

- **State** — `AI handling` / `Paused` / `You replying`
- **Time** — how long since last message
- **Heat 🔥 NN** — if escalation score is ≥ 60 (the AI flagged it as
  needing attention)
- **Preview** — last message content
- **Escalation reason** — one-line AI explanation (e.g. *"Hot lead
  asking for discount"*)

Sorted by escalation score (high first), then by recency. Updates
**live via SSE** — no refresh needed.

<a id="live-takeover"></a>

### Taking over a conversation

Click any row to open the conversation detail view. You see:

- **State + takeover controls** at the top
- **Full message history** (auto-scrolls as new messages arrive)
- **Reply box** (appears when state is paused or human)

#### Takeover modes

| Mode | What happens |
|---|---|
| **Take over (Pause)** | Bot stops sending replies for this conversation. Visitor sees a "Live" badge in the chat header. Your replies arrive in their chat instantly. |
| **AI off completely** | Bot stays out entirely for this conversation. Use when you want full manual control. |
| **Hand back to AI** | Bot resumes answering. State returns to `bot`. |

#### Sending a reply

1. Click **Take over**.
2. Type in the reply box.
3. **Enter** to send (Shift+Enter for newline).

Your reply appears in the visitor's chat **within ~1 second** via the
visitor-side SSE stream. Look for a green "Team" bubble in their
widget.

<a id="live-escalation"></a>

### Escalation alerts

After every bot reply, a cheap AI pass scores the
conversation **0-100** on urgency:

| Score | Meaning |
|---|---|
| 0-20 | Routine — bot handled it fine |
| 21-50 | Mild intent (lead-shaped) |
| 51-75 | High buying intent OR complaint OR confusion |
| 76-100 | URGENT — angry, dealbreaker, or bot mishandled |

When a conversation crosses your **escalation threshold** (default
60), the rule engine decides what to do based on your
[Escalation rules](#settings-escalation-rules) — push immediately,
queue for the next morning digest, or stay silent. The push opens
directly into that conversation's detail view, so you can take over
in two taps.

By default, alerts queue overnight and arrive bundled in your 8 am
digest email. If you want to be pinged 24/7 (the old behaviour),
switch *After-hours behaviour* to **Push anyway** in Settings.

---

<a id="vision"></a>

## Vision Q&A — visitors share photos

Visitors can attach photos to chat messages (📎 icon in the composer).
The bot **sees the photo** and answers based on it + your knowledge.

### What it works for

- *"What's wrong with this screen?"* (photo of an error)
- *"How much for a repair like this?"* (photo of a damaged item)
- *"Do you stock this part?"* (photo of a product)
- *"Can you make a cake like this?"* (photo from Pinterest)

### What it doesn't do

- **It does NOT invent prices.** If your knowledge base doesn't have a
  matching service/tier, the bot won't guess. It describes what it sees
  in objective terms and captures a rich-context lead so you can quote
  on the move.
- **It doesn't read text inside images for translation purposes alone.**
  It uses the image as context for the visitor's question.

### Limits + cost

- Max **5 MB per image**, up to **4 images per message**.
- Formats: JPEG, PNG, WebP, GIF.
- **2 credits per image-bearing message** (vs 1 for text-only).
- Images stored on our server **for 30 days** then auto-deleted.

### Where the photo shows up later

In the lead transcript on the lead detail page, with click-to-open
full-size. The owner sees exactly what the bot saw.

---

<a id="booking"></a>

## Inline booking — Cal.com / Calendly

When a visitor asks to book/schedule, the bot embeds your booking
calendar **inside the chat bubble**. Visitor picks a slot, gets the
confirmation email from Cal/Calendly directly.

**Setup:** Settings → Contact → "Booking link" → paste your Cal.com or
Calendly public URL → Save.

**Triggers:** the bot offers booking when the visitor uses words like
*book*, *schedule*, *meet*, *appointment*, *consultation*, *viewing*,
*call back*.

**Booking outcome:** Cal/Calendly emails you and the visitor directly.
We do NOT currently track booking confirmations server-side (Cal.com
webhooks are a future addition).

---

<a id="pwa"></a>

## Mobile install + push notifications

ObieChat console is a **PWA (Progressive Web App)** — you can install
it on your phone like a native app.

### Install on Android (Chrome)

1. Open <https://obiechat.obieonline.com/console/live> on your phone.
2. Tap the **⋮ menu** → **Add to home screen** (sometimes labelled
   *Install app*).
3. Confirm.

An ObieChat icon appears on your home screen. Tap to open — runs
full-screen, no browser chrome.

### Install on iOS (Safari, 16.4+)

1. Open the console in Safari.
2. Tap the **Share button** → **Add to Home Screen**.
3. Confirm.

iOS PWA push notifications require iOS 16.4 or newer.

### Enable push notifications

After installing (or even just in the browser):

1. Open **Console → Live**.
2. Click the **"Enable"** button on the push notifications card.
3. Browser asks for notification permission → click **Allow**.

That's it. From now on, any conversation that scores ≥ 60 on
escalation triggers a push to your device. Tapping the notification
opens directly into that conversation.

#### What push notifications look like

> **🔥 Hot conversation**
> *Hot lead asking for discount on Business Care*

Tap → opens `/console/conversations/[id]` → you can take over with one
more tap.

#### Disabling

Two ways:

1. **Per-device** — browser settings → block notifications from
   obiechat.obieonline.com.
2. **Per-account** — coming later (per-tenant opt-out in settings).

---

<a id="daily-digest"></a>

## Daily digest email

Every morning at **08:00 your local time** (default: Asia/Kuala_Lumpur,
configurable per-tenant), you get a 60-second email summary:

> **🌅 Yesterday's bot: 4 leads, 23 chats — ObieOnline**
>
> Good morning! Here's how your chatbot did yesterday:
>
> - **23** conversations
> - **4** leads captured — 🔥 1 hot
>
> **Top questions visitors asked:**
> - Pricing for monthly IT support
> - Do you serve KL?
> - What are your business hours?
>
> **📚 Knowledge gaps to fix:**
> - Parking — 5 visitors asked, your bot didn't know
>
> [Open the console →]

### Knowledge gaps in the digest

The digest has two related signals:

- **Yesterday's hint** (📚 *Knowledge gaps to fix*) — a daily AI
  summary of topics that looked under-served in yesterday's chats.
  Quick situational awareness, no action required.
- **Drafted answers ready to review** (🧠 *X drafted answers waiting*)
  — link to `/console/knowledge/gaps`. These are the persistent,
  AI-clustered + AI-drafted FAQ suggestions from the weekly self-
  learning loop. Approve them in one click and the bot gets smarter.

The second one is the killer feature — see [Knowledge gaps — the
self-learning loop](#knowledge-base) above.

### Opting out

The digest is on by default. To turn it off, send us a WhatsApp message
(opt-out toggle in the settings UI is coming soon).

### If you're not receiving the digest

1. **Check spam.** First-send digests from new tenants sometimes land
   in Gmail's Promotions / spam tab. Mark as "Not Spam" so future ones
   land in Inbox.
2. **Verify your Lead email is deliverable.** Settings → Contact →
   Lead email. If that mailbox doesn't actually receive mail (e.g. a
   vanity alias on a domain without MX records, or a typo), the digest
   will bounce silently. Resend marks the message as "tried" so we
   don't retry-storm. Change the Lead email to a confirmed deliverable
   inbox and the next morning's digest should arrive.
3. **Subject line filter.** The digest subject is *"🌅 Yesterday's bot:
   N leads, N chats"*. Search your inbox for the rocket emoji — if you
   find it filed somewhere, fix the filter.

---

<a id="insights"></a>

## Insights

Open **Console → Insights** for at-a-glance stats:

- **Chats in the last 7 / 30 days**
- **Leads in the last 7 / 30 days** (with the spam-flagged delta)
- **Lead-conversion rate** — of chats that consumed a credit, what %
  captured a qualified lead
- **Visitor satisfaction** — 👍 vs 👎 ratings on bot replies, plus
  satisfaction rate

Use the 👎 count to spot patterns in replies that aren't working — the
cheapest improvement signal you'll get.

---

<a id="billing"></a>

## Credits + billing

We bill in **credits** but talk to you in **conversations** —
because every conversation has a different shape:

- **1 credit** per visitor text message
- **2 credits** per visitor message with image(s) (vision is heavier)
- **A typical 5-message conversation** ≈ **5 credits**

So when we say "Growth includes ~300 conversations," we mean *about
1,500 credits* at the typical 5-message conversation. Some are
shorter (visitor bounces after greeting), some longer (deep lead
capture). The bot's replies are never charged.

### Tiers

| Tier | Price | Credits | ≈ Conversations | Knowledge cap |
|---|---|---|---|---|
| **Trial** | Free 30 days | 50 (one-off) | ~10 | 10K |
| **Starter** | RM 99/mo | 500 / month | ~100 | 10K |
| **Growth** ⭐ | RM 249/mo | 1,500 / month | ~300 | 25K |
| **Business** | RM 499/mo | 3,500 / month | ~700 | 50K |
| **Lapsed** | (paused) | 0 | — | — |

⭐ Growth is the most popular tier. It's the sweet spot for active
SMEs with multi-service offerings.

The trial is a **30-day window**, not a recurring tier — your 50
free credits are granted once at signup and don't refresh. You can
top up during the demo to extend usage; any unused top-up balance
carries forward when you subscribe.

You'll see a countdown banner on the Credits page during the demo,
plus a "demo ends tomorrow" email the day before expiry.

### Which tier should I pick?

Rough rule of thumb based on weekly visitor chat traffic:

| Weekly chats | Monthly conversations | Recommended tier |
|---|---|---|
| < 25 | < 100 | **Starter** |
| 25 – 75 | 100 – 300 | **Growth** |
| 75+ | 300+ | **Business** |

If you're brand new with no traffic data, start with Starter and
watch your usage on `/console/credits` for the first month. Upgrading
takes one click in `/console/credits` → **Manage your plan**, and
Stripe prorates so you only pay the difference for the remainder of
the current billing period.

### Need more knowledge?

Knowledge size is bundled with each tier — 10K for Starter, 25K for
Growth, 50K for Business. If you need a larger knowledge base, upgrade
to the next tier. This keeps the pricing simple: more conversations
*and* more knowledge in one move.

### Top-ups

Top-ups extend usage when you exceed your monthly allowance. Buy in
packs of **RM 30 per 100 credits** — minimum 1 pack (RM 30), maximum
100 packs (RM 3,000). All grants are valid 12 months.

| You pay | Credits granted | ≈ Conversations |
|---|---|---|
| RM 30 | 100 | ~20 |
| RM 90 | 300 | ~60 |
| RM 150 | 500 | ~100 |
| RM 300 | 1,000 | ~200 |

**Why top-ups are pricier per credit than subscriptions.** Top-ups
are RM 0.30/credit vs Growth's RM 0.17/credit. That's intentional:
top-ups are *emergency capacity* for when you forgot to upgrade and
need credits NOW. If you find yourself topping up regularly, it's
almost always cheaper to move up a tier:

| Monthly need | Cheapest path |
|---|---|
| 500 credits | Starter (no top-ups) |
| 800 credits | Starter + 3 top-ups (~RM 189) OR Growth (RM 249 — close call) |
| 1,500 credits | **Growth** wins (RM 249 vs Starter+10 top-ups = RM 399) |
| 3,500 credits | **Business** wins (RM 499 vs Growth+20 top-ups = RM 849) |

Credits roll over month-to-month within their 12-month TTL.
Consumption is **FIFO**: monthly credits first, then top-up grants
in order of earliest expiry.

### Lifecycle

- **Trial → Paid (any tier)**: click **Upgrade** on the Credits page →
  choose Starter / Growth / Business → complete checkout via Stripe.
  Your tenant flips to Paid within seconds and the monthly cycle
  starts from that date.
- **Trial → Lapsed (auto)**: 30 days after signup, the chatbot
  pauses if you haven't subscribed. You'll get an email the day
  before and on the day of pausing.
- **Tier change (any direction)**: in the Customer Portal (**Manage
  billing**), switch plans. Stripe prorates — you pay the difference
  for the rest of the current period.
- **Paid → cancelling**: in the Customer Portal, click Cancel. You
  keep service through the current period end (the date is shown on
  your Credits page as a banner). At period end, Stripe stops billing.
- **Cancelled → Lapsed**: at period end the bot stops responding
  (visitors see *"This chatbot is temporarily unavailable"*).
  Knowledge, leads, and unused top-up credits are kept; nothing
  deleted.
- **Lapsed → Paid**: click **Subscribe** again on the Credits page.
  Stripe Checkout re-runs and your tenant flips back; preserved
  top-up balance becomes spendable again immediately.

### Where to see credits

**Console → Credits** shows:

- Monthly bucket: used / included / remaining
- Active top-ups: each grant, credits remaining, expiry
- Last 50 credit events (consume, grant, expire, adjust)

---

<a id="refunds"></a>

## Refunds

ObieChat refunds are designed to be fair to both sides — service we
delivered we keep, service we haven't we return.

### Subscription (Starter / Growth / Business)

No refunds on subscription charges. Cancel anytime via **Manage
billing → Cancel subscription** and you keep service through the
current period end (the date is shown on your Credits page).
Cancellation stops future charges but doesn't refund the month in
progress, because monthly credits are use-or-lose and don't roll over
— a pro-rata refund would just be a free trial.

Switching tier mid-cycle (e.g. Starter → Growth) is handled by
Stripe's automatic proration: you pay the difference for the rest of
the current period; downgrades credit the unused portion to your
next invoice.

### Top-ups (minimum RM 30, RM 30 per 100 credits)

**Unused-balance refunds only.** We refund the remaining credit
balance from your top-up grants, at the original price-per-credit
(RM 0.30 since the 2026-05-28 reform; older RM 0.10 grants are
refunded at their historical rate).

**Formula:** `refund = (remaining_credits / credits_originally_purchased) × amount_paid`

**Example:** you bought 100 credits for RM 30 (RM 0.30/credit). You
used 40, 60 remain → refund **RM 18.00**.

Top-ups no longer come with a volume bonus. Each pack is exactly
100 credits regardless of how many you buy.

### First-purchase goodwill window

Within **7 days of your first-ever top-up**, we'll process the
unused-balance refund without asking why. After 7 days, or on
subsequent top-ups, refunds remain available at our discretion — in
practice we approve fair requests.

The goodwill window applies to top-ups only, not subscriptions.

### How to request a refund

WhatsApp [+60 19-313 9312](https://wa.me/60193139312) or email
[obie@obieonline.com](mailto:obie@obieonline.com) with your business
name and a brief note. Most refunds are processed within 2 business
days. Stripe takes a further 5-10 business days to return the funds
to your card. Stripe's original processing fee is non-refundable — we
absorb that on our side.

### Chargebacks

If you dispute a charge with your bank instead of contacting us, we'll
always cooperate with the bank's process, but we may pause your
account pending investigation and you'll incur Stripe's RM 60 dispute
fee. **Talk to us first** — we don't say no to fair refund requests.

---

<a id="privacy"></a>

## Privacy + data export

### What we store

- **Tenant data** (your business): profile, knowledge, settings,
  webhook secret. Stored on our managed PostgreSQL, RLS-isolated per
  tenant — no other ObieChat customer can ever see your data.
- **Lead data** (your visitors): name, phone, email, business,
  enquiry, full transcript, IP at capture. Same RLS isolation.
- **Visitor images**: stored on disk under our Docker volume, deleted
  after **30 days**.
- **Conversations + messages**: every chat is logged for the live
  console + escalation detection.

### What we don't store

- Cookies in the visitor's browser (we use `sessionStorage` — clears
  when the visitor closes the tab).
- Any tracking that survives across sites.
- Visitor PII beyond what they explicitly typed.

### Visitor consent

The widget footer shows a one-line consent string:

> *"By chatting, you agree contact details you share are sent to
> [Your Business] for follow-up."*

Translated to the visitor's language. Compliant with PDPA / GDPR
"contextual consent" standards.

### Data retention

| Data | Retention |
|---|---|
| Leads (incl. transcript) | **365 days** (Privacy Policy default) |
| Visitor images | **30 days** |
| Credit events | Kept forever (financial audit) |
| Admin audit log | Kept forever |
| Push subscriptions | Until you revoke / browser expires |

### Right to be forgotten

For an individual visitor: open their lead → **Delete lead**. This
removes the row + transcript. Their images expire on the regular
30-day cycle.

For your entire tenant: contact us — we'll archive (soft-delete) the
tenant. You can re-activate later.

### PDPA / GDPR data export

**Console → Settings → "Export all your data"**.

Downloads a complete JSON archive:

- Tenant settings
- Knowledge base
- Every lead (with full transcript)
- Top-up grants + credit events

Use this for off-platform backups or to take your data with you if
you ever leave.

---

<a id="faq"></a>

## FAQ

### How do I add the widget to WordPress?

See [Installing the widget → WordPress](#wordpress-plugin). Easiest:
install our official WordPress plugin and paste your site key.

### What's a credit?

One bot reply = 1 credit (2 if the visitor's message had an image).
Visitor messages don't cost credits — only **outgoing** bot replies.
Failed/refunded chats are returned to your bucket automatically.

### Does the bot speak proper Bahasa Malaysia?

Yes — distinct from Indonesian. The system prompt explicitly teaches
the model anchor words like *sesuai* (not *cocok*), *boleh* (not
*bisa*), *bisnes* (not *bisnis*). Enable it under
**Settings → Languages**.

### How accurate is Vision?

The model identifies objects, brands, text in images, layouts,
conditions reliably. It does **not** invent prices — only matches photos
to services/tiers you've documented in your knowledge base. If no match,
it captures a rich-context lead instead.

### Can I customise the bot's tone?

Tone is currently: *warm, concise, plain-spoken, 2-4 sentences*. You
shape it indirectly via your knowledge base — formal pricing language
leads to formal bot replies; casual taglines lead to casual replies.
Per-tenant tone overrides are on the roadmap.

### What if the bot gives wrong info?

1. Check the **Evidence panel** ("Why I said this" expander under each
   bot reply) — it shows which knowledge section the bot used. If the
   source is wrong, fix it in the knowledge base.
2. Use the **👎** rating below the reply. Future improvements use this
   signal.
3. For critical errors, contact us — we'll review the prompt for that
   case.

### Can the bot stay online when I sleep?

That's the whole point. The bot answers 24/7 from your knowledge base.
Real human takeover only happens when YOU choose to via the live
console. Visitors never see "we're closed".

### What happens to leads if my subscription lapses?

They're kept. Bot stops responding, but every captured lead remains in
the console + the data archive. Pay and the bot resumes; nothing lost.

### Can I run multiple sites on one ObieChat account?

Not currently — one tenant = one site key = one knowledge base.
Multi-site is on the roadmap. For now, each site needs its own ObieChat
account.

### Will the bot disclose it's an AI?

It doesn't volunteer the information, but it won't deny being an AI
either. The widget footer shows "Powered by ObieChat", which signals it.

### How do I update my widget without re-pasting the script?

You don't have to — `widget.js` is fetched from our CDN on every page
load. We push updates to all tenants simultaneously.

### Is there a self-hosted version?

Not currently. ObieChat is SaaS-only.

---

<a id="troubleshooting"></a>

## Troubleshooting

### The chat bubble doesn't appear on my site

1. Check the browser console for errors.
2. Verify the script tag has the right `data-site` key (matches
   Settings → Site key).
3. Verify your site's origin is in **Settings → Allowed websites**
   (both `www.` and non-`www.` versions).
4. Try in an incognito window — caching issues can hide a recent fix.

### Bot says "out of credits" or "temporarily unavailable"

Two scenarios:

- **Out of credits** — your monthly bucket + any top-ups are
  exhausted. **Console → Credits** shows what's left. Buy a top-up
  pack (RM 30 = 100 credits, valid 12 months) or upgrade to a higher
  tier — if you regularly need top-ups, the upgrade is cheaper per
  credit.
- **Demo paused** — your 30-day trial window has ended. The Credits
  page shows a "demo paused" banner; subscribe to resume
  immediately. Top-up balance and existing knowledge / leads are all
  preserved.

### Bot replies feel generic / wrong

Almost always a knowledge base issue. Open **Console → Knowledge** and
check:

- Are your services + descriptions filled in?
- Are pricing tiers concrete (not just "contact for quote")?
- Are there 3-5 FAQs for each service?

The bot is only as good as the knowledge you give it. Spend an hour on
the editor — bot quality improves dramatically.

### Push notifications don't fire

1. Check **Console → Live** → push card → "Enable" — did you allow?
2. Browser settings: notifications enabled for
   `obiechat.obieonline.com`?
3. iOS: are you on **16.4+** AND did you install the PWA (add to home
   screen) first? Web Push requires the PWA install on iOS.
4. Trigger a high-intent conversation to test (e.g. visitor says
   *"I want to buy now"*).
5. **Check your [escalation rules](#settings-escalation-rules).** By
   default, alerts outside business hours queue to the next morning
   digest instead of pinging your phone. If you expected an
   overnight ping, either flip *After-hours behaviour* to **Push
   anyway**, or add a keyword like `%urgent%` to *Always-push
   keywords* so high-priority messages bypass the queue.
6. Has the conversation crossed your *escalation threshold*
   (default 60)? Lower it on **Settings → Escalation rules** if the
   bot's scoring conservatively for your audience.

### Webhooks not firing

1. Verify the URL is `https://` and accepts POST.
2. Check the URL with <https://webhook.site> to confirm receivability.
3. Look at **Console → ?** logs (or contact us) for delivery attempts.
4. 4xx responses skip retries — fix your receiver to return 2xx.

### Lead emails not arriving

1. Check spam/junk.
2. Verify **Settings → Lead email** is correct.
3. Verify your domain isn't blocking Resend's sending domain
   (`obiechat.obieonline.com`).
4. Lead-emailed-yet flag visible on each lead row (✓ in old UI,
   "Emailed" status in current).

### "Connection problem (XXX). Please try again." in the widget

Usually a transient network issue. The error message contains the
underlying reason in parentheses. If it persists across multiple
attempts, contact us.

### Scanner finds 0 services / 0 pricing / 0 FAQs

Your site's content structure isn't discoverable. Try:

- Make sure key pages (about, services, pricing, contact) are linked
  from the homepage.
- Avoid heavy JS frameworks that render content after page load — the
  scanner reads server-rendered HTML.
- Fall back to the manual editor — usually 30 minutes of typing.

---

## Need help?

- **WhatsApp the founder**: see the WhatsApp icon on
  <https://obiechat.obieonline.com>
- **Email**: obie@obieonline.com (we strongly prefer WhatsApp — emails
  may take 24h+)
- **Found a bug?** WhatsApp us with screenshots + steps to reproduce.

---

*Last updated: May 2026. ObieChat v1.x. — Maintained by ObieOnline.*
