I
Local demo · sample data · no live services connected

Docs & Export

Reference documents only — no services or endpoints are deployed.

README.md

Overview and pages · access details omitted

BACKEND.md

Postgres schema & endpoint flow

n8n-workflow.json

n8n design example · requires separate setup

API-REFERENCE.md

Webhook & REST reference

Webhook endpoints

MethodPathDescription
GET/POST/webhook-receiverInbound messages from Meta, WhatsApp & website widget
POST/message-combineMerge threads of the same customer across channels
POST/ai-replyGenerate AI/keyword reply unless human control is on
POST/chat-transfer/initiateStart a transfer to another member
GET/chat-transfer/checkCurrent assignee & pending transfer
POST/chat-transfer/changeChange assignee immediately
GET/admin-takeover/checkIs a human in control?
POST/admin-takeover/startTurn human control on/off
GET/shift-schedule-checkOpen now? Who is on shift? Away message
POST/token-cost-countingLog tokens & cost per AI message

README.md

# Chatflow Commander — UST AI Control

A chatbot & AI-control platform for managing Facebook, Messenger, WhatsApp and website chats in one inbox.

This repository is a **frontend demo**: all data lives in your browser (localStorage). No login wall, no paid services, no external API calls.

## Demo access
- Open `/admin` or `/dashboard` — instant access.
- **Require confirmations** is OFF by default (Settings) — Resolve, Transfer, Delete act immediately.

## Pages
| Path | What it does |
|---|---|
| `/` | Landing page |
| `/dashboard`, `/admin` | Stats, charts, recent conversations, quick actions |
| `/inbox` | Live inbox: reply, human takeover, transfer, resolve/reopen, new conversation, simulate customer message |
| `/auto-replies` | Keyword rules CRUD, AI templates, global AI toggle |
| `/schedules` | Working hours (Asia/Dhaka), shifts, away reply |
| `/team` | Members, roles Admin/Agent/Viewer |
| `/channels` | Connect channels, copy webhook URL, test webhook |
| `/combine` | Cross-channel message combine settings + preview |
| `/analytics` | Date-range analytics, AI vs human, token cost, per-agent/channel |
| `/docs` | Documentation and downloads (README, BACKEND.md, n8n JSON, API reference) |
| `/settings` | Profile, notifications, confirmations, API keys, reset/clear data |

## Backend
See **BACKEND.md** for the Postgres schema (users, pages, conversations, messages, auto_replies, schedules, shifts, team_members, message_combine_settings, analytics_events, api_keys) and webhook endpoints (webhook-receiver, message-combine, ai-reply, chat-transfer initiate/check/change, admin-takeover check/start, shift-schedule-check, token-cost-counting).

## Develop
```bash
bun install
bun run dev
```
Reset sample data anytime: Settings → Danger zone → Reset demo data.

BACKEND.md — schema & flow

# Chatflow Commander — Backend Reference (UST AI Control)

The demo app runs entirely in the browser (localStorage). This document describes the production backend it is designed for: **Postgres** + an automation layer (n8n or serverless functions).

## Postgres schema

Tables included (all defined below): `users`, `pages` (channels), `channels` (view), `conversations`, `messages`, `auto_replies`, `schedules`, `shifts`, `team_members`, `message_combine_settings`, `analytics_events`, `api_keys`.

