Skip to main content

Twilio Phone Number Provider

🎯 Overview

The Number API Provider (Providers/number-api.js) handles phone number operations including searching available numbers, purchasing (provisioning), updating configuration, and releasing numbers.

Source: external/Integrations/Twilio/Providers/number-api.js

Key Capabilities:

  • Search available phone numbers by country/area code
  • Purchase phone numbers (local, toll-free, mobile)
  • Configure SMS/Voice URLs and capabilities
  • Update phone number settings
  • Release (delete) phone numbers
  • Regulatory compliance support (bundle SID, address SID)
  • Get phone number pricing by country
  • List available countries

🔌 Provider Functions

getList()

Search for available phone numbers in a specific country and area code.

Signature:

exports.getList = async(
SID,
authToken,
countryCode,
areaCode,
(capabilities = {}),
(type = 'local'),
);

Parameters:

ParameterTypeRequiredDefaultDescription
SIDString-Twilio Account SID
authTokenString-Twilio Auth Token
countryCodeString-ISO 3166-1 alpha-2 country code (US, GB, CA, etc.)
areaCodeString-Area code for local/mobile numbers (e.g., '212', '415')
capabilitiesObject{}Required capabilities (sms, voice, mms, fax)
typeString'local'Number type: 'local', 'mobile', 'toll_free'

Returns:

Promise<
Array<{
friendlyName: string; // "+1 (212) 555-1234"
phoneNumber: string; // "+12125551234" (E.164 format)
lata: string; // Local Access and Transport Area
locality: string; // City name
rateCenter: string; // Rate center location
latitude: string;
longitude: string;
region: string; // State/province code
postalCode: string;
isoCountry: string; // Country code
addressRequirements: string; // "none", "any", "local", "foreign"
beta: boolean;
capabilities: {
voice: boolean;
SMS: boolean;
MMS: boolean;
fax: boolean;
};
}>
>;

Business Logic:

  1. Create Twilio Client

  2. Verify Country Availability

    • Fetches country resource to check supported number types
    • Returns empty array if requested type not available
  3. Search by Number Type

    • Local: Area code optional, returns local numbers
    • Mobile: Mobile numbers (area code optional)
    • Toll-Free: 800, 888, 877, etc. numbers (no area code)
  4. Return Available Numbers

    • Maximum 100 results per request

Example Usage:

const numberProvider = require('./Providers/number-api');

// Search local numbers in New York (area code 212)
const numbers = await numberProvider.getList(
process.env.TWILIO_ACCOUNT_SID,
process.env.TWILIO_AUTH_TOKEN,
'US',
'212',
{},
'local',
);

console.log('Found numbers:', numbers.length);
numbers.forEach(num => {
console.log(`${num.friendlyName} - ${num.locality}, ${num.region}`);
console.log(`Capabilities: SMS=${num.capabilities.SMS}, Voice=${num.capabilities.voice}`);
});

// Output:
// +1 (212) 555-1234 - New York, NY
// Capabilities: SMS=true, Voice=true
// +1 (212) 555-5678 - New York, NY
// Capabilities: SMS=true, Voice=true

Search Toll-Free Numbers:

// Search toll-free numbers (no area code needed)
const tollFreeNumbers = await numberProvider.getList(
accountSID,
authToken,
'US',
null, // No area code for toll-free
{},
'toll_free',
);

console.log('Toll-free:', tollFreeNumbers[0].phoneNumber);
// Output: +18005551234

Search Mobile Numbers:

// Search mobile numbers in Los Angeles (area code 310)
const mobileNumbers = await numberProvider.getList(
accountSID,
authToken,
'US',
'310',
{},
'mobile',
);

console.log('Mobile:', mobileNumbers[0].phoneNumber);

Data Flow:

sequenceDiagram
participant API as DashClicks API
participant Provider as Number Provider
participant Twilio as Twilio SDK

API->>Provider: getList(countryCode, areaCode, type)
Provider->>Twilio: availablePhoneNumbers(country).fetch()
Twilio-->>Provider: Country resource

alt Number type available
Provider->>Twilio: .local.list() / .mobile.list() / .tollFree.list()
Note over Provider,Twilio: limit: 100, areaCode (if provided)
Twilio-->>Provider: Available numbers array
Provider-->>API: Return numbers
else Type not available
Provider-->>API: Return empty array []
end

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

addProvisionNumber()

Purchase (provision) a phone number from Twilio.

Signature:

exports.addProvisionNumber = async(SID, authToken, dataToAdd);

Parameters:

