Compliance & Regulations

Understand and implement compliance requirements for messaging regulations worldwide.

GDPR (Europe)

Data Processing

  • Obtain explicit consent before messaging
  • Provide opt-out mechanism
  • Honor data deletion requests
  • Maintain processing records

Implementation

// Check consent before sending
async function canMessageUser(userId: string) {
  const user = await db.users.findById(userId);

  return user.messagingConsent === true &&
         user.optedOutAt === null;
}

// Handle opt-out
app.post('/opt-out', async (req, res) => {
  await db.users.update(req.body.phoneNumber, {
    optedOutAt: new Date(),
    messagingConsent: false
  });

  res.send('You have been unsubscribed');
});

TCPA (United States)

Requirements

  • Written consent for marketing messages
  • Honor opt-outs within 24 hours
  • Maintain consent records
  • No messages before 8 AM or after 9 PM (recipient's timezone)

Best Practices

// Timezone-aware sending
async function sendInUserTimezone(userId: string, templateId: string) {
  const user = await db.users.findById(userId);
  const userTime = new Date().toLocaleString('en-US', {
    timeZone: user.timezone
  });
  const hour = new Date(userTime).getHours();

  // Only send between 9 AM and 8 PM
  if (hour >= 9 && hour < 20) {
    return client.messages.send({
      to: [user.phoneNumber],
      template: { id: templateId }
    });
  } else {
    // Queue for next available window
    await queueForTomorrow(userId, templateId);
  }
}

Country-Specific Requirements

CountryRequirement
AustraliaSPAM Act compliance, unsubscribe within 5 days
CanadaCASL - express consent required
UKPECR regulations, opt-in required
SingaporePDPA compliance
BrazilLGPD data protection

Opt-Out Handling

Handle opt-outs automatically via webhook (error code BUSINESS_007 indicates recipient opted out):

app.post('/webhooks/sent', async (req, res) => {
  // Acknowledge immediately
  res.sendStatus(200);

  const { field, payload } = req.body;

  if (field === 'messages' && payload.message_status === 'FAILED') {
    // Fetch the message via API for full error details (e.g. BUSINESS_007 = opted out)
    const message = await client.messages.get(payload.message_id);
    const errorCode = message.data?.error?.code;

    if (errorCode === 'BUSINESS_007') {
      await db.users.update(payload.inbound_number, {
        optedOutAt: new Date(),
        messagingConsent: false
      });
    }
  }
});

On this page