Skip to content

Notifications: Twilio WhatsApp provider #42

Description

@manoahLinks

Summary

Implement the Twilio WhatsApp provider for sending rich ticket confirmations via WhatsApp. Uses the same Twilio API as SMS but with WhatsApp-specific formatting and approved message templates.

API Reference

  • Docs: https://www.twilio.com/docs/whatsapp/api
  • Endpoint: Same as SMS — POST /2010-04-01/Accounts/{SID}/Messages.json
  • Auth: Same as SMS — Account SID + Auth Token
  • From: whatsapp:+14155238886 (Twilio sandbox) or approved business number

Key Difference from SMS

The only API difference is the From and To format:

From: "whatsapp:+14155238886"     (instead of plain phone number)
To:   "whatsapp:+2348012345678"   (prefix with "whatsapp:")

WhatsApp Message Templates

WhatsApp Business API requires pre-approved templates for outbound messages. For the sandbox, freeform messages work.

Ticket Confirmation (Rich Message)

🎟️ *Ticket Confirmed!*

*Event:* Lagos Tech Summit 2026
*Date:* May 15, 2026 at 9:00 AM
*Venue:* Eko Convention Center, Lagos
*Ticket:* VIP
*Reference:* HOSTIT_TKT_A3F2B9C1

Your QR code is attached below. Show it at the entrance for entry.

View your ticket: https://hostit.ng/tickets/HOSTIT_TKT_A3F2B9C1

WhatsApp supports:

  • Bold text
  • Italic text
  • Media attachments (QR code image)
  • Links (auto-previewed)

Sending Media (QR Code)

await this.client.messages.create({
  to: 'whatsapp:+2348012345678',
  from: 'whatsapp:+14155238886',
  body: 'Your ticket for Lagos Tech Summit is confirmed! Show this QR at entry.',
  mediaUrl: ['https://cdn.hostit.ng/qr/A3F2B9C1.png'],  // must be publicly accessible URL
});

Implementation

// src/notifications/providers/twilio-whatsapp.provider.ts

@Injectable()
export class TwilioWhatsappProvider {
  private client: twilio.Twilio;

  constructor(private configService: ConfigService) {
    this.client = twilio(
      this.configService.get('TWILIO_ACCOUNT_SID'),
      this.configService.get('TWILIO_AUTH_TOKEN'),
    );
  }

  async send(params: {
    to: string;           // +234XXXXXXXXXX (will be prefixed with whatsapp:)
    body: string;
    mediaUrl?: string[];  // QR code URL
  }): Promise<void> {
    await this.client.messages.create({
      to: `whatsapp:${params.to}`,
      from: `whatsapp:${this.configService.get('TWILIO_WHATSAPP_NUMBER')}`,
      body: params.body,
      mediaUrl: params.mediaUrl,
    });
  }
}

Tasks

  • Create src/notifications/providers/twilio-whatsapp.provider.ts:
    • Reuse Twilio client (same SDK as SMS)
    • Prefix whatsapp: to From and To numbers
    • Support media attachments (QR code image URL)
    • Handle WhatsApp-specific errors (template not approved, 24hr window)
  • Create WhatsApp message templates:
    • renderWhatsappTemplate(type, data) → { body, mediaUrl? }
    • Use WhatsApp formatting (bold, italic)
    • Include QR code as media attachment
  • Add TWILIO_WHATSAPP_NUMBER to env config
  • Add development fallback: log WhatsApp message to console in dev mode

WhatsApp Business API Limitations

Limitation Detail
Template messages Outbound messages must use approved templates (except sandbox)
24-hour window Free-form replies only within 24h of user's last message
Media URLs Must be publicly accessible HTTPS URLs
Rate limits Varies by tier (1K-100K messages/day)

For initial development, use the Twilio WhatsApp Sandbox which allows freeform messages. Production requires approved templates via WhatsApp Business API.

Acceptance Criteria

  • WhatsApp messages sent via Twilio API
  • Phone numbers correctly prefixed with whatsapp:
  • QR code sent as media attachment (publicly accessible URL)
  • Rich formatting used (bold event name, structured layout)
  • Twilio errors handled gracefully
  • Dev mode: messages logged to console
  • Works with Twilio sandbox for development

Metadata

Metadata

Assignees

No one assigned

    Labels

    notificationsEmail, SMS, and WhatsApp notificationsphase-7Phase 7: Notifications

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions