Skip to main content

Overview

Message reactions allow you to respond to any message with an emoji, providing a quick and expressive way to acknowledge, appreciate, or react to content without sending a full message. This feature enhances user engagement and reduces message clutter.

Key Features

  • Emoji Reactions: React with any emoji to any message
  • Predefined Constants: 14 commonly used emoji reactions
  • Convenience Methods: Dedicated methods for popular reactions
  • Remove Reactions: Ability to remove reactions from messages
  • Universal Support: React to any message type (text, media, interactive, etc.)

How Reactions Work

When you send a reaction, it appears as a small emoji overlay on the original message, similar to social media platforms. Users can see who reacted and with which emoji. Visual Example:
[Original Message]
📸 Here's the photo from our meeting

[With Reaction]
📸 Here's the photo from our meeting  👍  ❤️  🔥
    ↳ You reacted with 👍

Reaction Interface

export interface ReactionMessage extends BaseMessage {
  type: WhatsAppMessageType.REACTION;
  reaction: {
    message_id: string;  // ID of message to react to
    emoji: string;       // Emoji to use as reaction
  };
}

export interface ReactionResponse {
  success: boolean;
  messageId?: string;
  error?: string;
}

Available Emojis

Predefined Emoji Constants

export const REACTION_EMOJIS = {
  LIKE: '👍',
  LOVE: '❤️',
  LAUGH: '😂',
  WOW: '😮',
  SAD: '😢',
  ANGRY: '😠',
  THUMBS_UP: '👍',
  THUMBS_DOWN: '👎',
  HEART: '❤️',
  HEART_EYES: '😍',
  FIRE: '🔥',
  CLAP: '👏',
  CHECK: '✅',
  CROSS: '❌'
} as const;

export type ReactionEmoji = typeof REACTION_EMOJIS[keyof typeof REACTION_EMOJIS] | string;

Reaction Methods

Primary Reaction Method

// Send any emoji reaction
async sendReaction(to: string, messageId: string, emoji: ReactionEmoji): Promise<ReactionResponse>

// Remove a reaction
async removeReaction(to: string, messageId: string): Promise<ReactionResponse>

Convenience Methods

// Common reactions
async reactWithLike(to: string, messageId: string): Promise<ReactionResponse>
async reactWithLove(to: string, messageId: string): Promise<ReactionResponse>
async reactWithLaugh(to: string, messageId: string): Promise<ReactionResponse>
async reactWithWow(to: string, messageId: string): Promise<ReactionResponse>
async reactWithSad(to: string, messageId: string): Promise<ReactionResponse>
async reactWithAngry(to: string, messageId: string): Promise<ReactionResponse>

// Gesture reactions
async reactWithThumbsUp(to: string, messageId: string): Promise<ReactionResponse>
async reactWithThumbsDown(to: string, messageId: string): Promise<ReactionResponse>
async reactWithClap(to: string, messageId: string): Promise<ReactionResponse>

// Emotion reactions
async reactWithHeart(to: string, messageId: string): Promise<ReactionResponse>
async reactWithHeartEyes(to: string, messageId: string): Promise<ReactionResponse>
async reactWithFire(to: string, messageId: string): Promise<ReactionResponse>

// Status reactions
async reactWithCheck(to: string, messageId: string): Promise<ReactionResponse>
async reactWithCross(to: string, messageId: string): Promise<ReactionResponse>

Usage Examples

Basic Reactions

import { WhatsAppClient, REACTION_EMOJIS } from 'whatsapp-client-sdk';

const client = new WhatsAppClient(accessToken, phoneNumberId);

// React with a thumbs up
await client.reactWithLike('+1234567890', messageId);

// React with a custom emoji
await client.sendReaction('+1234567890', messageId, '🎉');

// React using predefined constants
await client.sendReaction('+1234567890', messageId, REACTION_EMOJIS.FIRE);

// Remove a reaction
await client.removeReaction('+1234567890', messageId);

Convenience Method Examples

// Positive reactions
await client.reactWithLike(userPhone, messageId);        // 👍
await client.reactWithLove(userPhone, messageId);        // ❤️
await client.reactWithFire(userPhone, messageId);        // 🔥
await client.reactWithClap(userPhone, messageId);        // 👏
await client.reactWithCheck(userPhone, messageId);       // ✅

// Emotional reactions
await client.reactWithLaugh(userPhone, messageId);       // 😂
await client.reactWithWow(userPhone, messageId);         // 😮
await client.reactWithHeartEyes(userPhone, messageId);   // 😍

// Negative reactions
await client.reactWithSad(userPhone, messageId);         // 😢
await client.reactWithAngry(userPhone, messageId);       // 😠
await client.reactWithThumbsDown(userPhone, messageId);  // 👎
await client.reactWithCross(userPhone, messageId);       // ❌

Webhook Integration

