Overview

This guide covers common issues you might encounter during deployment and operation of your WhatsApp Team Inbox, along with detailed solutions.
Can’t find your issue here? Check our Common Issues page or contact support.

Quick Diagnostic Tools

Run these commands to quickly diagnose common issues:
# Check application health
curl http://localhost:3000/api/health

# Check database connectivity
curl http://localhost:3000/api/health/db

# Check WhatsApp connection
curl http://localhost:3000/api/health/whatsapp

# View application logs
pm2 logs team-inbox --lines 100

# Check system resources
top
# or
htop

WhatsApp Integration Issues

Symptoms

  • WhatsApp messages sent to your business number don’t appear in inbox
  • No webhook events logged

Diagnosis

# Check if webhook endpoint is accessible
curl https://your-domain.com/api/webhooks/whatsapp

# Should return 405 Method Not Allowed (GET not allowed, only POST)

# Check webhook logs
tail -f logs/webhooks.log

Solutions

1

Verify Webhook Configuration

  1. Go to Meta Developer Console
  2. Navigate to WhatsApp > Configuration
  3. Verify webhook URL is correct: https://your-domain.com/api/webhooks/whatsapp
  4. Check verify token matches your .env file
  5. Ensure these fields are subscribed:
    • messages
    • message_status
2

Check HTTPS Certificate

# Test SSL certificate
curl -vI https://your-domain.com

# Should see successful SSL handshake
# Check certificate is valid and not expired
If certificate issues:
# Renew Let's Encrypt certificate
sudo certbot renew
sudo systemctl reload nginx
3

Verify Firewall Rules

# Check if port 443 is open
sudo ufw status

# Allow HTTPS if not open
sudo ufw allow 443/tcp
sudo ufw reload
4

Test Webhook Manually

Send a test webhook event:
curl -X POST https://your-domain.com/api/webhooks/whatsapp \
  -H "Content-Type: application/json" \
  -d '{
    "object": "whatsapp_business_account",
    "entry": [{
      "changes": [{
        "value": {
          "messaging_product": "whatsapp",
          "metadata": {
            "phone_number_id": "YOUR_PHONE_NUMBER_ID"
          },
          "messages": [{
            "from": "1234567890",
            "id": "test_message_id",
            "timestamp": "1234567890",
            "text": { "body": "Test message" },
            "type": "text"
          }]
        }
      }]
    }]
  }'
Should return 200 OK and message should appear in inbox.
5

Check Application Logs

# Look for webhook processing errors
grep "webhook" logs/application.log
grep "error" logs/application.log

# Common errors:
# - Database connection failed
# - Invalid message format
# - Missing environment variables

Still Not Working?

  • Check Meta Status
  • Verify Permissions
  • Review Meta Logs
Visit Meta Status Dashboard to check if WhatsApp API is experiencing issues.

Symptoms

  • Replies from inbox don’t reach WhatsApp users
  • Error: “Failed to send message”
  • 400 or 403 errors from WhatsApp API

Common Causes

  • Invalid Access Token
  • 24-Hour Window Expired
  • Phone Number Not Verified
  • Rate Limits Exceeded
Error: Invalid OAuth access tokenSolution:
# Generate new permanent access token
# 1. Go to Meta Developer Console
# 2. WhatsApp > Configuration
# 3. Generate new system user token
# 4. Update .env file

# Test token
curl -X GET "https://graph.facebook.com/v18.0/debug_token?input_token=YOUR_TOKEN&access_token=YOUR_TOKEN"

Debug Message Sending

// Enable debug mode for WhatsApp API calls
WHATSAPP_API_DEBUG=true

// Check detailed error response
try {
  await sendMessage(messageData);
} catch (error) {
  console.error('WhatsApp API Error:', {
    status: error.response?.status,
    code: error.response?.data?.error?.code,
    message: error.response?.data?.error?.message,
    details: error.response?.data?.error?.error_data
  });
}

Symptoms

  • Cannot save webhook URL in Meta Developer Console
  • Error: “The callback URL or verify token couldn’t be validated”

Solution Steps

1

Check Verify Token

# In your .env file
WHATSAPP_WEBHOOK_VERIFY_TOKEN=your_secret_token

# This must match exactly what you enter in Meta Console
2

Test Verification Endpoint

# Meta sends a GET request like this
curl "https://your-domain.com/api/webhooks/whatsapp?hub.mode=subscribe&hub.challenge=CHALLENGE_ACCEPTED&hub.verify_token=your_secret_token"

# Should return exactly: CHALLENGE_ACCEPTED
3

Check Server Response

Your webhook endpoint should implement this:
app.get('/api/webhooks/whatsapp', (req, res) => {
  const mode = req.query['hub.mode'];
  const token = req.query['hub.verify_token'];
  const challenge = req.query['hub.challenge'];

  if (mode === 'subscribe' && token === process.env.WHATSAPP_WEBHOOK_VERIFY_TOKEN) {
    console.log('Webhook verified');
    res.status(200).send(challenge);
  } else {
    res.sendStatus(403);
  }
});
4

Verify HTTPS

  • Webhook URL must use HTTPS (not HTTP)
  • Certificate must be valid
  • No self-signed certificates allowed