```sql
create table users (
  id uuid primary key default gen_random_uuid(),
  email text unique not null,
  name text not null,
  password_hash text,
  created_at timestamptz default now()
);

create table pages (
  id uuid primary key default gen_random_uuid(),
  owner_id uuid references users(id) on delete cascade,
  channel text check (channel in ('facebook','messenger','whatsapp','website')) not null,
  external_page_id text not null,
  access_token_encrypted text,
  connected boolean default false,
  webhook_verified_at timestamptz,
  created_at timestamptz default now()
);

-- channels: convenience view over pages (one row per connected channel)
create view channels as
  select id, owner_id, channel, external_page_id, connected, webhook_verified_at from pages;

create table team_members (
  id uuid primary key default gen_random_uuid(),
  user_id uuid references users(id) on delete cascade,
  workspace_owner_id uuid references users(id) on delete cascade,
  role text check (role in ('Admin','Agent','Viewer')) not null default 'Agent',
  online boolean default false,
  created_at timestamptz default now()
);

create table conversations (
  id uuid primary key default gen_random_uuid(),
  page_id uuid references pages(id) on delete cascade,
  customer_external_id text not null,
  customer_name text,
  customer_phone text,
  customer_email text,
  status text check (status in ('open','resolved')) default 'open',
  human_takeover boolean default false,
  takeover_by uuid references team_members(id),
  assignee_id uuid references team_members(id),
  combined_into uuid references conversations(id),
  unread_count int default 0,
  last_message_at timestamptz,
  created_at timestamptz default now()
);

create table messages (
  id uuid primary key default gen_random_uuid(),
  conversation_id uuid references conversations(id) on delete cascade,
  sender text check (sender in ('customer','ai','human')) not null,
  author_id uuid references team_members(id),
  channel text not null,
  body text not null,
  status text check (status in ('sent','delivered','read','failed')) default 'sent',
  external_message_id text,
  tokens_in int default 0,
  tokens_out int default 0,
  created_at timestamptz default now()
);
create index on messages (conversation_id, created_at);

create table auto_replies (
  id uuid primary key default gen_random_uuid(),
  owner_id uuid references users(id) on delete cascade,
  keyword text not null,
  reply text not null,
  channel text default 'all',
  active boolean default true,
  created_at timestamptz default now()
);

create table schedules (
  id uuid primary key default gen_random_uuid(),
  owner_id uuid references users(id) on delete cascade,
  timezone text default 'Asia/Dhaka',
  day text not null,           -- Sat..Fri
  is_open boolean default true,
  open_time time, close_time time,
  away_message text,
  away_active boolean default true
);

create table shifts (
  id uuid primary key default gen_random_uuid(),
  member_id uuid references team_members(id) on delete cascade,
  day text not null,
  start_time time not null,
  end_time time not null
);

create table message_combine_settings (
  owner_id uuid primary key references users(id) on delete cascade,
  enabled boolean default true,
  window_minutes int default 30,
  match_phone boolean default true,
  match_email boolean default true,
  match_name boolean default false,
  keep_channel_tag boolean default true
);

create table analytics_events (
  id bigserial primary key,
  owner_id uuid references users(id) on delete cascade,
  conversation_id uuid references conversations(id) on delete set null,
  type text not null,          -- message_in, ai_reply, human_reply, transfer, takeover, resolve
  tokens int default 0,
  cost_usd numeric(10,6) default 0,
  meta jsonb default '{}',
  created_at timestamptz default now()
);

create table api_keys (
  id uuid primary key default gen_random_uuid(),
  owner_id uuid references users(id) on delete cascade,
  label text,
  key_hash text not null,      -- store sha256, never the raw key
  last_used_at timestamptz,
  created_at timestamptz default now()
);
```

## Webhook / API endpoints

All endpoints accept JSON and require header `x-api-key: cfc_live_...` (except Meta verification GET).

| Method | Path | Purpose |
|---|---|---|
| GET/POST | `/webhook-receiver` | Meta/WhatsApp/website inbound. GET answers `hub.challenge`; POST stores the message, runs combine + AI. |
| POST | `/message-combine` | `{conversation_id}` → merges with other open threads of the same customer inside the combine window. |
| POST | `/ai-reply` | `{conversation_id, text}` → skips if `human_takeover`; keyword rule match else LLM reply; logs tokens. |
| POST | `/chat-transfer/initiate` | `{conversation_id, to_member_id}` → creates pending transfer. |
| GET | `/chat-transfer/check?conversation_id=` | Returns current assignee + pending transfer. |
| POST | `/chat-transfer/change` | `{conversation_id, member_id}` → sets assignee immediately. |
| GET | `/admin-takeover/check?conversation_id=` | `{human_takeover, takeover_by}` — AI must call this before replying. |
| POST | `/admin-takeover/start` | `{conversation_id, member_id, on: true|false}` → toggles human control. |
| GET | `/shift-schedule-check?at=ISO` | Returns `{open, on_shift: [...], away_message}` in Asia/Dhaka. |
| POST | `/token-cost-counting` | `{message_id, tokens_in, tokens_out, model}` → writes analytics_events with cost. |

### Inbound flow
1. `webhook-receiver` → upsert conversation, insert message.
2. `message-combine` (if enabled).
3. `shift-schedule-check` → outside hours ⇒ send away message.
4. `admin-takeover/check` → if human control, stop.
5. `ai-reply` → send via channel API → `token-cost-counting`.