Automatic reactions based on message content:
const webhookProcessor = client.createWebhookProcessor({
  onTextMessage: async (message) => {
    const text = message.text.toLowerCase();

    // Auto-react to positive messages
    if (text.includes('awesome') || text.includes('amazing')) {
      await client.reactWithFire(message.from, message.id);
    }

    // React to gratitude
    else if (text.includes('thank') || text.includes('gracias')) {
      await client.reactWithHeart(message.from, message.id);
    }

    // React to questions
    else if (text.includes('?')) {
      await client.reactWithWow(message.from, message.id);
    }

    // Default positive reaction
    else {
      await client.reactWithLike(message.from, message.id);
    }
  },

  onImageMessage: async (message) => {
    // React to images with heart eyes
    await client.reactWithHeartEyes(message.from, message.id);
  },

  onVideoMessage: async (message) => {
    // React to videos with fire
    await client.reactWithFire(message.from, message.id);
  },

  onAudioMessage: async (message) => {
    // React to audio with thumbs up
    await client.reactWithThumbsUp(message.from, message.id);
  }
});

Advanced Use Cases

Smart Customer Service Reactions

class SmartReactionBot {
  async processCustomerMessage(message) {
    const content = message.text?.toLowerCase() || '';

    // Categorize and react accordingly
    if (this.isComplaint(content)) {
      await client.reactWithSad(message.from, message.id);
      await this.escalateToSupport(message);
    }

    else if (this.isCompliment(content)) {
      await client.reactWithHeartEyes(message.from, message.id);
      await this.thankCustomer(message);
    }

    else if (this.isUrgent(content)) {
      await client.reactWithWow(message.from, message.id);
      await this.prioritizeMessage(message);
    }

    else if (this.isOrder(content)) {
      await client.reactWithCheck(message.from, message.id);
      await this.processOrder(message);
    }
  }

  isComplaint(text) {
    return text.includes('problem') || text.includes('issue') || text.includes('wrong');
  }

  isCompliment(text) {
    return text.includes('great') || text.includes('excellent') || text.includes('perfect');
  }

  isUrgent(text) {
    return text.includes('urgent') || text.includes('asap') || text.includes('emergency');
  }

  isOrder(text) {
    return text.includes('order') || text.includes('buy') || text.includes('purchase');
  }
}

Reaction-Based Feedback System

class FeedbackCollector {
  async sendFeedbackRequest(userPhone) {
    const response = await client.sendText(
      userPhone,
      'How was your experience with our service today?'
    );

    // Store message ID for reaction tracking
    this.pendingFeedback.set(response.messages[0].id, {
      userPhone,
      timestamp: Date.now()
    });
  }

  async handleReactionWebhook(reactionData) {
    const { message_id, emoji, from } = reactionData;

    if (this.pendingFeedback.has(message_id)) {
      const feedback = this.analyzeFeedback(emoji);

      await this.storeFeedback(from, feedback);

      // Thank with appropriate reaction
      if (feedback.rating >= 4) {
        await client.reactWithLove(from, message_id);
      } else if (feedback.rating >= 3) {
        await client.reactWithThumbsUp(from, message_id);
      } else {
        await client.reactWithSad(from, message_id);
      }
    }
  }

  analyzeFeedback(emoji) {
    const feedbackMap = {
      '😍': { rating: 5, sentiment: 'excellent' },
      '❤️': { rating: 5, sentiment: 'love' },
      '🔥': { rating: 5, sentiment: 'amazing' },
      '👍': { rating: 4, sentiment: 'good' },
      '👏': { rating: 4, sentiment: 'satisfied' },
      '😊': { rating: 3, sentiment: 'okay' },
      '😐': { rating: 2, sentiment: 'neutral' },
      '👎': { rating: 1, sentiment: 'poor' },
      '😢': { rating: 1, sentiment: 'disappointed' },
      '😠': { rating: 1, sentiment: 'angry' }
    };

    return feedbackMap[emoji] || { rating: 3, sentiment: 'unknown' };
  }
}

Multi-Reaction Conversation Flow

async function handleOrderProcess(message) {
  const orderId = extractOrderId(message.text);

  if (orderId) {
    // Acknowledge order inquiry
    await client.reactWithCheck(message.from, message.id);

    // Send order status
    const orderStatus = await getOrderStatus(orderId);
    const statusMessage = await client.sendText(
      message.from,
      `Order #${orderId} status: ${orderStatus.status}`
    );

    // React based on status
    switch (orderStatus.status) {
      case 'delivered':
        await client.reactWithCheck(message.from, statusMessage.messages[0].id);
        break;
      case 'shipped':
        await client.reactWithFire(message.from, statusMessage.messages[0].id);
        break;
      case 'processing':
        await client.reactWithThumbsUp(message.from, statusMessage.messages[0].id);
        break;
      case 'cancelled':
        await client.reactWithSad(message.from, statusMessage.messages[0].id);
        break;
    }
  }
}

Custom Emoji Reactions