ParameterTypeRequiredDescription
SIDStringTwilio Account SID
authTokenStringTwilio Auth Token
dataToAddObjectPhone number configuration

dataToAdd Object:

FieldTypeRequiredDescription
phoneNumberStringPhone number to purchase (E.164 format)
friendlyNameStringDisplay name for the number
smsUrlStringWebhook URL for incoming SMS
voiceUrlStringWebhook URL for incoming calls
smsMethodStringHTTP method for SMS webhook (GET or POST, default POST)
voiceMethodStringHTTP method for voice webhook (default POST)
bundleSidStringRegulatory compliance bundle SID (required for some countries)
addressSidStringAddress SID for compliance (required for some numbers)
emergencyEnabledBooleanEnable E911 emergency services
emergencyAddressSidStringE911 address SID

Returns:

Promise<{
sid: string; // Phone number SID (PN...)
accountSid: string;
friendlyName: string;
phoneNumber: string;
voiceUrl: string;
smsUrl: string;
voiceMethod: string;
smsMethod: string;
capabilities: {
voice: boolean;
SMS: boolean;
MMS: boolean;
fax: boolean;
};
addressRequirements: string;
beta: boolean;
dateCreated: Date;
dateUpdated: Date;
emergencyStatus: string; // "Active" if emergency enabled
emergencyAddressSid: string;
bundleSid: string;
identitySid: string;
origin: string; // "twilio" or "hosted"
status: string; // "in-use"
uri: string;
}>;

Business Logic:

  1. Create Twilio Client

  2. Prepare Provision Data

    • Copy dataToAdd object
    • Handle regulatory compliance (bundleSid, addressSid)
    • Remove internal parameters (countryCode)
  3. Purchase Number via Twilio

    • Calls client.incomingPhoneNumbers.create()
    • Twilio provisions number immediately
  4. Error Handling

    • Enhanced error messages for compliance failures
    • Code 21452: Bundle validation failed
    • Code 21453: Address validation failed

Example Usage:

// Purchase local number with SMS/Voice webhooks
const number = await numberProvider.addProvisionNumber(
process.env.TWILIO_ACCOUNT_SID,
process.env.TWILIO_AUTH_TOKEN,
{
phoneNumber: '+12125551234',
friendlyName: 'Customer Support Line',
smsUrl: 'https://app.dashclicks.com/webhooks/sms',
voiceUrl: 'https://app.dashclicks.com/webhooks/voice',
smsMethod: 'POST',
voiceMethod: 'POST',
},
);

console.log('Purchased:', number.phoneNumber);
console.log('SID:', number.sid); // PN1234567890abcdef
console.log('Status:', number.status); // in-use

Purchase with Regulatory Compliance (Required for some countries):

// Purchase number with regulatory bundle (required for US A2P)
const number = await numberProvider.addProvisionNumber(accountSID, authToken, {
phoneNumber: '+12125551234',
bundleSid: 'BU1234567890abcdef', // From regulatory-compliance provider
addressSid: 'AD1234567890abcdef', // From address provider
smsUrl: 'https://app.dashclicks.com/webhooks/sms',
voiceUrl: 'https://app.dashclicks.com/webhooks/voice',
});

Purchase with Emergency Services:

// Purchase with E911 enabled
const number = await numberProvider.addProvisionNumber(accountSID, authToken, {
phoneNumber: '+12125551234',
emergencyEnabled: true,
emergencyAddressSid: 'AD1234567890abcdef',
voiceUrl: 'https://app.dashclicks.com/webhooks/voice',
});

updateProvisionNumber()

Update configuration of a purchased phone number.

Signature:

exports.updateProvisionNumber = async(accountSID, authToken, numberSID, dataToUpdate);

Parameters:

ParameterTypeRequiredDescription
accountSIDStringTwilio Account SID
authTokenStringTwilio Auth Token
numberSIDStringPhone number SID (PN...)
dataToUpdateObjectFields to update

dataToUpdate Object (all optional):

FieldTypeDescription
friendlyNameStringUpdate display name
smsUrlStringUpdate SMS webhook URL
voiceUrlStringUpdate voice webhook URL
smsMethodStringUpdate SMS HTTP method
voiceMethodStringUpdate voice HTTP method
emergencyEnabledBooleanEnable/disable E911
emergencyAddressSidStringUpdate E911 address

Returns:

Promise<TwilioPhoneNumberInstance>;

Example Usage:

// Update SMS webhook URL
const updated = await numberProvider.updateProvisionNumber(
accountSID,
authToken,
'PN1234567890abcdef',
{
smsUrl: 'https://app.dashclicks.com/v2/webhooks/sms',
voiceUrl: 'https://app.dashclicks.com/v2/webhooks/voice',
},
);