# Test SSL
openssl s_client -connect your-domain.com:443 -servername your-domain.com

Symptoms

  • Images, videos, or documents not sending/receiving
  • Media appears as broken link
  • Download errors

Solutions

  • Receiving Media
  • Sending Media
  • Storage Issues
// Download media from WhatsApp
async function downloadMedia(mediaId) {
  // Get media URL
  const mediaInfo = await fetch(
    `https://graph.facebook.com/v18.0/${mediaId}`,
    {
      headers: {
        'Authorization': `Bearer ${process.env.WHATSAPP_ACCESS_TOKEN}`
      }
    }
  );

  const { url } = await mediaInfo.json();

  // Download media file
  const mediaFile = await fetch(url, {
    headers: {
      'Authorization': `Bearer ${process.env.WHATSAPP_ACCESS_TOKEN}`
    }
  });

  return mediaFile.buffer();
}
Common issues:
  • Media URLs expire after a few minutes - download immediately
  • Large files may timeout - increase request timeout
  • Check file storage permissions

Database Issues

Symptoms

  • ECONNREFUSED errors
  • too many clients already
  • Database timeouts

Solutions

1

Check Database is Running

# PostgreSQL
sudo systemctl status postgresql

# Start if not running
sudo systemctl start postgresql

# For Docker
docker ps | grep postgres
docker-compose up -d postgres
2

Verify Connection String

# Test connection
psql "postgresql://user:pass@host:5432/dbname"

# Or use environment variable
psql $DATABASE_URL

# Common issues:
# - Wrong password
# - Wrong host/port
# - Database doesn't exist
# - User lacks permissions
3

Fix Connection Pool

# In .env
DATABASE_POOL_MIN=2
DATABASE_POOL_MAX=20

# Check current connections
SELECT count(*) FROM pg_stat_activity WHERE datname = 'team_inbox';

# Kill idle connections
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE datname = 'team_inbox'
AND state = 'idle'
AND state_change < current_timestamp - INTERVAL '10 minutes';
4

Increase Max Connections

# Edit postgresql.conf
sudo nano /etc/postgresql/14/main/postgresql.conf

# Increase max_connections
max_connections = 200

# Restart PostgreSQL
sudo systemctl restart postgresql

Symptoms

  • Migration fails to run
  • Schema out of sync
  • Cannot start application

Solutions

# Check migration status
npx prisma migrate status

# Mark migration as applied (if already applied manually)
npx prisma migrate resolve --applied <migration_name>

# Mark migration as rolled back
npx prisma migrate resolve --rolled-back <migration_name>

# Reset database (DEVELOPMENT ONLY - deletes all data!)
npx prisma migrate reset

# Deploy migrations
npx prisma migrate deploy

# Generate Prisma Client
npx prisma generate

Common Issues

  • Missing Migration
  • Failed Migration
  • Schema Drift
# If migration file is missing
# Recreate migration from schema
npx prisma migrate dev --name recreate_missing_migration

# Or manually create migration SQL file in prisma/migrations/

Diagnosis

-- Enable slow query logging
ALTER DATABASE team_inbox SET log_min_duration_statement = 1000;  -- 1 second

-- Find slow queries
SELECT query, mean_exec_time, calls
FROM pg_stat_statements
ORDER BY mean_exec_time DESC
LIMIT 10;

-- Analyze specific query
EXPLAIN ANALYZE SELECT * FROM conversations WHERE assigned_to = 'user-id';

Solutions

1

Add Missing Indexes

-- Common indexes for Team Inbox
CREATE INDEX CONCURRENTLY idx_conversations_assigned
  ON conversations(assigned_to);

CREATE INDEX CONCURRENTLY idx_conversations_status_updated
  ON conversations(status, updated_at DESC);

CREATE INDEX CONCURRENTLY idx_messages_conversation_created
  ON messages(conversation_id, created_at DESC);

CREATE INDEX CONCURRENTLY idx_contacts_phone
  ON contacts(phone);

-- Check index usage
SELECT schemaname, tablename, indexname, idx_scan
FROM pg_stat_user_indexes
ORDER BY idx_scan ASC;
2

Optimize Queries

// Bad: N+1 query problem
const conversations = await prisma.conversation.findMany();
for (const conv of conversations) {
  const messages = await prisma.message.findMany({
    where: { conversationId: conv.id }
  });
}

// Good: Use include/join
const conversations = await prisma.conversation.findMany({
  include: {
    messages: {
      take: 10,
      orderBy: { createdAt: 'desc' }
    }
  }
});
3

Use Database Caching

// Cache frequent queries in Redis
const cacheKey = `conversations:user:${userId}`;
let conversations = await redis.get(cacheKey);

if (!conversations) {
  conversations = await prisma.conversation.findMany({
    where: { assignedTo: userId }
  });
  await redis.set(cacheKey, JSON.stringify(conversations), 'EX', 300);
}
4

Run VACUUM

-- Regular maintenance
VACUUM ANALYZE conversations;
VACUUM ANALYZE messages;

-- Full vacuum (requires lock, do during maintenance window)
VACUUM FULL;

