1. Go to https://console.twilio.com
2. Sign up for a free account (requires phone verification)
3. Navigate to: Messaging → Try it Out → Send a WhatsApp message
4. Follow instructions to join the sandbox:
- Send "join <your-sandbox-keyword>" to the Twilio WhatsApp number
5. Configure the webhook:
- "WHEN A MESSAGE COMES IN" → https://your-backend-url.com/api/whatsapp/webhook
- Method: POST
Since Twilio needs a public URL to send webhooks during development:
# Install ngrok
npm install -g ngrok
# Start your backend server
cd server
npm run dev
# In another terminal, expose port 5000
ngrok http 5000Copy the ngrok https:// URL and paste it in Twilio's webhook configuration:
https://abc123.ngrok.io/api/whatsapp/webhook
1. Send "Hi" to the Twilio WhatsApp sandbox number
2. You should receive the welcome/daily brief message
3. Reply with "1" to get today's task
4. Reply with "2" to see portfolio
5. Reply with "help" for all commands
6. Reply with "streak" to see streak status
Add to server/server.js at the bottom (before app.listen):
// ============ SCHEDULED TASKS ============
const cron = require('node-cron');
const { resetBrokenStreaks } = require('./src/services/streakService');
const { sendWhatsAppMessage } = require('./src/config/twilio');
const { supabaseAdmin } = require('./src/config/database');
// Reset broken streaks every day at midnight IST (18:30 UTC)
cron.schedule('30 18 * * *', async () => {
console.log('⏰ Running daily streak reset...');
await resetBrokenStreaks();
});
// Send daily reminders at 9 AM IST (03:30 UTC)
cron.schedule('30 3 * * *', async () => {
console.log('📱 Sending daily WhatsApp reminders...');
try {
// Get users who opted in and haven't completed today's task
const today = new Date().toISOString().split('T')[0];
const { data: users } = await supabaseAdmin
.from('profiles')
.select('phone, full_name, current_streak')
.eq('whatsapp_opted_in', true)
.neq('last_activity_date', today)
.not('phone', 'is', null);
for (const user of users || []) {
const message = user.current_streak > 0
? `🔥 Good morning, ${user.full_name.split(' ')[0]}! Your ${user.current_streak}-day streak is at risk! Complete a quick 5-min task to keep it alive.\n\nReply *1* to get today's task.`
: `🌅 Good morning, ${user.full_name.split(' ')[0]}! Start your civic journey with a quick 5-min task today.\n\nReply *1* to begin!`;
try {
await sendWhatsAppMessage(user.phone, message);
} catch (err) {
console.error(`Failed to send reminder to ${user.phone}:`, err.message);
}
// Rate limiting — wait 1 second between messages
await new Promise(resolve => setTimeout(resolve, 1000));
}
console.log(`📱 Sent reminders to ${users?.length || 0} users`);
} catch (error) {
console.error('Daily reminder error:', error);
}
});