console.log('Updated:', updated.phoneNumber);
console.log('New SMS URL:', updated.smsUrl);

Update Friendly Name:

// Change display name
await numberProvider.updateProvisionNumber(accountSID, authToken, numberSID, {
friendlyName: 'Sales Line - East Coast',
});

deleteProvisionNumber()

Release (delete) a purchased phone number.

Signature:

exports.deleteProvisionNumber = async(accountSID, authToken, numberSID);

Parameters:

ParameterTypeRequiredDescription
accountSIDStringTwilio Account SID
authTokenStringTwilio Auth Token
numberSIDStringPhone number SID to delete

Returns:

Promise<true>;

Example Usage:

// Release phone number
const result = await numberProvider.deleteProvisionNumber(
accountSID,
authToken,
'PN1234567890abcdef',
);

console.log('Number released:', result); // true

⚠️ WARNING: This permanently releases the number back to Twilio's pool. It may be purchased by another customer immediately.


getPricing()

Get phone number pricing for a specific country.

Signature:

exports.getPricing = async(SID, authToken, countryCode);

Parameters:

ParameterTypeRequiredDescription
SIDStringTwilio Account SID
authTokenStringTwilio Auth Token
countryCodeStringISO 3166-1 alpha-2 country code

Returns:

Promise<{
country: string;
isoCountry: string;
phoneNumberPrices: Array<{
type: string; // "local", "mobile", "toll free"
basePrice: string;
currentPrice: string;
}>;
priceUnit: string; // Currency code (USD, GBP, etc.)
url: string;
}>;

Example Usage:

// Get US phone number pricing
const pricing = await numberProvider.getPricing(accountSID, authToken, 'US');

console.log('Country:', pricing.country); // United States
console.log('Currency:', pricing.priceUnit); // USD

// Local number price
const localPrice = pricing.phoneNumberPrices.find(p => p.type === 'local');
console.log('Local number:', localPrice.currentPrice); // 1.00 (per month)

// Toll-free price
const tollFreePrice = pricing.phoneNumberPrices.find(p => p.type === 'toll free');
console.log('Toll-free:', tollFreePrice.currentPrice); // 2.00 (per month)

Pricing by Country:

// Get UK pricing
const ukPricing = await numberProvider.getPricing(accountSID, authToken, 'GB');
console.log('UK Local:', ukPricing.phoneNumberPrices[0].currentPrice); // GBP

// Get Canada pricing
const caPricing = await numberProvider.getPricing(accountSID, authToken, 'CA');
console.log('CA Local:', caPricing.phoneNumberPrices[0].currentPrice); // CAD

availableCountries()

List all countries where Twilio offers phone numbers.

Signature:

exports.availableCountries = async(accountSID, authToken, limit);

Parameters:

ParameterTypeRequiredDescription
accountSIDStringTwilio Account SID
authTokenStringTwilio Auth Token
limitNumberMaximum countries to return

Returns:

Promise<
Array<{
country: string; // "United States"
countryCode: string; // "US"
uri: string;
beta: boolean;
subresourceUris: {
local: string; // URI if local numbers available
mobile: string; // URI if mobile numbers available
tollFree: string; // URI if toll-free available
};
}>
>;

Example Usage:

// Get all available countries
const countries = await numberProvider.availableCountries(accountSID, authToken, 100);

console.log('Countries:', countries.length);

countries.forEach(country => {
console.log(`${country.country} (${country.countryCode})`);
console.log(' Local:', !!country.subresourceUris.local);
console.log(' Mobile:', !!country.subresourceUris.mobile);
console.log(' Toll-Free:', !!country.subresourceUris.tollFree);
});

// Output:
// United States (US)
// Local: true
// Mobile: true
// Toll-Free: true
// United Kingdom (GB)
// Local: true
// Mobile: true
// Toll-Free: false

💡 Integration Examples

Phone Number Purchase Flow

async function purchasePhoneNumber(countryCode, areaCode) {
// 1. Search available numbers
const available = await numberProvider.getList(
process.env.TWILIO_ACCOUNT_SID,
process.env.TWILIO_AUTH_TOKEN,
countryCode,
areaCode,
{},
'local',
);

if (available.length === 0) {
throw new Error('No available numbers found');
}

// 2. Purchase first available
const number = await numberProvider.addProvisionNumber(
process.env.TWILIO_ACCOUNT_SID,
process.env.TWILIO_AUTH_TOKEN,
{
phoneNumber: available[0].phoneNumber,
friendlyName: `${countryCode} ${areaCode} Number`,
smsUrl: process.env.TWILIO_SMS_URL,
voiceUrl: process.env.TWILIO_VOICE_URL,
},
);

// 3. Store in database
await TwilioNumber.create({
sid: number.sid,
phoneNumber: number.phoneNumber,
friendlyName: number.friendlyName,
accountSid: number.accountSid,
smsUrl: number.smsUrl,
voiceUrl: number.voiceUrl,
capabilities: number.capabilities,
});

return number;
}

