Skip to main content

Twilio Regulatory Compliance Provider

🎯 Overview

The Regulatory Compliance Provider (Providers/regulatory-compliance.js) manages regulatory requirements for phone number provisioning in countries that require identity verification and documentation.

Source: external/Integrations/Twilio/Providers/regulatory-compliance.js

Key Capabilities:

  • Fetch regulatory requirements by country/number type
  • Get list of countries with regulatory requirements
  • Cache regulation data for performance (24-hour TTL)
  • Support for different end user types (individual, business)
  • Parse and structure complex regulation responses

Why Regulatory Compliance?

Many countries require identity verification before purchasing phone numbers:

  • United States: A2P 10DLC registration for SMS
  • United Kingdom: Address verification for all numbers
  • Germany: Business registration documentation
  • Australia: Identity verification for local numbers

🔌 Provider Functions

getRegulationRequirements()

Fetch regulatory requirements for phone numbers in a specific country.

Signature:

const getRegulationRequirements = async(
isoCountry,
(numberType = 'local'),
(endUserType = 'business'),
(twilioCredentials = null),
);

Parameters:

ParameterTypeRequiredDefaultDescription
isoCountryString-ISO 3166-1 alpha-2 country code (GB, US, DE, AU, etc.)
numberTypeString'local'Phone number type: 'local', 'mobile', 'toll-free'
endUserTypeString'business'End user type: 'individual', 'business'
twilioCredentialsObjectnullOptional credentials: {accountSid, authToken}

Returns:

Promise<{
hasRequirements: boolean; // Whether country has requirements
regulationSid?: string; // Regulation SID (if requirements exist)
friendlyName?: string; // Regulation name
isoCountry?: string; // Country code
numberType?: string; // Number type
endUserType?: string; // End user type
requirements?: {
address: Array<{
// Address requirements
constraint: string;
fields: string[]; // Required fields
detailed_fields: Array<{
machine_name: string; // Field name (e.g., 'street')
friendly_name: string; // Display name (e.g., 'Street')
description: string;
constraint: string;
}>;
name: string;
requirement_name: string;
type: string;
}>;
end_user: Array<{
// End user information requirements
constraint: string;
fields: string[];
name: string;
requirement_name: string;
type: string;
}>;
supporting_document: Array<{
// Supporting documents required
accepted_documents: string[];
constraint: string;
name: string;
requirement_name: string;
type: string;
}>;
};
message?: string; // Message if no requirements
}>;

Business Logic:

  1. Check Cache First

    • Caches results for 24 hours (production only)
    • Cache key: ${isoCountry}_${numberType}_${endUserType}
    • Improves performance and reduces API calls
  2. Initialize Twilio Client

    • Uses provided credentials OR environment defaults
    • Logs which credentials are being used
  3. Fetch Regulations from Twilio

    • Calls client.numbers.v2.regulatoryCompliance.regulations.list()
    • Includes includeConstraints: true for detailed field info
  4. Parse and Structure Response

    • Determines if requirements exist
    • Structures nested requirement objects
    • Adds standard address fields
  5. Cache and Return

    • Stores in cache with timestamp
    • Returns structured requirements

Example Usage:

const complianceProvider = require('./Providers/regulatory-compliance');

// Check UK requirements for local numbers
const ukRequirements = await complianceProvider.getRegulationRequirements(
'GB',
'local',
'business',
);

console.log('Has requirements:', ukRequirements.hasRequirements); // true
console.log('Regulation:', ukRequirements.friendlyName); // "UK Local - Business"

if (ukRequirements.hasRequirements) {
// Check what's required
console.log('Address required:', ukRequirements.requirements.address.length > 0);
console.log('End user info required:', ukRequirements.requirements.end_user.length > 0);
console.log('Documents required:', ukRequirements.requirements.supporting_document.length > 0);

// Get address fields
if (ukRequirements.requirements.address.length > 0) {
const addressReq = ukRequirements.requirements.address[0];
console.log('Required address fields:', addressReq.fields);
// Output: ['customer_name', 'street', 'city', 'region', 'postal_code', 'iso_country']
}
}

Check US A2P Requirements:

// US requires A2P registration for SMS
const usRequirements = await complianceProvider.getRegulationRequirements(
'US',
'local',
'business',
);

console.log('US has requirements:', usRequirements.hasRequirements); // true
console.log('Required documents:', usRequirements.requirements.supporting_document);
// May require business registration, tax ID, etc.

Country with No Requirements:

// Some countries have no requirements
const caRequirements = await complianceProvider.getRegulationRequirements(
'CA',
'local',
'business',
);

