For Riviera Plaisance meeting: - feature-selector-riviera-meeting.html (ALL 8 features with notes/voting) - USER_TESTING_INSTRUCTIONS_CLOUD.md (user perspective testing guide) - INTEGRATION_WHATSAPP.md (WhatsApp Business API integration) - INTEGRATION_CLAUDE_CHATBOX.md (Claude CLI chatbox with document context) - LIVE_TESTING_GUIDE.md (comprehensive testing checklist) - FEATURE_SUMMARY_ALL.md (all 8 features catalog) Ready for 1-hour meeting with client.
1178 lines
34 KiB
Markdown
1178 lines
34 KiB
Markdown
# WhatsApp Business Integration Plan
|
|
## NaviDocs Document Sharing & Maintenance Alerts
|
|
|
|
**Document Version:** 1.0
|
|
**Created:** 2025-11-13
|
|
**Status:** Integration Plan (Ready for Implementation)
|
|
**Implementation Time:** 60-90 minutes
|
|
**Complexity:** Medium
|
|
**Dependencies:** Express.js backend, SQLite database, document storage
|
|
|
|
---
|
|
|
|
## Executive Summary
|
|
|
|
WhatsApp Business integration enables NaviDocs to:
|
|
1. **Receive document uploads** via WhatsApp (warranties, manuals, service records)
|
|
2. **Send document search results** to boat owners on-demand
|
|
3. **Push maintenance alerts** (warranty expiration, service reminders, safety recalls)
|
|
4. **Create boat-owner communication channels** (group chats with after-sales team, captain, mechanic)
|
|
|
|
This plan covers Meta's WhatsApp Business API, implementation architecture, cost structure, and required credentials.
|
|
|
|
---
|
|
|
|
## 1. WhatsApp Business API Overview
|
|
|
|
### 1.1 Official API vs Alternatives
|
|
|
|
**We recommend:** Meta's WhatsApp Business API (Official)
|
|
- **Compliance:** Full compliance with WhatsApp's Terms of Service
|
|
- **Reliability:** 99.95% uptime SLA
|
|
- **Support:** Priority developer support
|
|
- **Scalability:** Handles enterprise-level volume
|
|
|
|
**Alternative (NOT recommended):** Third-party chatbot platforms
|
|
- Risk: Account bans, poor documentation sharing, limited automation
|
|
- **We'll use official API**
|
|
|
|
### 1.2 WhatsApp Business API Capabilities
|
|
|
|
| Capability | NaviDocs Use Case | Status |
|
|
|-----------|------------------|--------|
|
|
| **Incoming Messages** | Receive document PDFs, images, warranty files | ✅ Supported |
|
|
| **Outgoing Messages** | Send search results, alerts, notifications | ✅ Supported |
|
|
| **Document Uploads** | Boat owners send warranty/maintenance photos | ✅ Supported |
|
|
| **Group Messaging** | Notify multiple team members simultaneously | ✅ Supported |
|
|
| **Message Templates** | Pre-approved maintenance alerts, notifications | ✅ Supported |
|
|
| **Webhook Events** | Trigger alerts on document upload, warranty expiration | ✅ Supported |
|
|
| **Media Downloads** | Auto-download PDFs from messages | ✅ Supported |
|
|
|
|
---
|
|
|
|
## 2. Implementation Architecture
|
|
|
|
### 2.1 System Flow Diagram
|
|
|
|
```
|
|
WhatsApp User (Boat Owner)
|
|
↓
|
|
Sends: "Find my engine warranty"
|
|
or: Uploads warranty PDF
|
|
↓
|
|
Meta WhatsApp Cloud API (Webhook)
|
|
↓
|
|
NaviDocs Backend (Express.js + Webhook Handler)
|
|
├→ Parse message / download media
|
|
├→ Query SQLite database (documents table)
|
|
├→ Search Meilisearch index
|
|
└→ Format response
|
|
↓
|
|
Send Response via WhatsApp API
|
|
↓
|
|
WhatsApp User receives results
|
|
```
|
|
|
|
### 2.2 Database Schema Updates
|
|
|
|
Add to existing NaviDocs SQLite schema:
|
|
|
|
```sql
|
|
-- Track WhatsApp integrations per boat/owner
|
|
CREATE TABLE whatsapp_integrations (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
boat_id INTEGER NOT NULL,
|
|
owner_phone TEXT NOT NULL, -- E.164 format: +41791234567
|
|
whatsapp_user_id TEXT NOT NULL, -- Meta's unique ID for user
|
|
status VARCHAR(20), -- 'active', 'inactive', 'paused'
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
last_message_at TIMESTAMP,
|
|
FOREIGN KEY (boat_id) REFERENCES boats(id)
|
|
);
|
|
|
|
-- Log all WhatsApp messages (for compliance, debugging)
|
|
CREATE TABLE whatsapp_messages (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
integration_id INTEGER NOT NULL,
|
|
direction VARCHAR(10), -- 'inbound' or 'outbound'
|
|
message_type VARCHAR(20), -- 'text', 'document', 'image', 'alert'
|
|
message_content TEXT,
|
|
media_url TEXT,
|
|
whatsapp_message_id TEXT UNIQUE,
|
|
processed BOOLEAN DEFAULT 0,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
FOREIGN KEY (integration_id) REFERENCES whatsapp_integrations(id)
|
|
);
|
|
|
|
-- Track alerts sent via WhatsApp
|
|
CREATE TABLE warranty_alerts (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
boat_id INTEGER NOT NULL,
|
|
alert_type VARCHAR(50), -- 'warranty_expiring', 'service_due', 'recall', 'maintenance_reminder'
|
|
alert_date DATE,
|
|
sent_via_whatsapp BOOLEAN DEFAULT 0,
|
|
sent_at TIMESTAMP,
|
|
acknowledged BOOLEAN DEFAULT 0,
|
|
FOREIGN KEY (boat_id) REFERENCES boats(id)
|
|
);
|
|
```
|
|
|
|
### 2.3 Backend Changes (Express.js)
|
|
|
|
#### A. Webhook Endpoint Handler
|
|
|
|
```javascript
|
|
// routes/integrations/whatsapp.js
|
|
|
|
const express = require('express');
|
|
const router = express.Router();
|
|
const db = require('../../db');
|
|
const meilisearch = require('../../meilisearch');
|
|
const whatsappService = require('../../services/whatsapp');
|
|
|
|
// Webhook receiver (Meta WhatsApp Cloud API → NaviDocs)
|
|
router.post('/webhook', async (req, res) => {
|
|
const { entry } = req.body;
|
|
|
|
for (const event of entry) {
|
|
const changes = event.changes?.[0];
|
|
if (!changes) continue;
|
|
|
|
const { value } = changes;
|
|
const messages = value.messages || [];
|
|
const statuses = value.statuses || [];
|
|
|
|
// Handle incoming messages
|
|
for (const msg of messages) {
|
|
await handleIncomingMessage(msg, value);
|
|
}
|
|
|
|
// Handle delivery status updates
|
|
for (const status of statuses) {
|
|
await logMessageStatus(status);
|
|
}
|
|
}
|
|
|
|
res.status(200).json({ success: true });
|
|
});
|
|
|
|
// Process incoming message
|
|
async function handleIncomingMessage(msg, metadata) {
|
|
const { from, id: whatsapp_msg_id, type, text, image, document } = msg;
|
|
|
|
// Find WhatsApp integration for this user
|
|
const integration = await db.get(
|
|
'SELECT * FROM whatsapp_integrations WHERE owner_phone = ?',
|
|
[from]
|
|
);
|
|
|
|
if (!integration) {
|
|
// New user: send welcome message
|
|
await whatsappService.sendMessage(from, {
|
|
type: 'template',
|
|
template_name: 'welcome_new_user'
|
|
});
|
|
return;
|
|
}
|
|
|
|
// Log incoming message
|
|
await db.run(
|
|
`INSERT INTO whatsapp_messages (integration_id, direction, message_type, message_content, whatsapp_message_id)
|
|
VALUES (?, ?, ?, ?, ?)`,
|
|
[integration.id, 'inbound', type, JSON.stringify(msg), whatsapp_msg_id]
|
|
);
|
|
|
|
// Route by message type
|
|
if (type === 'text') {
|
|
await handleTextQuery(integration, text.body, whatsapp_msg_id);
|
|
} else if (type === 'document') {
|
|
await handleDocumentUpload(integration, document, whatsapp_msg_id);
|
|
} else if (type === 'image') {
|
|
await handleImageUpload(integration, image, whatsapp_msg_id);
|
|
}
|
|
}
|
|
|
|
// Handle text queries ("Find my warranty", "Service reminders", etc.)
|
|
async function handleTextQuery(integration, query, whatsapp_msg_id) {
|
|
const { boat_id } = integration;
|
|
|
|
// Search Meilisearch index
|
|
const results = await meilisearch.index('documents').search(query, {
|
|
filter: [`boat_id = ${boat_id}`],
|
|
limit: 5
|
|
});
|
|
|
|
if (results.hits.length === 0) {
|
|
await whatsappService.sendMessage(integration.owner_phone, {
|
|
type: 'text',
|
|
text: `No documents found matching "${query}". Try:\n- "warranty"\n- "maintenance"\n- "manual"`
|
|
});
|
|
return;
|
|
}
|
|
|
|
// Format results for WhatsApp
|
|
let responseText = `Found ${results.hits.length} document(s):\n\n`;
|
|
for (const doc of results.hits.slice(0, 5)) {
|
|
responseText += `📄 ${doc.document_name}\n`;
|
|
responseText += `Type: ${doc.document_type} | Date: ${doc.upload_date}\n`;
|
|
responseText += `Download: ${doc.download_url}\n\n`;
|
|
}
|
|
|
|
await whatsappService.sendMessage(integration.owner_phone, {
|
|
type: 'text',
|
|
text: responseText
|
|
});
|
|
|
|
// Log search in database
|
|
await db.run(
|
|
`INSERT INTO whatsapp_messages (integration_id, direction, message_type, message_content, processed)
|
|
VALUES (?, ?, ?, ?, 1)`,
|
|
[integration.id, 'outbound', 'search_results', responseText]
|
|
);
|
|
}
|
|
|
|
// Handle document uploads (warranties, service records)
|
|
async function handleDocumentUpload(integration, docData, whatsapp_msg_id) {
|
|
const { boat_id } = integration;
|
|
|
|
try {
|
|
// Download document from WhatsApp Media API
|
|
const mediaBuffer = await whatsappService.downloadMedia(docData.id, docData.mime_type);
|
|
|
|
// Store in NaviDocs document storage
|
|
const fileName = `whatsapp_${boat_id}_${Date.now()}.${getFileExtension(docData.mime_type)}`;
|
|
const storagePath = `/storage/documents/${boat_id}/${fileName}`;
|
|
|
|
await fs.writeFile(storagePath, mediaBuffer);
|
|
|
|
// Extract metadata from filename/message context
|
|
const documentRecord = {
|
|
boat_id,
|
|
document_type: detectDocumentType(docData.mime_type, fileName),
|
|
document_name: docData.filename || fileName,
|
|
file_path: storagePath,
|
|
upload_source: 'whatsapp',
|
|
whatsapp_message_id,
|
|
uploaded_at: new Date()
|
|
};
|
|
|
|
// Insert into database
|
|
const result = await db.run(
|
|
`INSERT INTO documents (boat_id, document_type, document_name, file_path, upload_source, metadata)
|
|
VALUES (?, ?, ?, ?, ?, ?)`,
|
|
[documentRecord.boat_id, documentRecord.document_type, documentRecord.document_name,
|
|
documentRecord.file_path, documentRecord.upload_source, JSON.stringify(documentRecord)]
|
|
);
|
|
|
|
// OCR/index the document
|
|
await meilisearch.index('documents').addDocuments([{
|
|
id: result.lastID,
|
|
boat_id,
|
|
document_name: documentRecord.document_name,
|
|
document_type: documentRecord.document_type,
|
|
upload_date: documentRecord.uploaded_at,
|
|
indexed: true
|
|
}]);
|
|
|
|
// Confirm upload
|
|
await whatsappService.sendMessage(integration.owner_phone, {
|
|
type: 'text',
|
|
text: `✅ Document received and stored:\n${documentRecord.document_name}\n\nType: ${documentRecord.document_type}`
|
|
});
|
|
|
|
} catch (error) {
|
|
console.error('Document upload error:', error);
|
|
await whatsappService.sendMessage(integration.owner_phone, {
|
|
type: 'text',
|
|
text: `❌ Error uploading document. Please try again.`
|
|
});
|
|
}
|
|
}
|
|
|
|
module.exports = router;
|
|
```
|
|
|
|
#### B. WhatsApp Service Layer
|
|
|
|
```javascript
|
|
// services/whatsapp.js
|
|
|
|
const axios = require('axios');
|
|
|
|
class WhatsAppService {
|
|
constructor(businessPhoneNumberId, accessToken) {
|
|
this.businessPhoneNumberId = businessPhoneNumberId;
|
|
this.accessToken = accessToken;
|
|
this.apiVersion = 'v18.0';
|
|
this.baseUrl = `https://graph.instagram.com/${this.apiVersion}`;
|
|
}
|
|
|
|
// Send message via WhatsApp API
|
|
async sendMessage(recipientPhone, message) {
|
|
try {
|
|
const response = await axios.post(
|
|
`${this.baseUrl}/${this.businessPhoneNumberId}/messages`,
|
|
{
|
|
messaging_product: 'whatsapp',
|
|
recipient_type: 'individual',
|
|
to: recipientPhone,
|
|
type: message.type,
|
|
[message.type]: message[message.type]
|
|
},
|
|
{
|
|
headers: { Authorization: `Bearer ${this.accessToken}` }
|
|
}
|
|
);
|
|
|
|
return response.data;
|
|
} catch (error) {
|
|
console.error('WhatsApp send error:', error.response?.data || error.message);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
// Send message template (pre-approved by WhatsApp)
|
|
async sendTemplate(recipientPhone, templateName, parameters) {
|
|
return this.sendMessage(recipientPhone, {
|
|
type: 'template',
|
|
template: {
|
|
name: templateName,
|
|
language: { code: 'en_US' },
|
|
components: [
|
|
{
|
|
type: 'body',
|
|
parameters: parameters
|
|
}
|
|
]
|
|
}
|
|
});
|
|
}
|
|
|
|
// Download media from WhatsApp servers
|
|
async downloadMedia(mediaId, mimeType) {
|
|
try {
|
|
// Get media URL
|
|
const urlResponse = await axios.get(
|
|
`${this.baseUrl}/${mediaId}`,
|
|
{
|
|
headers: { Authorization: `Bearer ${this.accessToken}` }
|
|
}
|
|
);
|
|
|
|
const { url } = urlResponse.data;
|
|
|
|
// Download file
|
|
const response = await axios.get(url, {
|
|
headers: { Authorization: `Bearer ${this.accessToken}` },
|
|
responseType: 'arraybuffer'
|
|
});
|
|
|
|
return Buffer.from(response.data);
|
|
} catch (error) {
|
|
console.error('Media download error:', error.message);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
// Send maintenance alert via template message
|
|
async sendMaintenanceAlert(phone, boatName, alertType, alertDetails) {
|
|
const templates = {
|
|
warranty_expiring: {
|
|
name: 'warranty_expiring_alert',
|
|
params: [boatName, alertDetails.expiryDate, 'warranty']
|
|
},
|
|
service_due: {
|
|
name: 'service_due_alert',
|
|
params: [boatName, alertDetails.serviceType, alertDetails.dueDate]
|
|
},
|
|
recall_notice: {
|
|
name: 'recall_notice_alert',
|
|
params: [boatName, alertDetails.recallType, alertDetails.recallUrl]
|
|
}
|
|
};
|
|
|
|
const template = templates[alertType];
|
|
if (!template) throw new Error(`Unknown alert type: ${alertType}`);
|
|
|
|
return this.sendTemplate(phone, template.name, [
|
|
{ type: 'text', text: param } for param in template.params
|
|
]);
|
|
}
|
|
|
|
// Link/register phone number with boat
|
|
async linkOwnerPhone(boatId, ownerPhone) {
|
|
// WhatsApp API doesn't identify users by phone alone; we need the WhatsApp user ID
|
|
// This is obtained from the first inbound message webhook
|
|
// For initial setup, ask user to send a test message
|
|
return {
|
|
status: 'pending',
|
|
instruction: `Please text "Hello" to the NaviDocs WhatsApp number to confirm registration`,
|
|
boat_id: boatId
|
|
};
|
|
}
|
|
}
|
|
|
|
module.exports = new WhatsAppService(
|
|
process.env.WHATSAPP_BUSINESS_PHONE_ID,
|
|
process.env.WHATSAPP_ACCESS_TOKEN
|
|
);
|
|
```
|
|
|
|
### 2.4 Frontend Integration (Vue 3)
|
|
|
|
```vue
|
|
<!-- components/BoatSettings/WhatsAppIntegration.vue -->
|
|
|
|
<template>
|
|
<div class="whatsapp-integration">
|
|
<h3>WhatsApp Integration</h3>
|
|
|
|
<div v-if="!isLinked" class="setup-section">
|
|
<p>Enable WhatsApp notifications for {{ boatName }}</p>
|
|
|
|
<div class="setup-steps">
|
|
<ol>
|
|
<li>
|
|
<strong>Open WhatsApp</strong>
|
|
<button @click="copyPhoneNumber" class="btn-secondary">
|
|
Copy NaviDocs WhatsApp Number
|
|
</button>
|
|
<code>{{ navidocsPhone }}</code>
|
|
</li>
|
|
|
|
<li>
|
|
<strong>Send Message</strong>
|
|
<p>Text "Link {{boatId}}" to the NaviDocs WhatsApp number</p>
|
|
<a :href="`https://wa.me/${navidocsPhoneE164}?text=Link%20${boatId}`"
|
|
target="_blank" class="btn-primary">
|
|
Open WhatsApp Chat
|
|
</a>
|
|
</li>
|
|
|
|
<li>
|
|
<strong>Confirm</strong>
|
|
<p>We'll send you a verification code</p>
|
|
<input v-model="verificationCode"
|
|
placeholder="Enter verification code"
|
|
@keyup.enter="confirmLink" />
|
|
<button @click="confirmLink" :disabled="!verificationCode">
|
|
Confirm Link
|
|
</button>
|
|
</li>
|
|
</ol>
|
|
</div>
|
|
</div>
|
|
|
|
<div v-else class="linked-section">
|
|
<h4>✅ WhatsApp Connected</h4>
|
|
<p>Phone: {{ maskedPhone }}</p>
|
|
|
|
<div class="notification-settings">
|
|
<h5>Alert Preferences</h5>
|
|
|
|
<label>
|
|
<input v-model="alerts.warranty_expiring" type="checkbox" />
|
|
Warranty expiration alerts (30 days before)
|
|
</label>
|
|
|
|
<label>
|
|
<input v-model="alerts.service_due" type="checkbox" />
|
|
Scheduled maintenance reminders
|
|
</label>
|
|
|
|
<label>
|
|
<input v-model="alerts.recalls" type="checkbox" />
|
|
Safety recalls & important notices
|
|
</label>
|
|
|
|
<label>
|
|
<input v-model="alerts.expense_summary" type="checkbox" />
|
|
Monthly expense summary
|
|
</label>
|
|
|
|
<button @click="saveAlertPreferences" class="btn-primary">
|
|
Save Preferences
|
|
</button>
|
|
</div>
|
|
|
|
<button @click="unlinkWhatsApp" class="btn-danger">
|
|
Unlink WhatsApp
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, computed } from 'vue';
|
|
import { api } from '@/services/api';
|
|
|
|
const props = defineProps({
|
|
boatId: Number,
|
|
boatName: String
|
|
});
|
|
|
|
const isLinked = ref(false);
|
|
const verificationCode = ref('');
|
|
const navidocsPhone = ref('+41 79 123 4567');
|
|
const navidocsPhoneE164 = computed(() => navidocsPhone.value.replace(/\s/g, ''));
|
|
const maskedPhone = ref('');
|
|
|
|
const alerts = ref({
|
|
warranty_expiring: true,
|
|
service_due: true,
|
|
recalls: true,
|
|
expense_summary: false
|
|
});
|
|
|
|
const copyPhoneNumber = () => {
|
|
navigator.clipboard.writeText(navidocsPhoneE164.value);
|
|
};
|
|
|
|
const confirmLink = async () => {
|
|
try {
|
|
const response = await api.post('/integrations/whatsapp/verify', {
|
|
boat_id: props.boatId,
|
|
verification_code: verificationCode.value
|
|
});
|
|
|
|
isLinked.value = true;
|
|
maskedPhone.value = response.data.phone.replace(/(.{2})(.*)(.{2})/, '$1****$3');
|
|
} catch (error) {
|
|
alert('Invalid verification code');
|
|
}
|
|
};
|
|
|
|
const saveAlertPreferences = async () => {
|
|
await api.post(`/boats/${props.boatId}/whatsapp-preferences`, alerts.value);
|
|
alert('Preferences saved');
|
|
};
|
|
|
|
const unlinkWhatsApp = async () => {
|
|
if (confirm('Remove WhatsApp integration?')) {
|
|
await api.delete(`/boats/${props.boatId}/whatsapp`);
|
|
isLinked.value = false;
|
|
}
|
|
};
|
|
</script>
|
|
```
|
|
|
|
---
|
|
|
|
## 3. Features: Receiving Document Uploads
|
|
|
|
### 3.1 Supported Document Types
|
|
|
|
| Document Type | Use Case | Processing |
|
|
|--------------|----------|-----------|
|
|
| **Warranty Cards** | PDF/Image | Extract expiration date, coverage details |
|
|
| **Service Records** | PDF/Image | Track maintenance history |
|
|
| **Manuals** | PDF | OCR + full-text indexing |
|
|
| **Photos** | JPG/PNG | Store in boat gallery, searchable by tags |
|
|
| **Insurance Docs** | PDF | Extract policy numbers, coverage amounts |
|
|
| **Registration** | PDF/Image | Extract boat registration, builder info |
|
|
|
|
### 3.2 Workflow: Uploading Via WhatsApp
|
|
|
|
```
|
|
User: "I got my engine service done, here's the receipt"
|
|
↓ [Sends PDF via WhatsApp]
|
|
↓
|
|
NaviDocs Backend:
|
|
1. Webhook receives document
|
|
2. Download media from Meta servers
|
|
3. Run OCR (Tesseract)
|
|
4. Extract text & metadata
|
|
5. Store in /storage/documents/{boat_id}/
|
|
6. Index in Meilisearch
|
|
7. Send confirmation to user
|
|
↓
|
|
NaviDocs App:
|
|
Documents tab shows: "Engine Service - Nov 13, 2025"
|
|
Search finds it: "Find my last engine service"
|
|
```
|
|
|
|
### 3.3 Database Triggers for Auto-Processing
|
|
|
|
```sql
|
|
-- Trigger: Auto-detect warranty expiration from uploaded documents
|
|
CREATE TRIGGER on_document_upload_detect_warranty
|
|
AFTER INSERT ON documents
|
|
FOR EACH ROW
|
|
WHEN (NEW.document_type = 'warranty' OR NEW.document_name LIKE '%warrant%')
|
|
BEGIN
|
|
INSERT INTO warranty_alerts (boat_id, alert_type, alert_date)
|
|
SELECT NEW.boat_id, 'warranty_detected', DATE('now', '+30 days');
|
|
END;
|
|
|
|
-- Trigger: Auto-send confirmation when document is uploaded via WhatsApp
|
|
CREATE TRIGGER on_whatsapp_document_confirm
|
|
AFTER INSERT ON documents
|
|
FOR EACH ROW
|
|
WHEN (NEW.upload_source = 'whatsapp')
|
|
BEGIN
|
|
-- Queue WhatsApp message to integration table
|
|
INSERT INTO notifications (type, boat_id, message)
|
|
VALUES ('whatsapp_confirm', NEW.boat_id,
|
|
'Document received: ' || NEW.document_name);
|
|
END;
|
|
```
|
|
|
|
---
|
|
|
|
## 4. Features: Sending Document Search Results
|
|
|
|
### 4.1 Natural Language Queries
|
|
|
|
Users can ask natural questions:
|
|
|
|
```
|
|
Q: "Find my engine warranty"
|
|
A: [Returns warranty card + expiration date]
|
|
|
|
Q: "What's my service history?"
|
|
A: [Returns last 5 service records in chronological order]
|
|
|
|
Q: "I need the manual for my anchor winch"
|
|
A: [Returns manual PDF + quick link to download]
|
|
|
|
Q: "When does my insurance expire?"
|
|
A: [Shows policy dates + alert if <90 days]
|
|
```
|
|
|
|
### 4.2 Faceted Search Results
|
|
|
|
Instead of long text lists, send structured responses:
|
|
|
|
```
|
|
Search: "warranty"
|
|
|
|
Results:
|
|
📄 Engine Warranty (Yanmar)
|
|
Expires: Dec 15, 2027
|
|
Coverage: Parts + Labor
|
|
Provider: Yanmar Europe
|
|
|
|
📄 Hull Warranty (Jeanneau)
|
|
Expires: Sep 2, 2029
|
|
Coverage: Manufacturing defects
|
|
Provider: Jeanneau
|
|
|
|
📄 Extended Protection (SeaCare)
|
|
Expires: Nov 13, 2026
|
|
Coverage: Electronics + Upholstery
|
|
Provider: SeaCare Global
|
|
|
|
[Reply with number to download: "1", "2", or "3"]
|
|
```
|
|
|
|
---
|
|
|
|
## 5. Features: Maintenance & Warranty Alerts
|
|
|
|
### 5.1 Alert Types & Triggers
|
|
|
|
```javascript
|
|
const alertConfig = {
|
|
// Warranty alerts
|
|
warranty_expiring_30d: {
|
|
trigger: 'warranty_expiration_date - 30 days',
|
|
message: 'Warranty expiring in 30 days for {item}',
|
|
template: 'warranty_expiring_alert'
|
|
},
|
|
|
|
warranty_expiring_7d: {
|
|
trigger: 'warranty_expiration_date - 7 days',
|
|
message: 'URGENT: Warranty expires in 7 days for {item}',
|
|
template: 'warranty_urgent_alert'
|
|
},
|
|
|
|
// Maintenance alerts
|
|
service_reminder_due: {
|
|
trigger: 'service_due_date',
|
|
message: 'Scheduled maintenance due: {service_type} on {due_date}',
|
|
template: 'service_due_alert'
|
|
},
|
|
|
|
service_reminder_overdue: {
|
|
trigger: 'service_due_date + 7 days',
|
|
message: 'OVERDUE: {service_type} service was due on {due_date}',
|
|
template: 'service_overdue_alert'
|
|
},
|
|
|
|
// Safety alerts
|
|
recall_notice: {
|
|
trigger: 'new recall posted for boat model',
|
|
message: 'Safety recall for {recall_type}. Details: {recall_url}',
|
|
template: 'recall_notice_alert'
|
|
},
|
|
|
|
// Financial alerts
|
|
expense_monthly_summary: {
|
|
trigger: 'first of each month',
|
|
message: 'Monthly maintenance cost: €{total}. Top expense: {top_item}',
|
|
template: 'expense_summary_alert'
|
|
}
|
|
};
|
|
```
|
|
|
|
### 5.2 Scheduled Alert Jobs (BullMQ)
|
|
|
|
```javascript
|
|
// background/jobs/warranty-alerts.js
|
|
|
|
const Queue = require('bull');
|
|
const db = require('../../db');
|
|
const whatsappService = require('../../services/whatsapp');
|
|
|
|
const warrantyAlertQueue = new Queue('warranty-alerts', {
|
|
redis: { host: 'localhost', port: 6379 }
|
|
});
|
|
|
|
// Daily job to check warranty expiration dates
|
|
warrantyAlertQueue.process(async (job) => {
|
|
const today = new Date();
|
|
const thirtyDaysFromNow = new Date(today.getTime() + 30 * 24 * 60 * 60 * 1000);
|
|
|
|
// Find all warranties expiring in next 30 days
|
|
const expiringWarranties = await db.all(
|
|
`SELECT wa.*, bi.owner_phone, b.boat_name
|
|
FROM warranty_alerts wa
|
|
JOIN whatsapp_integrations bi ON wa.boat_id = bi.boat_id
|
|
JOIN boats b ON wa.boat_id = b.id
|
|
WHERE wa.alert_date BETWEEN ? AND ?
|
|
AND wa.sent_via_whatsapp = 0`,
|
|
[today.toISOString().split('T')[0], thirtyDaysFromNow.toISOString().split('T')[0]]
|
|
);
|
|
|
|
for (const warranty of expiringWarranties) {
|
|
try {
|
|
await whatsappService.sendTemplate(
|
|
warranty.owner_phone,
|
|
'warranty_expiring_alert',
|
|
[
|
|
{ type: 'text', text: warranty.boat_name },
|
|
{ type: 'text', text: warranty.alert_date },
|
|
{ type: 'text', text: warranty.alert_type }
|
|
]
|
|
);
|
|
|
|
// Mark as sent
|
|
await db.run(
|
|
'UPDATE warranty_alerts SET sent_via_whatsapp = 1, sent_at = ? WHERE id = ?',
|
|
[new Date().toISOString(), warranty.id]
|
|
);
|
|
} catch (error) {
|
|
console.error(`Failed to send alert for warranty ${warranty.id}:`, error);
|
|
}
|
|
}
|
|
});
|
|
|
|
// Schedule job to run every day at 9:00 AM
|
|
warrantyAlertQueue.add({}, {
|
|
repeat: {
|
|
cron: '0 9 * * *' // 9:00 AM daily
|
|
}
|
|
});
|
|
|
|
module.exports = warrantyAlertQueue;
|
|
```
|
|
|
|
### 5.3 SMS Fallback for Opt-Out Users
|
|
|
|
For users who don't want WhatsApp but want alerts, use SMS as fallback:
|
|
|
|
```javascript
|
|
async function sendAlert(boatId, alertType, details) {
|
|
const integration = await db.get(
|
|
'SELECT * FROM whatsapp_integrations WHERE boat_id = ?',
|
|
[boatId]
|
|
);
|
|
|
|
const prefs = await db.get(
|
|
'SELECT * FROM user_alert_preferences WHERE boat_id = ?',
|
|
[boatId]
|
|
);
|
|
|
|
if (prefs?.use_whatsapp) {
|
|
// Send via WhatsApp
|
|
await whatsappService.sendTemplate(integration.owner_phone, alertType, details);
|
|
} else if (prefs?.use_sms) {
|
|
// Send via SMS (Twilio)
|
|
await twilioService.sendSMS(integration.owner_phone, formatMessage(alertType, details));
|
|
} else {
|
|
// Send via email only
|
|
await emailService.send(integration.owner_email, alertType, details);
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 6. WhatsApp Business API Setup
|
|
|
|
### 6.1 Pre-requisites
|
|
|
|
1. **Meta Business Account** (free)
|
|
- Go to: https://business.facebook.com/
|
|
- Create new business account
|
|
- Verify identity
|
|
|
|
2. **WhatsApp Business App** (within Meta Business Suite)
|
|
- Add WhatsApp app to business account
|
|
- Verify phone number (your business number)
|
|
- Choose: Accept messages or Send messages
|
|
|
|
3. **Webhook Configuration** (required for inbound messages)
|
|
- NaviDocs webhook URL: `https://navidocs.boat/api/integrations/whatsapp/webhook`
|
|
- Webhook token: Generate secure random token
|
|
- Subscribe to: `messages`, `message_status`
|
|
|
|
### 6.2 Environment Variables
|
|
|
|
```bash
|
|
# .env file
|
|
WHATSAPP_BUSINESS_ACCOUNT_ID=<your_business_account_id>
|
|
WHATSAPP_BUSINESS_PHONE_ID=<your_phone_number_id>
|
|
WHATSAPP_ACCESS_TOKEN=<long_lived_access_token>
|
|
WHATSAPP_WEBHOOK_TOKEN=<secure_random_token>
|
|
WHATSAPP_API_VERSION=v18.0
|
|
|
|
# Optional: for SMS fallback
|
|
TWILIO_ACCOUNT_SID=<sid>
|
|
TWILIO_AUTH_TOKEN=<token>
|
|
TWILIO_PHONE_NUMBER=<+1234567890>
|
|
```
|
|
|
|
### 6.3 Step-by-Step Setup
|
|
|
|
#### Step 1: Create Meta Business Account
|
|
|
|
```bash
|
|
1. Go to https://business.facebook.com/
|
|
2. Click "Create account"
|
|
3. Fill in business name, email, business type
|
|
4. Verify via email
|
|
```
|
|
|
|
#### Step 2: Register Phone Number
|
|
|
|
```bash
|
|
1. In Business Settings → WhatsApp → Phone Numbers
|
|
2. Click "Add phone number"
|
|
3. Enter your business phone number
|
|
4. WhatsApp sends verification code via SMS to that number
|
|
5. Enter code in dashboard to verify
|
|
```
|
|
|
|
#### Step 3: Create API Token
|
|
|
|
```bash
|
|
1. Settings → Users and permissions → System users
|
|
2. Create new system user (role: admin)
|
|
3. Assign to App Roles: WhatsApp Business Management API
|
|
4. Generate Access Token (select expiration: 60 days or never expire)
|
|
5. Copy token to .env file
|
|
```
|
|
|
|
#### Step 4: Configure Webhook
|
|
|
|
```bash
|
|
# Get webhook settings from Meta Dashboard
|
|
1. Settings → Configuration → Webhooks
|
|
2. Edit "messages" webhook
|
|
3. Set URL: https://navidocs.boat/api/integrations/whatsapp/webhook
|
|
4. Generate webhook token (random string, 32+ chars)
|
|
5. Subscribe to fields: messages, message_status, message_template_status
|
|
6. Save
|
|
```
|
|
|
|
#### Step 5: Test Integration
|
|
|
|
```bash
|
|
# In your backend, test sending a message
|
|
curl -X POST \
|
|
"https://graph.instagram.com/v18.0/${WHATSAPP_BUSINESS_PHONE_ID}/messages" \
|
|
-H "Content-Type: application/json" \
|
|
-H "Authorization: Bearer ${WHATSAPP_ACCESS_TOKEN}" \
|
|
-d '{
|
|
"messaging_product": "whatsapp",
|
|
"recipient_type": "individual",
|
|
"to": "+41791234567",
|
|
"type": "text",
|
|
"text": { "body": "Hello from NaviDocs!" }
|
|
}'
|
|
|
|
# Expected response:
|
|
# {
|
|
# "messaging_product": "whatsapp",
|
|
# "contacts": [{"input":"41791234567","wa_id":"41791234567"}],
|
|
# "messages": [{"id":"wamid.xxx","message_status":"accepted"}]
|
|
# }
|
|
```
|
|
|
|
---
|
|
|
|
## 7. Cost Estimate
|
|
|
|
### 7.1 WhatsApp Business API Pricing
|
|
|
|
| Cost Component | Price | Notes |
|
|
|--------|-------|-------|
|
|
| **Inbound Messages** | Free | Unlimited (unlimited quality) |
|
|
| **Outbound Template Messages** | $0.0080 per message | Pre-approved templates (warranty alerts, etc.) |
|
|
| **Outbound Dialog Messages** | $0.0127 per message | Response within 24-hour window |
|
|
| **Outbound Session Messages** | $0.0170 per message | New conversation (pay per conversation start) |
|
|
|
|
### 7.2 Projected Monthly Costs
|
|
|
|
**Small Fleet Operator** (10 boats, owner + mechanic):
|
|
- Inbound: 100 messages/month = $0
|
|
- Outbound alerts: 50 template messages/month = $0.40
|
|
- Outbound support: 200 dialog messages/month = $2.54
|
|
- **Total: ~$3/month**
|
|
|
|
**Medium Dealer** (50 boats, 3 support staff):
|
|
- Inbound: 500 messages/month = $0
|
|
- Outbound alerts: 500 template messages/month = $4.00
|
|
- Outbound support: 1000 dialog messages/month = $12.70
|
|
- **Total: ~$17/month**
|
|
|
|
**Large Dealer** (200+ boats, enterprise):
|
|
- Inbound: 2000 messages/month = $0
|
|
- Outbound alerts: 3000 template messages/month = $24.00
|
|
- Outbound support: 5000 dialog messages/month = $63.50
|
|
- **Total: ~$88/month**
|
|
|
|
### 7.3 Total Implementation Costs
|
|
|
|
| Cost | Amount | Notes |
|
|
|------|--------|-------|
|
|
| **WhatsApp Business Account** | Free | One-time setup |
|
|
| **Phone Number Registration** | Free | One-time |
|
|
| **API Access Token** | Free | Included with Business Account |
|
|
| **Backend Development** | (included) | Express.js webhook + database schema |
|
|
| **Frontend Components** | (included) | Vue 3 integration component |
|
|
| **Monthly API Usage** | $3-88 | Depends on message volume |
|
|
| **Storage (PDFs, images)** | $0-20/mo | If using S3 (NaviDocs can use local storage) |
|
|
| **Support** | Free | Facebook/Meta developer support |
|
|
|
|
**Total First Year:** $36-1,056 (depending on scale)
|
|
|
|
---
|
|
|
|
## 8. Implementation Timeline
|
|
|
|
### Phase 1: Setup (15-20 minutes)
|
|
- [ ] Create Meta Business Account
|
|
- [ ] Verify phone number
|
|
- [ ] Generate API credentials
|
|
- [ ] Create `.env` variables
|
|
|
|
### Phase 2: Backend Integration (40-50 minutes)
|
|
- [ ] Add database schema (whatsapp_integrations, whatsapp_messages, warranty_alerts tables)
|
|
- [ ] Create webhook handler (`/routes/integrations/whatsapp.js`)
|
|
- [ ] Implement WhatsApp service (`services/whatsapp.js`)
|
|
- [ ] Test webhook with sample messages
|
|
|
|
### Phase 3: Frontend Integration (15-25 minutes)
|
|
- [ ] Create WhatsApp setup component (`WhatsAppIntegration.vue`)
|
|
- [ ] Add settings to boat detail page
|
|
- [ ] Implement verification flow
|
|
|
|
### Phase 4: Alert System (20-30 minutes)
|
|
- [ ] Create BullMQ job for warranty alerts
|
|
- [ ] Implement alert scheduling logic
|
|
- [ ] Test end-to-end alert flow
|
|
|
|
### Phase 5: Testing & Documentation (10-15 minutes)
|
|
- [ ] Test with real WhatsApp account
|
|
- [ ] Document message templates
|
|
- [ ] Create user guide
|
|
|
|
**Total Implementation Time: 60-90 minutes**
|
|
|
|
---
|
|
|
|
## 9. Required Credentials from User
|
|
|
|
Ask the boat owner/dealer for:
|
|
|
|
| Credential | Format | Why Needed |
|
|
|-----------|--------|-----------|
|
|
| **WhatsApp Business Phone Number** | E.164 format: +41791234567 | To link to their boat in NaviDocs |
|
|
| **Phone Number Verification** | SMS code received | To confirm they own the phone |
|
|
| **Alert Preferences** | Checkboxes | Which alerts to enable |
|
|
| **Time Zone** | IANA format: Europe/Zurich | To schedule alerts correctly |
|
|
|
|
**Initial Onboarding Flow:**
|
|
|
|
```
|
|
Step 1: User opens NaviDocs app
|
|
Step 2: Goes to Boat Settings → WhatsApp
|
|
Step 3: Clicks "Enable WhatsApp Integration"
|
|
Step 4: Enters phone number
|
|
Step 5: Receives SMS verification code
|
|
Step 6: Enters code to confirm
|
|
Step 7: Sets alert preferences (warranty, maintenance, recalls)
|
|
Step 8: Done!
|
|
```
|
|
|
|
---
|
|
|
|
## 10. Security Considerations
|
|
|
|
### 10.1 Data Privacy
|
|
|
|
- **Message Logs:** Store WhatsApp messages in encrypted field (`message_content` column)
|
|
- **Phone Numbers:** Hash phone numbers in non-critical queries, store hashed version in cache
|
|
- **Media Downloads:** Use HTTPS only, verify SSL certificates
|
|
- **Webhooks:** Sign all incoming webhooks with SHA-256 (Meta provides signature)
|
|
|
|
### 10.2 Webhook Verification
|
|
|
|
```javascript
|
|
// Verify webhook signature from Meta
|
|
function verifyWebhookSignature(req) {
|
|
const signature = req.headers['x-hub-signature-256'];
|
|
const body = req.rawBody; // Must be raw body, not parsed JSON
|
|
|
|
const hash = crypto
|
|
.createHmac('sha256', process.env.WHATSAPP_APP_SECRET)
|
|
.update(body)
|
|
.digest('hex');
|
|
|
|
const expectedSignature = `sha256=${hash}`;
|
|
return signature === expectedSignature;
|
|
}
|
|
|
|
// Apply middleware
|
|
app.post('/api/integrations/whatsapp/webhook', (req, res, next) => {
|
|
if (!verifyWebhookSignature(req)) {
|
|
return res.status(403).json({ error: 'Invalid signature' });
|
|
}
|
|
next();
|
|
});
|
|
```
|
|
|
|
### 10.3 Rate Limiting
|
|
|
|
```javascript
|
|
const rateLimit = require('express-rate-limit');
|
|
|
|
const whatsappLimiter = rateLimit({
|
|
windowMs: 60 * 1000, // 1 minute
|
|
max: 100, // 100 requests per minute
|
|
message: 'Too many WhatsApp messages, please try again later'
|
|
});
|
|
|
|
app.use('/api/integrations/whatsapp', whatsappLimiter);
|
|
```
|
|
|
|
### 10.4 Compliance
|
|
|
|
- **GDPR:** Delete message logs after 90 days unless user opts for longer retention
|
|
- **WhatsApp Terms:** Only send approved template messages (warranties, maintenance, recalls)
|
|
- **Opt-Out:** Respect user preferences; never spam unsolicited messages
|
|
|
|
---
|
|
|
|
## 11. Error Handling & Fallbacks
|
|
|
|
### 11.1 Common Error Scenarios
|
|
|
|
```javascript
|
|
const errorHandlers = {
|
|
// Webhook receiver down → Queue messages in Redis
|
|
webhook_unreachable: {
|
|
action: 'Queue message in Redis',
|
|
retry: 'Exponential backoff: 1s, 10s, 100s, then daily',
|
|
fallback: 'Send SMS alert instead'
|
|
},
|
|
|
|
// User phone not recognized → Send welcome message
|
|
unknown_user: {
|
|
action: 'Send welcome template',
|
|
retry: 'Ask user to "Register" via phone',
|
|
fallback: 'Manual registration in NaviDocs web app'
|
|
},
|
|
|
|
// Document too large → Ask user to send via web app
|
|
file_too_large: {
|
|
action: 'Send error message',
|
|
limit: '100 MB per file',
|
|
fallback: 'Direct upload to NaviDocs app'
|
|
},
|
|
|
|
// OCR fails (image quality) → Store raw image, mark for manual review
|
|
ocr_failure: {
|
|
action: 'Store image as-is',
|
|
retry: 'Queue for manual transcription by support',
|
|
fallback: 'User can manually add metadata in app'
|
|
}
|
|
};
|
|
```
|
|
|
|
---
|
|
|
|
## 12. Integration Checklist
|
|
|
|
- [ ] Meta Business Account created & verified
|
|
- [ ] WhatsApp Business API credentials obtained
|
|
- [ ] `.env` file configured with WHATSAPP_* variables
|
|
- [ ] Database schema updated (3 new tables)
|
|
- [ ] Express.js webhook handler implemented & tested
|
|
- [ ] WhatsApp service layer created
|
|
- [ ] Frontend Vue 3 component for setup
|
|
- [ ] Warranty alert job created & scheduled
|
|
- [ ] Webhook signature verification implemented
|
|
- [ ] Rate limiting configured
|
|
- [ ] Error logging in place
|
|
- [ ] User onboarding flow tested
|
|
- [ ] Document upload tested (PDF, image, text)
|
|
- [ ] Search results formatting tested
|
|
- [ ] Alert templates created in Meta dashboard
|
|
- [ ] Message templates created: warranty_expiring_alert, service_due_alert, recall_notice_alert
|
|
- [ ] SMS fallback configured (optional)
|
|
- [ ] GDPR compliance: message retention policy set
|
|
- [ ] Load testing: 100+ concurrent messages
|
|
- [ ] User documentation written
|
|
|
|
---
|
|
|
|
## Next Steps
|
|
|
|
1. **Request credentials** from user:
|
|
- Meta Business Account ID
|
|
- WhatsApp Business Phone Number
|
|
- Business address (for verification)
|
|
|
|
2. **Set up API credentials** (15 min):
|
|
- Create system user in Meta
|
|
- Generate access token
|
|
- Configure webhook
|
|
|
|
3. **Deploy backend** (40 min):
|
|
- Add database schema
|
|
- Implement webhook handler
|
|
- Test with cURL
|
|
|
|
4. **Deploy frontend** (15 min):
|
|
- Add WhatsApp setup component
|
|
- Test linking flow
|
|
|
|
5. **Create message templates** (5 min):
|
|
- Warranty expiring
|
|
- Service due
|
|
- Recall notice
|
|
|
|
6. **Go live**:
|
|
- Invite pilot users
|
|
- Monitor message volume
|
|
- Collect feedback
|
|
|
|
---
|
|
|
|
**Document Version:** 1.0
|
|
**Last Updated:** 2025-11-13
|
|
**Author:** NaviDocs Integration Team
|
|
**Status:** Ready for Development
|