Number Search UI

async function searchNumbers(filters) {
const { countryCode, areaCode, type, capabilities } = filters;

// Search numbers
const numbers = await numberProvider.getList(
accountSID,
authToken,
countryCode,
areaCode,
capabilities,
type,
);

// Format for UI
return numbers.map(num => ({
phoneNumber: num.phoneNumber,
friendlyName: num.friendlyName,
location: `${num.locality}, ${num.region}`,
capabilities: {
sms: num.capabilities.SMS,
voice: num.capabilities.voice,
mms: num.capabilities.MMS,
},
addressRequirements: num.addressRequirements,
}));
}

Update Number Configuration

async function updateNumberWebhooks(numberSID, smsUrl, voiceUrl) {
const updated = await numberProvider.updateProvisionNumber(accountSID, authToken, numberSID, {
smsUrl,
voiceUrl,
});

// Update database
await TwilioNumber.updateOne({ sid: numberSID }, { smsUrl, voiceUrl });

return updated;
}

Release Unused Numbers

async function releaseUnusedNumbers() {
// Find numbers not used in last 30 days
const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);

const unusedNumbers = await TwilioNumber.find({
lastUsed: { $lt: thirtyDaysAgo },
});

const released = [];

for (const number of unusedNumbers) {
try {
await numberProvider.deleteProvisionNumber(accountSID, authToken, number.sid);

await TwilioNumber.deleteOne({ _id: number._id });

released.push(number.phoneNumber);
} catch (error) {
console.error(`Failed to release ${number.phoneNumber}:`, error);
}
}

return released;
}

Cost Calculator

async function calculateMonthlyCost(countryCode, localCount, tollFreeCount) {
// Get pricing
const pricing = await numberProvider.getPricing(accountSID, authToken, countryCode);

// Find prices
const localPrice = parseFloat(
pricing.phoneNumberPrices.find(p => p.type === 'local')?.currentPrice || 0,
);

const tollFreePrice = parseFloat(
pricing.phoneNumberPrices.find(p => p.type === 'toll free')?.currentPrice || 0,
);

// Calculate
const localCost = localPrice * localCount;
const tollFreeCost = tollFreePrice * tollFreeCount;
const totalCost = localCost + tollFreeCost;

return {
currency: pricing.priceUnit,
localCount,
localPrice,
localCost,
tollFreeCount,
tollFreePrice,
tollFreeCost,
totalCost,
};
}

// Usage
const cost = await calculateMonthlyCost('US', 10, 2);
console.log(`Monthly cost: $${cost.totalCost}`);
// Output: Monthly cost: $14.00 (10 local @ $1 + 2 toll-free @ $2)

🚨 Error Handling

Common Errors

1. Number Already Purchased

Error: 21452 - Phone number is already owned by another account
  • Cause: Number was purchased by another customer
  • Solution: Search for new available numbers

2. Regulatory Compliance Required

Error: 21452 - Regulatory bundle validation failed
  • Cause: Country requires regulatory bundle (e.g., US A2P)
  • Solution: Create regulatory bundle first, provide bundleSid

3. Address Validation Failed

Error: 21453 - Address validation failed for compliance
  • Cause: Invalid or missing address SID for compliance
  • Solution: Validate address via address provider first

4. Number Not Found

Error: PHONE_NUMBERS_NOT_FOUND - No available numbers found for this country
  • Cause: No numbers available for country/area code combination
  • Solution: Try different area code or country

5. Invalid Country Code

Error: 21401 - Invalid Country Code
  • Cause: Country code not in ISO 3166-1 alpha-2 format
  • Solution: Use 2-letter codes (US, GB, CA, not USA, UK, CAN)

⚡ Performance Considerations

Search Optimization

  • Limit: Twilio returns max 100 numbers per request
  • Caching: Consider caching available numbers for 5-10 minutes
  • Fallback: If specific area code unavailable, try nearby area codes

Purchase Flow

  • Immediate: Number provisioning is synchronous (< 1 second)
  • Atomic: Either succeeds completely or fails (no partial success)
  • Verification: Always verify purchase success before storing in DB

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