console.log('Has requirements:', caRequirements.hasRequirements); // false
console.log('Message:', caRequirements.message);
// Output: "No regulatory requirements found for this country/number type combination"

Using Custom Credentials:

// Use subaccount credentials
const requirements = await complianceProvider.getRegulationRequirements('GB', 'local', 'business', {
accountSid: 'AC1234567890abcdef',
authToken: 'your_auth_token',
});

Data Flow:

sequenceDiagram
participant API as DashClicks API
participant Provider as Compliance Provider
participant Cache as In-Memory Cache
participant Twilio as Twilio Regulations API

API->>Provider: getRegulationRequirements(GB, local, business)
Provider->>Cache: Check cache(GB_local_business)

alt Cache hit (< 24 hours)
Cache-->>Provider: Return cached data
Provider-->>API: Return requirements
else Cache miss or expired
Provider->>Twilio: regulations.list()
Note over Twilio: includeConstraints: true
Twilio-->>Provider: Regulation data
Provider->>Provider: Parse response
Provider->>Cache: Store in cache (24h TTL)
Provider-->>API: Return requirements
end

style Provider fill:#e3f2fd
style Cache fill:#e8f5e9
style Twilio fill:#fff4e6

getAvailableCountries()

Get list of all countries that have regulatory requirements.

Signature:

const getAvailableCountries = async();

Returns:

Promise<Array<string>>; // Array of ISO country codes (e.g., ['GB', 'US', 'DE', 'AU'])

Business Logic:

  1. Check Cache

    • Uses cache key: 'available_countries'
    • 24-hour TTL
  2. Fetch All Regulations

    • Calls client.numbers.v2.regulatoryCompliance.regulations.list()
    • No filters - gets all regulations
  3. Extract Unique Countries

    • Maps through regulations
    • Extracts isoCountry field
    • Returns unique country codes
  4. Cache and Return

Example Usage:

// Get all countries with regulatory requirements
const countries = await complianceProvider.getAvailableCountries();

console.log('Countries with requirements:', countries);
// Output: ['GB', 'US', 'DE', 'AU', 'AT', 'BE', 'CH', 'CZ', 'DK', ...]

// Check if specific country has requirements
const hasRequirements = countries.includes('GB');
console.log('GB has requirements:', hasRequirements); // true

// Display to users
countries.forEach(country => {
console.log(`${country}: Regulatory compliance required`);
});

💡 Integration Examples

Pre-Purchase Compliance Check

async function checkComplianceBeforePurchase(countryCode, numberType) {
// 1. Check if country has requirements
const requirements = await complianceProvider.getRegulationRequirements(
countryCode,
numberType,
'business',
);

if (!requirements.hasRequirements) {
return {
canPurchase: true,
requiresCompliance: false,
message: 'No regulatory requirements',
};
}

// 2. Check what's needed
const needed = {
address: requirements.requirements.address.length > 0,
endUser: requirements.requirements.end_user.length > 0,
documents: requirements.requirements.supporting_document.length > 0,
};

// 3. Check if we have everything
const account = await Account.findOne({
/* ... */
});

const hasAddress = !!account?.twilio_account?.a2p?.customer_profile_address;
const hasEndUser = account?.twilio_account?.a2p?.end_users?.length > 0;
const hasBundle = !!account?.twilio_account?.a2p?.customer_profile_bundle;

const ready =
(!needed.address || hasAddress) &&
(!needed.endUser || hasEndUser) &&
(!needed.documents || hasBundle);

return {
canPurchase: ready,
requiresCompliance: true,
needed,
hasCompleted: {
address: hasAddress,
endUser: hasEndUser,
bundle: hasBundle,
},
requirements,
};
}

Display Compliance Requirements UI

async function getComplianceRequirementsForUI(countryCode) {
const requirements = await complianceProvider.getRegulationRequirements(
countryCode,
'local',
'business',
);

if (!requirements.hasRequirements) {
return {
required: false,
message: 'No documentation needed for this country',
};
}

const ui = {
required: true,
sections: [],
};

// Address section
if (requirements.requirements.address.length > 0) {
const addressReq = requirements.requirements.address[0];
ui.sections.push({
type: 'address',
title: 'Business Address',
description: 'Provide your business address for verification',
fields: addressReq.detailed_fields.map(f => ({
name: f.machine_name,
label: f.friendly_name,
required: true,
placeholder: f.description || `Enter ${f.friendly_name.toLowerCase()}`,
})),
});
}

// End user section
if (requirements.requirements.end_user.length > 0) {
requirements.requirements.end_user.forEach(endUserReq => {
ui.sections.push({
type: 'end_user',
title: endUserReq.name,
description: 'Provide business or individual information',
fields: endUserReq.fields || [],
});
});
}

// Documents section
if (requirements.requirements.supporting_document.length > 0) {
requirements.requirements.supporting_document.forEach(docReq => {
ui.sections.push({
type: 'document',
title: docReq.name,
description: 'Upload required documentation',
acceptedTypes: docReq.accepted_documents || [],
});
});
}

return ui;
}

