SOP CS-02: Live Chat System (v1.8)¶
Status: Production Ready
Last Updated: 2026-01-21
Owner: Protocol Raw Operations
Platform: Supabase Edge Functions + OpenAI GPT-4o-mini + Cloudflare Pages + Slack + Customer.io
Overview¶
AI-powered live chat widget for the Protocol Raw website that handles pre-purchase inquiries, provides product information, and escalates complex issues with full AI triage integration - generating draft responses and sending acknowledgment emails automatically.
Flow: Website Widget → Supabase Edge Function → Knowledge Base Fetch → OpenAI GPT-4o-mini (streaming) → AI Response (or Escalation → Email Validation → AI Triage → Acknowledgment Email → Slack)
Key Achievements: - 24/7 instant response to common questions - Automatic AI-drafted responses for escalated chats - Customer acknowledgment within seconds of escalation - Email validation ensures customer context is loaded - Human operators see tickets with ready-to-review drafts
v1.8 Changes: - Consolidated documentation from v1.6 and v1.7 - Restored missing Escalation Flow, Support Workstation, and Database Schema sections
v1.7 Changes: - Widget v6.7 - Launcher label shortened to "Ask a question" (fits mobile) - Mobile back button fix - Back button now closes chat instead of leaving site (History API) - Widget v6.6 - Opening message signals domain expertise (feeding amounts, transition timing, batch testing) - Header subtext - Updated to "Instant answers, 24/7"
Architecture Components¶
System Flow Diagram¶
+-----------------------------+
| Website Visitor |
| (Chat Widget v6.7) |
+-------------+---------------+
| POST /functions/v1/chat
v
+-----------------------------+
| Supabase Edge Fn | <- chat (v20)
| (Deno Runtime) |
+-------------+---------------+
|
+-----+-----+
v v
+-------------+ +---------------------+
| Knowledge | | Session |
| Base | | CRUD |
| Fetch | | (parallel) |
+------+------+ +----------+----------+
| |
+-------+-----------+
v
+---------------------+
| OpenAI |
| GPT-4o-mini |
| (streaming) |
+----------+----------+
|
v
+---------------------+
| AI Response |
| (with judgment |
| on escalation) |
+----------+----------+
|
+-----+-----+
v v
+----------+ +--------------------------+
| Normal | | AI Chose to Escalate |
| Response | | (asks for email) |
+----+-----+ +----------+---------------+
| |
v v
+------------+ +------------------+
| Rating | | Customer |
| Buttons | | Provides Email |
+------------+ +--------+---------+
|
v
+------------------+
| Email Validation |
| (check customer |
| database) |
+--------+---------+
|
+----------+----------+
v v
+---------------+ +---------------+
| Email Found | | Email NOT |
| -> Proceed | | Found -> Ask |
| | | for correct |
+-------+-------+ | email |
| +---------------+
v
+---------------------+
| Get Customer Context|
| (subscription, |
| delivery status) |
+----------+----------+
|
v
+---------------------+
| cs-agent-triage |
| (creates ticket + |
| drafts response) |
+----------+----------+
|
+---------+---------+
v v
+-------------+ +-------------+
| Policy Gate | | AI Draft |
| Triggered | | Generated |
| (human-only)| | (ready to |
| | | review) |
+------+------+ +------+------+
| |
+--------+---------+
v
+---------------------+
| send-support-email |
| (acknowledgment) |
+----------+----------+
|
v
+-------------+ +-------------+ +------------------+
| Customer | | Slack | | Support |
| Receives | | Alert | | Workstation |
| Ack Email | | #cust-ops | | (with draft) |
+-------------+ +-------------+ +------------------+
Chat Widget (v6.7)¶
Widget Positioning Strategy¶
The chat widget uses a two-stage positioning approach:
- Launcher button - Short, neutral, fits mobile: "Ask a question"
- Opening message - Signals domain expertise and specific capabilities
This separation ensures the launcher doesn't overwhelm mobile screens while the opening message immediately establishes credibility.
Widget Copy¶
Launcher button:
Header:
Opening message (bot greeting):
I can help with feeding amounts, transition timing, ingredients, or how our batch safety testing works. What would you like to know?
Mobile UX Fixes (v6.7)¶
Back button handling:
- Chat panel pushes a history state when opened
- popstate listener closes chat instead of navigating away
- Prevents visitors from accidentally leaving the site
// When chat opens
history.pushState({ prChatOpen: true }, '');
// Listen for back button
window.addEventListener('popstate', (e) => {
if (state.isOpen) {
close();
}
});
Deployment¶
Location: https://cdn.protocolraw.co.uk/chat-widget.js
Version: 6.7
To deploy:
1. Upload chat-widget.js to Cloudflare Pages
2. Cache will update within minutes
Edge Function: chat (v20)¶
Deployment¶
URL: https://znfjpllsiuyezqlneqzr.supabase.co/functions/v1/chat
Runtime: Deno (Supabase Edge Functions)
Version: v20 (AI triage integration + email validation)
Key Features in v20¶
Email Validation:
- When customer provides email, system checks v_customer_full_context
- If email not found in customer database, AI asks for registered email
- Prevents escalation with unknown emails (no customer context available)
Customer Context Lookup: - Fetches full customer profile before calling triage - Includes: subscription status, box size, delivery tracking, shipment status - Context passed to triage for better AI draft quality
AI Triage Integration:
- Instead of creating ticket directly, calls cs-agent-triage
- Triage creates ticket + generates AI draft response
- Policy gates still apply (health concerns → human-only)
Acknowledgment Email:
- Sent via send-support-email Edge Function (template mode)
- Uses Customer.io template ID 5
- Sent immediately after triage creates ticket
Request Format¶
Method: POST
Headers:
Body:
{
"message": "User's question here",
"visitor_id": "v_abc123xyz",
"session_id": "uuid-string",
"entry_page": "/calculator",
"referrer": "https://google.com",
"device_type": "desktop"
}
Response Format (SSE Stream)¶
Content-Type: text/event-stream
1. Start Event:
2. Chunk Events:
3. Done Event:
event: done
data: {"session_id":"uuid","message_id":"uuid","escalated":false,"escalation_reason":null,"response_time_ms":847}
Environment Variables¶
| Variable | Description |
|---|---|
| OPENAI_API_KEY | OpenAI API key for GPT-4o-mini |
| SUPABASE_URL | Supabase project URL |
| SUPABASE_SERVICE_ROLE_KEY | Service role key for database access |
| SLACK_CHAT_ESCALATION_WEBHOOK | Webhook for #cust-ops channel |
| AGENT_INTERNAL_SECRET | Secret for calling cs-agent-triage |
AI Configuration¶
Model: GPT-4o-mini
Temperature: 0.3
Max Tokens: 400
Context Window: Last 20 messages
Streaming: Enabled
Escalation Flow¶
Overview¶
When AI decides to escalate, the system: 1. Validates the customer's email against the database 2. Fetches full customer context (subscription, delivery, etc.) 3. Calls AI triage to create ticket and draft response 4. Sends acknowledgment email to customer 5. Notifies team via Slack
Email Validation¶
Why: Without validated email, we can't load customer context, resulting in poor AI drafts.
Flow:
1. Customer provides email in chat
2. fn_link_chat_to_customer checks if email exists in customers table
3. If found → customer_id is set on session → proceed with escalation
4. If NOT found → AI asks for the registered email address
System Note Injection: When email is not found, the chat function injects:
[SYSTEM NOTE: The email "fake@example.com" was not found in our customer database.
Ask the customer to provide the email address they used when they signed up for Protocol Raw.]
Customer Context¶
Before calling triage, the chat function fetches from v_customer_full_context:
| Field | Purpose |
|---|---|
| customer_id | Identify customer |
| first_name | Personalization |
| subscription_status | Context for AI |
| box_size | Order details |
| tracking_no | Delivery context |
| shipment_status | Delivery status |
| latest_event_text | Latest courier update |
| next_billing_date | Subscription timing |
| is_repeat_contacter | Policy gate trigger |
| has_recent_credit | Policy gate trigger |
Triage Payload¶
The chat function sends this to cs-agent-triage:
{
"customer_email": "customer@example.com",
"subject": "Chat escalation",
"body": "CUSTOMER DELIVERY STATUS:\nTracking: 1234567890\nStatus: IN_TRANSIT\nLatest Update: Out for delivery\n\nCHAT TRANSCRIPT:\nCustomer: My delivery was supposed to arrive yesterday...\nAI: I'd like our team to look at this...",
"source": "chat",
"customer_context": {
"customer_id": "uuid",
"first_name": "John",
"subscription_status": "active",
"box_size": "8kg",
"is_repeat_contacter": false,
"has_recent_credit": false
}
}
Policy Gates¶
Triage applies the same policy gates as email:
| Gate Code | Trigger | Result |
|---|---|---|
| health_vomiting | "vomiting" in transcript | Human-only, no draft |
| health_blood | "blood" in transcript | Human-only, no draft |
| repeat_contacter | 3+ tickets in 7 days | Human-only, no draft |
| has_recent_credit | Recent credit + asking for more | Human-only, no draft |
| financial_refund | "refund" request | Human-only, no draft |
If no gate triggers, AI drafts a response using customer context.
Acknowledgment Email¶
Template ID: 5 (Customer.io)
Subject: We've received your message
Content:
Hi [first_name],
Thanks for chatting with us. I've flagged your concern to the team and someone will follow up by email within the next few hours.
If anything's urgent in the meantime, just reply to this email.
Protocol Raw
Timing: Sent immediately after triage creates ticket (no delay).
Support Workstation Integration¶
Badge Display¶
Chat escalation tickets show a distinctive badge in the queue:
Styling: - Background: #E8F5E9 (light green) - Color: #4B685B (Protocol Raw green) - Font weight: 500
Ticket Details¶
When viewing a chat escalation ticket:
| Field | Value |
|---|---|
| Source | chat |
| Channel badge | 💬 Chat |
| Subject | "Chat escalation" |
| Body | Formatted transcript with delivery context |
| AI Draft | Ready to review (if no policy gate) |
| Status | escalated (if policy gate) or ai_reviewed |
Conversation Thread¶
The ticket body contains the chat transcript:
CUSTOMER DELIVERY STATUS:
Tracking: 1234567890
Status: IN_TRANSIT
Latest Update: Out for delivery
CHAT TRANSCRIPT:
Customer: My delivery was supposed to arrive yesterday but it still hasn't come
AI: I'm sorry to hear that. I'd like our team to look at this properly. Could you share your email address?
Customer: john@example.com
AI: Thank you for sharing your email. I'll ensure our team follows up with you shortly.
Knowledge Base Integration¶
The chat system fetches its prompt from the centralised AI Knowledge Base (see SOP AI-KB-01 v1.8).
Sections Used (Chat Channel)¶
| Section | Purpose |
|---|---|
| brand_identity | Voice, tone, terminology |
| response_principles | 11 core principles |
| product_knowledge | SKU, pricing, ingredients, FEDIAF |
| safety_verification | Batch testing, proof portal |
| storage_serving | Freezer, defrost, serve |
| feeding_calculations | Reference examples, calculator link |
| transition_guidance | 10-day schedule, warning signs |
| delivery_logistics | DPD, dispatch days |
| self_service_portal | Portal features |
| common_scenarios | Shared Q&A |
| escalation_rules | AI judgment rules |
| hard_boundaries | Prohibitions |
| chat_context | Chat-specific role |
| chat_format | Short responses, markdown |
Prompt Assembly¶
Rating System¶
How It Works¶
- After each AI response, thumbs up/down buttons appear on hover (always visible on mobile)
- Positive ratings submitted immediately
- Negative ratings prompt for optional feedback
- All ratings stored in
chat_message_ratingstable
Rating Endpoint¶
Edge Function: chat-rating
Request:
Monitoring Ratings¶
SELECT
rating,
COUNT(*) as count,
COUNT(feedback) as with_feedback
FROM raw_ops.chat_message_ratings
WHERE created_at > NOW() - INTERVAL '7 days'
GROUP BY rating;
Database Schema¶
Table: chat_sessions¶
| Column | Type | Description |
|---|---|---|
| id | UUID | Primary key (session_id) |
| visitor_id | TEXT | Persistent visitor identifier |
| visitor_email | TEXT | Email if provided for escalation |
| customer_id | UUID | Linked customer (if email validated) |
| entry_page | TEXT | First page visited |
| referrer | TEXT | Traffic source |
| device_type | TEXT | 'desktop' or 'mobile' |
| status | TEXT | 'active', 'escalated', 'closed' |
| support_ticket_id | UUID | Linked ticket if escalated |
| created_at | TIMESTAMPTZ | Session start |
| last_message_at | TIMESTAMPTZ | Last activity |
Table: chat_messages¶
| Column | Type | Description |
|---|---|---|
| id | UUID | Primary key (message_id) |
| session_id | UUID | FK to chat_sessions |
| role | TEXT | 'user' or 'assistant' |
| content | TEXT | Message text |
| tokens_used | INTEGER | Token count |
| response_time_ms | INTEGER | AI response latency |
| escalation_trigger | BOOLEAN | Did this response trigger escalation? |
| created_at | TIMESTAMPTZ | When sent |
Table: chat_message_ratings¶
| Column | Type | Description |
|---|---|---|
| id | UUID | Primary key |
| message_id | UUID | FK to chat_messages |
| rating | TEXT | 'positive' or 'negative' |
| feedback | TEXT | Optional feedback text |
| created_at | TIMESTAMPTZ | When rated |
Database Schema Updates (v1.5)¶
chat_sessions - Added columns for triage integration:
| Column | Type | Description |
|---|---|---|
| customer_id | UUID | FK to customers (set on email validation) |
| support_ticket_id | UUID | FK to support_tickets (from triage) |
support_tickets - Source constraint updated:
support_tickets - Status constraint updated:
CHECK (status IN ('new', 'open', 'in_progress', 'awaiting_reply', 'pending', 'resolved', 'closed', 'ai_reviewed', 'escalated'))
Function: fn_get_chat_history¶
Returns conversation history for a session:
SELECT * FROM raw_ops.fn_get_chat_history('session-uuid-here');
-- Returns: (id, session_id, role, content, created_at, escalation_trigger)
Updated to include escalation_trigger:
Escalation Detection¶
How AI Escalation is Detected¶
The edge function checks the AI's response for phrases indicating it chose to escalate:
const escalationPhrases = [
'share your email',
'provide your email',
'your email address',
'team to look at',
'team to follow up',
'someone will follow up',
'someone will contact',
'team will reach out',
'have our team',
'i\'d like our team'
];
Previous Escalation Check¶
The function also checks if a previous message in the session triggered escalation:
function checkPreviousEscalation(history) {
return history.some(msg =>
msg.role === 'assistant' && msg.escalation_trigger === true
);
}
This ensures escalation proceeds when: 1. AI asks for email (escalation_trigger = true) 2. Customer provides email (no trigger in current response) 3. System detects previous escalation and proceeds
Operations Guide¶
Handling Chat Escalations¶
- Ticket appears in Support Workstation with "💬 Escalated from chat" badge
- Review AI draft (if available) - drafts use full customer context
- Check for policy gates - if triggered, no draft available
- Send response or edit draft before sending
- Response threads to acknowledgment email customer already received
Monitoring Escalation Quality¶
Check triage results:
SELECT
t.id,
t.subject,
t.ai_category,
t.ai_confidence,
t.ai_draft_response IS NOT NULL AS has_draft,
d.policy_gate_triggered,
d.policy_gate_code
FROM raw_ops.support_tickets t
LEFT JOIN raw_ops.cs_agent_decisions d ON d.ticket_id = t.id
WHERE t.source = 'chat'
ORDER BY t.created_at DESC
LIMIT 20;
Check email validation failures:
SELECT
id,
visitor_email,
customer_id,
status
FROM raw_ops.chat_sessions
WHERE visitor_email IS NOT NULL
AND customer_id IS NULL
AND created_at > NOW() - INTERVAL '7 days'
ORDER BY created_at DESC;
Daily Monitoring¶
- Check Ops Portal → Support Workstation for chat escalations
- Handle escalations within 4 hours (SLA target)
- Review any unmatched emails (customers providing wrong email)
Weekly Review¶
- Run policy gate analysis - are legitimate issues being blocked?
- Review AI draft quality for chat escalations
- Check email validation rate
- Update knowledge base if escalation rules need adjustment
Deployment¶
Edge Functions¶
# Chat function (v20)
supabase functions deploy chat --project-ref znfjpllsiuyezqlneqzr
# Send support email (for acknowledgments)
supabase functions deploy send-support-email --project-ref znfjpllsiuyezqlneqzr
# Triage function
supabase functions deploy cs-agent-triage --project-ref znfjpllsiuyezqlneqzr
# Rating function (no JWT verification)
supabase functions deploy chat-rating --no-verify-jwt --project-ref znfjpllsiuyezqlneqzr
Widget Deployment¶
# Upload to Cloudflare Pages
# File: chat-widget.js (v6.7)
# Path: https://cdn.protocolraw.co.uk/chat-widget.js
Customer.io Template¶
Template ID 5: Chat escalation acknowledgment
- Must have "From" address configured
- Variables: first_name
Troubleshooting¶
Escalation Not Creating Ticket¶
- Check email validation - is customer_id set on session?
- Check triage logs:
supabase functions logs cs-agent-triage - Verify AGENT_INTERNAL_SECRET is set
AI Draft Missing/Poor Quality¶
- Check if policy gate triggered (expected - no draft)
- Verify customer context is being fetched
- Check triage logs for errors
Acknowledgment Email Not Sent¶
- Check chat logs:
supabase functions logs chat - Verify send-support-email has JWT disabled
- Check Customer.io template ID 5 exists with "From" address
Customer Providing Unregistered Email¶
- AI should ask for registered email (check knowledge base
escalation_rules) - Customer may need to provide the email used at signup
- If customer is new (no account), escalation won't proceed - they should email support@protocolraw.co.uk
Email Threading Not Working¶
Acknowledgment email creates thread anchor. Human response should thread to it automatically via In-Reply-To headers.
Mobile Back Button Leaving Site¶
- Verify widget version is 6.7+
- Check
popstatelistener is registered - Confirm
history.pushStateis called inopen()function
Version History¶
v1.8 (2026-01-21) - Documentation Consolidation¶
Restored from v1.6: - Escalation Flow section (email validation, customer context, triage payload, policy gates, acknowledgment email) - Support Workstation Integration section (badge display, ticket details, conversation thread) - Database Schema Updates section (chat_sessions columns, support_tickets constraints, fn_get_chat_history)
Retained from v1.7: - Clean ASCII flow diagram - Widget v6.7 changes (launcher label, mobile back button fix) - All troubleshooting items
v1.7 (2026-01-21) - Widget UX Improvements¶
Widget Changes (v6.7): - Launcher label: "Feeding or safety questions?" → "Ask a question" (shorter, fits mobile) - Mobile back button now closes chat instead of leaving site (History API)
Widget Changes (v6.6): - Header subtext: "Usually replies instantly" → "Instant answers, 24/7" - Opening message updated to signal domain expertise: "I can help with feeding amounts, transition timing, ingredients, or how our batch safety testing works."
Rationale: Widget copy now mirrors visitor's internal state (feeding anxiety, safety concerns) while launcher stays short for mobile. Back button fix prevents accidental site abandonment.
v1.6 (2026-01-21) - Widget Positioning Update¶
Widget Changes (v6.6): - Widget label: "Questions?" → "Feeding or safety questions?" - Header subtext: "Usually replies instantly" → "Instant answers, 24/7" - Opening message updated to signal domain expertise
Rationale: Widget copy now mirrors visitor's internal state (feeding anxiety, safety concerns) and signals specific capabilities rather than generic chatbot positioning.
v1.5 (2026-01-21) - AI Triage Integration¶
New Features:
- Escalated chats now go through cs-agent-triage
- AI drafts generated with full customer context
- Acknowledgment email sent immediately on escalation
- Email validation before escalation (must be registered customer)
- "Escalated from chat" badge in Support Workstation
- Policy gates apply to chat escalations
Technical Changes:
- Edge Function bumped to v20
- fn_get_chat_history updated to return escalation_trigger
- support_tickets.source constraint updated for 'chat'
- support_tickets.status constraint updated for 'ai_reviewed', 'escalated'
- Customer.io template ID 5 for acknowledgment
Database:
- chat_sessions.customer_id populated on email validation
- chat_sessions.support_ticket_id populated from triage response
v1.4 (2026-01-12) - AI Judgment Escalation¶
- Removed keyword-based escalation entirely
- Edge Function bumped to v13
- AI now uses judgment based on
escalation_rules - Response principles consolidated (15 → 11)
v1.3 (2026-01-12) - Rating System¶
- Added response rating system (thumbs up/down with optional feedback)
- Edge Function v11 (returns message_id)
- New Edge Function: chat-rating v1
- Widget v6.1 (rating UI)
v1.2 (2026-01-12) - Knowledge Base Integration¶
- Prompt content fetched from AI Knowledge Base
- Edge Function v10
v1.1 (2026-01-12) - Streaming + Performance¶
- Streaming SSE responses
- GPT-4o-mini model
- Parallelized DB calls
v1.0 (2025-12-04) - Initial Production Release¶
- JSON request/response format
- GPT-4o-mini integration
- Basic escalation detection
- Slack notifications
Related Documentation¶
- SOP CS-01 v2.1: AI Customer Service Triage (Email)
- SOP CS-00 v1.5: Customer Operations System (architecture overview)
- SOP AI-KB-01 v1.8: AI Knowledge Base (prompt management)
- Ops Portal Documentation v3.7: Portal overview (Support Workstation updates)
- Visual Identity Guide v2.1: Brand colors and styling
Document Owner: Protocol Raw Technical Team
Last Reviewed: 2026-01-21
Next Review: 2026-04-21
Version: 1.8
Status: Production Ready ✅