Beyond the predefined constants, you can use any emoji:
// Custom emojis
await client.sendReaction(userPhone, messageId, '🎉');  // Party
await client.sendReaction(userPhone, messageId, '🌟');  // Star
await client.sendReaction(userPhone, messageId, '💯');  // 100
await client.sendReaction(userPhone, messageId, '🚀');  // Rocket
await client.sendReaction(userPhone, messageId, '☕');  // Coffee
await client.sendReaction(userPhone, messageId, '🎵');  // Music
await client.sendReaction(userPhone, messageId, '📝');  // Note
await client.sendReaction(userPhone, messageId, '💡');  // Idea

// Seasonal reactions
await client.sendReaction(userPhone, messageId, '🎄');  // Christmas
await client.sendReaction(userPhone, messageId, '🎃');  // Halloween
await client.sendReaction(userPhone, messageId, '💝');  // Gift
await client.sendReaction(userPhone, messageId, '🌺');  // Flower

Message Structure

A reaction message has this structure:
{
  messaging_product: 'whatsapp',
  recipient_type: 'individual',
  to: '+1234567890',
  type: 'reaction',
  reaction: {
    message_id: 'wamid.HBgLMTY1MDM4Nzk0MzkVAgARGBJDQjZCMzlEQUE4OTJCMTE4RTUA',
    emoji: '👍'
  }
}
To remove a reaction:
{
  messaging_product: 'whatsapp',
  recipient_type: 'individual',
  to: '+1234567890',
  type: 'reaction',
  reaction: {
    message_id: 'wamid.HBgLMTY1MDM4Nzk0MzkVAgARGBJDQjZCMzlEQUE4OTJCMTE4RTUA',
    emoji: ''  // Empty string removes the reaction
  }
}

Best Practices

1. Appropriate Reaction Selection

// ✅ Good: Match reaction to content
await client.reactWithFire(userPhone, messageId);     // For exciting content
await client.reactWithHeart(userPhone, messageId);    // For appreciation
await client.reactWithCheck(userPhone, messageId);    // For confirmations
await client.reactWithThumbsUp(userPhone, messageId); // For approval

// ❌ Avoid: Inappropriate reactions
// Don't use sad face for positive content
// Don't overuse fire emoji for everything

2. Reaction Timing

// ✅ Good: React promptly to user messages
webhookProcessor.onTextMessage = async (message) => {
  // Quick acknowledgment reaction
  await client.reactWithLike(message.from, message.id);

  // Then process and respond
  const response = await processMessage(message);
  await client.sendText(message.from, response);
};

// ❌ Avoid: Delayed reactions without context
// Don't react to messages hours later without explanation

3. Reaction Combinations

// ✅ Good: Use reactions + messages for rich communication
async function acknowledgeOrder(message, orderDetails) {
  // React to acknowledge receipt
  await client.reactWithCheck(message.from, message.id);

  // Follow with detailed response
  await client.sendText(
    message.from,
    `Order confirmed! 🎉\n\nOrder #${orderDetails.id}\nTotal: $${orderDetails.total}\nETA: ${orderDetails.eta}`
  );
}

4. Error Handling

async function safeReaction(userPhone, messageId, emoji) {
  try {
    const response = await client.sendReaction(userPhone, messageId, emoji);

    if (!response.success) {
      console.log('Reaction failed:', response.error);
      // Optionally try alternative reaction
      await client.reactWithThumbsUp(userPhone, messageId);
    }
  } catch (error) {
    console.error('Reaction error:', error);
    // Don't let reaction failures break the flow
  }
}

Limitations

  • One Reaction per User: You can only have one active reaction per message
  • Message Age: Can only react to messages within the last 30 days
  • Emoji Support: Limited to standard Unicode emojis
  • Rate Limits: Reactions count toward your messaging rate limits
  • Remove Only Own: You can only remove your own reactions

Migration Guide

Adding Reactions to Existing Workflows

// Before: Simple acknowledgment
webhookProcessor.onTextMessage = async (message) => {
  await client.sendText(message.from, 'Message received');
};

// After: Reaction + response
webhookProcessor.onTextMessage = async (message) => {
  // Quick reaction for immediate feedback
  await client.reactWithThumbsUp(message.from, message.id);

  // Optional follow-up message if needed
  if (requiresResponse(message)) {
    await client.sendText(message.from, 'Thanks! I\'ll get back to you shortly.');
  }
};

Reaction Analytics

Track reaction patterns for insights:
class ReactionAnalytics {
  constructor() {
    this.reactionStats = new Map();
  }

  async trackReaction(userPhone, messageId, emoji) {
    await client.sendReaction(userPhone, messageId, emoji);

    // Track for analytics
    const key = `${userPhone}:${emoji}`;
    const count = this.reactionStats.get(key) || 0;
    this.reactionStats.set(key, count + 1);
  }

  getTopReactions() {
    return Array.from(this.reactionStats.entries())
      .sort(([,a], [,b]) => b - a)
      .slice(0, 10);
  }

  getUserPreferences(userPhone) {
    const userReactions = Array.from(this.reactionStats.entries())
      .filter(([key]) => key.startsWith(userPhone))
      .map(([key, count]) => [key.split(':')[1], count]);

    return userReactions.sort(([,a], [,b]) => b - a);
  }
}

Next Steps

I