Country Availability Dashboard

async function generateComplianceDashboard() {
const countriesWithRequirements = await complianceProvider.getAvailableCountries();

const dashboard = {
totalCountries: countriesWithRequirements.length,
byNumberType: {
local: [],
mobile: [],
tollFree: [],
},
byComplexity: {
simple: [], // Only address
moderate: [], // Address + end user
complex: [], // All requirements
},
};

// Analyze each country
for (const country of countriesWithRequirements) {
// Check each number type
for (const numberType of ['local', 'mobile', 'toll_free']) {
const req = await complianceProvider.getRegulationRequirements(
country,
numberType,
'business',
);

if (req.hasRequirements) {
dashboard.byNumberType[numberType].push(country);

// Categorize by complexity
const reqCount =
(req.requirements.address?.length || 0) +
(req.requirements.end_user?.length || 0) +
(req.requirements.supporting_document?.length || 0);

if (reqCount === 1) {
dashboard.byComplexity.simple.push(`${country} (${numberType})`);
} else if (reqCount === 2) {
dashboard.byComplexity.moderate.push(`${country} (${numberType})`);
} else {
dashboard.byComplexity.complex.push(`${country} (${numberType})`);
}
}
}
}

return dashboard;
}

Compliance Validation

async function validateCompliance(accountId, countryCode, numberType) {
// 1. Get requirements
const requirements = await complianceProvider.getRegulationRequirements(
countryCode,
numberType,
'business',
);

if (!requirements.hasRequirements) {
return { valid: true, message: 'No requirements' };
}

// 2. Get account compliance data
const account = await Account.findById(accountId);
const a2p = account?.twilio_account?.a2p;

const validation = {
valid: true,
errors: [],
warnings: [],
};

// 3. Validate address
if (requirements.requirements.address.length > 0) {
if (!a2p?.customer_profile_address) {
validation.valid = false;
validation.errors.push('Address is required but not provided');
} else {
// Check required fields
const requiredFields = requirements.requirements.address[0].fields;
const address = a2p.customer_profile_address;

requiredFields.forEach(field => {
if (!address[field]) {
validation.valid = false;
validation.errors.push(`Address field '${field}' is required`);
}
});
}
}

// 4. Validate end user
if (requirements.requirements.end_user.length > 0) {
if (!a2p?.end_users || a2p.end_users.length === 0) {
validation.valid = false;
validation.errors.push('End user information is required');
}
}

// 5. Validate documents
if (requirements.requirements.supporting_document.length > 0) {
if (!a2p?.customer_profile_bundle) {
validation.valid = false;
validation.errors.push('Customer profile bundle is required');
} else if (a2p.customer_profile_bundle.status !== 'approved') {
validation.warnings.push(
`Bundle status is ${a2p.customer_profile_bundle.status}, not approved`,
);
}
}

return validation;
}

🚨 Error Handling

Common Errors

1. Invalid Country Code

Error: 20404 - The requested resource was not found
  • Cause: Country code not recognized by Twilio
  • Solution: Use ISO 3166-1 alpha-2 codes (GB, US, not UK, USA)

2. Authentication Failed

Error: 20003 - Authenticate;
  • Cause: Invalid credentials
  • Solution: Verify accountSid and authToken

3. Rate Limit Exceeded

Error: 20429 - Too Many Requests
  • Cause: Too many API calls
  • Solution: Leverage 24-hour cache, reduce redundant calls

⚡ Performance Considerations

Caching Strategy

  • TTL: 24 hours (regulations rarely change)
  • Storage: In-memory Map (resets on service restart)
  • Production Only: Caching disabled in development
  • Cache Keys: Unique per country/numberType/endUserType

Optimization Tips

  1. Batch Country Checks: Get available countries once, cache locally
  2. Reuse Requirements: Don't re-fetch for same country repeatedly
  3. Lazy Loading: Only fetch requirements when user selects country
  4. Pre-fetch Common: Cache US, GB, CA requirements on startup

External Resources:

💬

Documentation Assistant

Ask me anything about the docs

Hi! I'm your documentation assistant. Ask me anything about the docs!

I can help you with:
- Code examples
- Configuration details
- Troubleshooting
- Best practices

Try asking: How do I configure the API?
09:31 AM