Application Errors

Diagnosis

# Check application logs
pm2 logs team-inbox --err --lines 50

# Or direct logs
tail -f logs/error.log

# Check for common issues:
# - Port already in use
# - Missing environment variables
# - Syntax errors
# - Missing dependencies

Common Solutions

  • Port Already in Use
  • Missing Environment Variables
  • Dependency Issues
  • Syntax Errors
# Find process using port 3000
sudo lsof -i :3000

# Kill the process
sudo kill -9 <PID>

# Or use different port
PORT=3001 npm start

Symptoms

  • Memory usage constantly increasing
  • Application becomes slow over time
  • Out of memory errors

Diagnosis

# Monitor memory usage
pm2 monit

# Or use Node.js built-in
node --inspect app.js
# Then open chrome://inspect

# Take heap snapshot
kill -USR2 <process_pid>

# Check for common causes:
# - Event listeners not removed
# - Global variables growing
# - Cache without size limit
# - Database connections not closed

Solutions

// Fix: Remove event listeners
const handler = (data) => { /* ... */ };
socket.on('message', handler);
// Later:
socket.off('message', handler);

// Fix: Limit cache size
const cache = new Map();
const MAX_CACHE_SIZE = 1000;

function addToCache(key, value) {
  if (cache.size >= MAX_CACHE_SIZE) {
    const firstKey = cache.keys().next().value;
    cache.delete(firstKey);
  }
  cache.set(key, value);
}

// Fix: Close database connections
try {
  const result = await prisma.conversation.findMany();
  return result;
} finally {
  // Connections automatically returned to pool with Prisma
  // But ensure no lingering references
}

// Temporary fix: Restart periodically
// Add to crontab:
// 0 4 * * * pm2 restart team-inbox

Diagnosis

# Check CPU usage
top -p $(pgrep -f "node.*team-inbox")

# Profile CPU
node --prof app.js
# Run workload
# Stop server
node --prof-process isolate-*.log > profile.txt

Common Causes

  • Inefficient Loops
  • Too Many Concurrent Requests
  • RegEx Performance
// Bad: Nested loops with large datasets
conversations.forEach(conv => {
  messages.forEach(msg => {
    if (msg.conversationId === conv.id) {
      // Process
    }
  });
});

// Good: Use Map for O(1) lookup
const messagesByConv = new Map();
messages.forEach(msg => {
  if (!messagesByConv.has(msg.conversationId)) {
    messagesByConv.set(msg.conversationId, []);
  }
  messagesByConv.get(msg.conversationId).push(msg);
});

conversations.forEach(conv => {
  const msgs = messagesByConv.get(conv.id) || [];
  // Process
});

Email Issues

Solutions by Provider

  • SendGrid
  • AWS SES
  • SMTP
# Test API key
curl -X POST https://api.sendgrid.com/v3/mail/send \
  -H "Authorization: Bearer $SENDGRID_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "personalizations": [{
      "to": [{"email": "test@example.com"}]
    }],
    "from": {"email": "noreply@yourdomain.com"},
    "subject": "Test",
    "content": [{"type": "text/plain", "value": "Test"}]
  }'

# Check SendGrid activity
# Visit: https://app.sendgrid.com/email_activity

# Common issues:
# - API key invalid or expired
# - Sender not verified
# - Account suspended

Solutions

1

Set Up SPF Record

# Add to your DNS
Type: TXT
Host: @
Value: v=spf1 include:sendgrid.net ~all
# Or for AWS SES:
Value: v=spf1 include:amazonses.com ~all
2

Set Up DKIM

# SendGrid provides these records
# Add each to your DNS:
Type: CNAME
Host: s1._domainkey
Value: s1.domainkey.u123456.wl.sendgrid.net

Type: CNAME
Host: s2._domainkey
Value: s2.domainkey.u123456.wl.sendgrid.net
3

Set Up DMARC

Type: TXT
Host: _dmarc
Value: v=DMARC1; p=quarantine; rua=mailto:dmarc@yourdomain.com
4

Test Email Deliverability

  1. Send test email to mail-tester.com
  2. Check spam score (aim for 9/10 or higher)
  3. Review and fix flagged issues
  4. Re-test after fixes

Performance Issues

Quick Fixes

# Enable caching
CACHE_ENABLED=true
REDIS_URL=redis://localhost:6379

# Optimize database queries (add indexes)
# See Database Issues > Slow queries

# Enable compression
npm install compression
const compression = require('compression');
app.use(compression());

Monitor Performance

// Add request timing middleware
app.use((req, res, next) => {
  const start = Date.now();
  res.on('finish', () => {
    const duration = Date.now() - start;
    if (duration > 1000) {
      console.warn(`Slow request: ${req.method} ${req.path} ${duration}ms`);
    }
  });
  next();
});

Getting Help

If you’re still stuck after trying these solutions:

Debug Mode

Enable comprehensive logging:
# In .env
NODE_ENV=development
DEBUG=*
LOG_LEVEL=debug
WHATSAPP_API_DEBUG=true
DATABASE_LOGGING=true
EMAIL_DEBUG=true
This will provide detailed logs for troubleshooting all components.