๐ Phone Number Assignment (A2P)
๐ Overviewโ
The Phone Number Assignment job automatically assigns Twilio phone numbers to verified A2P messaging campaigns. It runs every 30 seconds, identifies accounts with verified campaigns that don't have numbers assigned, and assigns numbers based on the campaign use case type (SOLE_PROPRIETOR vs other types). Sole proprietor campaigns get a single number (preferably the default SMS-capable number), while other campaign types get all available numbers assigned. This is the final step in the A2P compliance workflow before SMS sending is enabled.
Complete Flow:
- Cron Initialization:
queue-manager/crons/communication/a2p/numberAssignment.js - Service Processing:
queue-manager/services/communication/a2p.js(numberAssignment) - Queue Definition:
queue-manager/queues/communication/a2p/numberAssignment.js
Execution Pattern: Rapid polling (every 30 seconds) with account-level queuing
Queue Name: comm_a2p_numberAssignment
Environment Flag: QM_COMMUNICATION_A2P_NUMBER_ASSIGNMENT=true (in index.js)
๐ Complete Processing Flowโ
sequenceDiagram
participant CRON as Cron Schedule<br/>(every 30s)
participant SERVICE as Number Assignment<br/>Service
participant ACCOUNT_DB as Accounts DB
participant PHONE_DB as Phone Numbers<br/>Collection
participant QUEUE as Number Assignment<br/>Queue
participant TWILIO as Twilio Messaging<br/>Service API
CRON->>SERVICE: numberAssignment()
SERVICE->>ACCOUNT_DB: Aggregate accounts where:<br/>campaignStatus = 'VERIFIED'
SERVICE->>PHONE_DB: Lookup phone numbers<br/>for each account
ACCOUNT_DB-->>SERVICE: Accounts with phone numbers
SERVICE->>SERVICE: Filter accounts:<br/>- Has unassigned numbers<br/>- SOLE_PROPRIETOR: no assigned numbers
loop Each account
SERVICE->>QUEUE: Add job: {account}
end
loop Each queued account
QUEUE->>QUEUE: Filter out already<br/>assigned numbers
alt Campaign type = SOLE_PROPRIETOR
QUEUE->>QUEUE: Select single number:<br/>1. Default SMS-capable<br/>2. Any SMS-capable<br/>3. First number
else Campaign type = Other
QUEUE->>QUEUE: Select all numbers
end
loop Each selected number
QUEUE->>TWILIO: POST /Services/{serviceSid}/PhoneNumbers<br/>Assign number to service
TWILIO-->>QUEUE: Success
QUEUE->>PHONE_DB: Set assignedService = serviceSid
end
end
๐ Source Filesโ
1. Cron Initializationโ
File: queue-manager/crons/communication/a2p/numberAssignment.js
Purpose: Schedule number assignment check every 30 seconds
Cron Pattern: */30 * * * * * (every 30 seconds)
Initialization:
const { numberAssignment } = require('../../../services/communication/a2p');
const cron = require('node-cron');
const logger = require('../../../utilities/logger');
let inProgress = false;
exports.start = async () => {
try {
cron.schedule('*/30 * * * * *', async () => {
if (!inProgress) {
inProgress = true;
await numberAssignment();
inProgress = false;
}
});
} catch (err) {
logger.error({ initiator: 'QM/communication/a2p/number-assignment', error: err });
}
};
In-Progress Lock: Prevents overlapping executions.
Execution Times: Every 30 seconds (twice per minute)
2. Service Processingโ
File: queue-manager/services/communication/a2p.js (numberAssignment export)
Purpose: Find accounts with verified campaigns needing number assignment and queue them
Key Features:
- Aggregation with phone number lookup
- Complex filtering logic for sole proprietor vs other campaign types
- Batch job queuing
Main Service Function:
const numberAssignment = require('../../queues/communication/a2p/numberAssignment');
const Account = require('../../models/account');
const logger = require('../../utilities/logger');
exports.numberAssignment = async () => {
try {
let accounts = await Account.aggregate([
{
$match: {
'twilio_account.a2p.messaging_campaign.campaignStatus': 'VERIFIED',
},
},
{
$lookup: {
from: 'twilio.number',
localField: '_id',
foreignField: 'account_id',
as: 'phone_numbers',
},
},
{
$match: {
phone_numbers: { $not: { $size: 0 } },
},
},
]);
accounts = accounts.filter(a => {
if (a.phone_numbers.every(p => p.assignedService)) return false; // If all numbers are assigned then ignore account.
if (
a.twilio_account.a2p.messaging_campaign.usAppToPersonUsecase == 'SOLE_PROPRIETOR' &&
a.phone_numbers.find(p => p.assignedService)
)
return false; // If sole proprietor and atleast 1 number is assigned then ignore account.
return true;
});
if (accounts.length) {
let queue = await numberAssignment.start();
await Promise.all(
accounts.map(async a => {
try {
await queue.add(
{
account: a,
},
{
removeOnComplete: true,
},
);
} catch (err) {
logger.error({
initiator: 'QM/communication/a2p/number-assignment/add-queue',
error: err,
});
}
}),
);
}
} catch (err) {
logger.error({ initiator: 'QM/communication/a2p/number-assignment/service', error: err });
}
};
Filtering Logic Breakdown:
Step 1: MongoDB Aggregation
- Match accounts with
campaignStatus: 'VERIFIED' - Lookup phone numbers from
twilio.numbercollection - Exclude accounts with no phone numbers
Step 2: JavaScript Filtering
- Exclude if all numbers already assigned to a service
- Exclude if sole proprietor campaign AND at least 1 number already assigned
- Include otherwise
Campaign Use Cases:
SOLE_PROPRIETOR- Individual/small business (single number)- Other types (e.g.,
LOW_VOLUME_STANDARD) - Business/enterprise (all numbers)
3. Queue Processing (THE ASSIGNMENT LOGIC)โ
File: queue-manager/queues/communication/a2p/numberAssignment.js
Purpose: Assign phone numbers to messaging service based on campaign type
Key Functions:
- Filter out already-assigned numbers
- Select numbers based on campaign use case
- Call Twilio API to assign numbers
- Update database with assignment status
Complete Processor:
const mongoose = require('mongoose');
const QueueWrapper = require('../../../common/queue-wrapper');
const logger = require('../../../utilities/logger');
const a2p = require('../twilio/services/a2p');
const processCb = async (job, done) => {
try {
const { account } = job.data;
const metadata = {
twilio_creds: {
sid: account.twilio_account?.sid,
token: account.twilio_account?.authToken,
},
};
account.phone_numbers = account.phone_numbers.filter(p => !p.assignedService); // Ignore numbers already assigned
let numberToAssign;
if (account.twilio_account.a2p.messaging_campaign.usAppToPersonUsecase == 'SOLE_PROPRIETOR') {
let number = account.phone_numbers.find(p => {
if (p.default && p.capabilities?.sms) return true;
return false;
});
if (!number) {
number = account.phone_numbers.find(p => {
if (p.capabilities?.sms) return true;
return false;
});
}
if (!number) number = account.phone_numbers[0];
numberToAssign = number.sid;
} else {
numberToAssign = 'all';
}
let finalList =
numberToAssign == 'all'
? account.phone_numbers.reduce((a, c) => {
a.push(c.sid);
return a;
}, [])
: [numberToAssign];
await Promise.all(
finalList.map(sid => {
return a2p.assignNumber(metadata, account.twilio_account.a2p?.messaging_service?.sid, sid);
}),
);
return done();
} catch (err) {
done(err);
}
};
const failedCb = async (job, err) => {
logger.error({
initiator: 'QM/communication/a2p/number-assignment/queue',
message: `Failed to assign one or more numbers`,
error: err,
job: job.id,
job_data: job.data,
});
};
const completedCb = async job => {
logger.log({
initiator: 'QM/communication/a2p/number-assignment/queue',
message: `Numbers assigned successfully`,
job: job.id,
job_data: job.data,
});
};
let queue;
exports.start = async () => {
try {
if (!queue)
queue = QueueWrapper(`comm_a2p_numberAssignment`, 'global', {
processCb,
completedCb,
failedCb,
});
return Promise.resolve(queue);
} catch (err) {
logger.error({
initiator: 'QM/communication/a2p/number-assignment/queue',
error: err,
message: `Error while starting queue`,
});
}
};
4. Twilio A2P Service Utilitiesโ
File: queue-manager/queues/communication/twilio/services/a2p.js (assignNumber)
Purpose: Assign phone number to messaging service via Twilio API and update database
Function:
const assignNumber = async (metadata, service, id) => {
try {
const client = Twilio(metadata.twilio_creds.sid, metadata.twilio_creds.token);
await client.messaging.v1.services(service).phoneNumbers.create({ phoneNumberSid: id });
await PhoneNumber.updateMany(
{
sid: id,
},
{
$set: {
assignedService: service,
},
},
);
return Promise.resolve(campaign);
} catch (err) {
return Promise.reject(err);
}
};
Parameters:
metadata: Twilio credentialsservice: Messaging service SIDid: Phone number SID
Returns: Promise (note: returns campaign variable which doesn't exist - likely copy/paste error, but doesn't affect functionality)
๐๏ธ Collections Usedโ
_accountsโ
- Operations: Aggregate, Read
- Model:
shared/models/account.js - Usage Context: Store A2P campaign data
Key Fields (twilio_account.a2p object):
messaging_campaign: Object - A2P campaign datacampaignStatus: 'VERIFIED' (required for number assignment)usAppToPersonUsecase: 'SOLE_PROPRIETOR' | 'LOW_VOLUME_STANDARD' | etc.
messaging_service: Object - Messaging service datasid: Messaging service SID (target for number assignment)
twilio.numberโ
- Operations: Lookup, Update
- Model:
shared/models/twilio-number.js(likely) - Usage Context: Store phone number data and assignment status
Key Fields:
sid: Phone number SID (unique identifier)account_id: Reference to_accounts._iddefault: Boolean - Default number for accountcapabilities: Object - Number capabilitiessms: Boolean - SMS capabilityvoice: Boolean - Voice capability
assignedService: String - Messaging service SID (null if unassigned)
๐ง Job Configurationโ
Cron Scheduleโ
'*/30 * * * * *'; // Every 30 seconds
Frequency: 120 times per hour (twice per minute)
Rationale: Fast number assignment after campaign verification; 30-second interval ensures quick turnaround without overwhelming API.
Queue Settingsโ
QueueWrapper(`comm_a2p_numberAssignment`, 'global', {
processCb,
completedCb,
failedCb,
});
Queue Name: comm_a2p_numberAssignment
Concurrency: Default (1)
Job Options:
{
removeOnComplete: true;
}
Note: No retry configuration - relies on default Bull settings (no retries, fail immediately).
๐ Processing Logic - Detailed Flowโ
1. Account Query & Filteringโ
Step 1: MongoDB Aggregation
{
$match: {
'twilio_account.a2p.messaging_campaign.campaignStatus':'VERIFIED'
}
}
Matches: Only accounts with verified campaigns.
{
$lookup: {
from: 'twilio.number',
localField: '_id',
foreignField: 'account_id',
as: 'phone_numbers'
}
}
Joins: Phone numbers from twilio.number collection.
{
$match: {
phone_numbers: {
$not: {
$size: 0;
}
}
}
}
Filters: Only accounts with at least 1 phone number.
Step 2: JavaScript Filtering
accounts = accounts.filter(a => {
if (a.phone_numbers.every(p => p.assignedService)) return false; // If all numbers are assigned then ignore account.
if (
a.twilio_account.a2p.messaging_campaign.usAppToPersonUsecase == 'SOLE_PROPRIETOR' &&
a.phone_numbers.find(p => p.assignedService)
)
return false; // If sole proprietor and atleast 1 number is assigned then ignore account.
return true;
});
Filters:
- Exclude if all numbers already assigned (no work needed)
- Exclude if sole proprietor AND at least 1 number assigned (sole proprietor only gets 1 number)
- Include all others (has unassigned numbers OR needs all numbers assigned)
2. Number Selection Logicโ
Pre-Filter: Remove already-assigned numbers
account.phone_numbers = account.phone_numbers.filter(p => !p.assignedService);
Effect: Only unassigned numbers remain for selection.
Sole Proprietor Selection (single number):
if (account.twilio_account.a2p.messaging_campaign.usAppToPersonUsecase == 'SOLE_PROPRIETOR') {
// Priority 1: Default SMS-capable number
let number = account.phone_numbers.find(p => {
if (p.default && p.capabilities?.sms) return true;
return false;
});
// Priority 2: Any SMS-capable number
if (!number) {
number = account.phone_numbers.find(p => {
if (p.capabilities?.sms) return true;
return false;
});
}
// Priority 3: First available number (even if not SMS-capable)
if (!number) number = account.phone_numbers[0];
numberToAssign = number.sid;
}
Selection Priority:
- Default number with SMS capability
- Any number with SMS capability
- First available number (fallback)
Other Campaign Types (all numbers):
else {
numberToAssign = 'all';
}
Selection: All unassigned numbers.
3. Number Assignment Executionโ
Build Final List:
let finalList =
numberToAssign == 'all'
? account.phone_numbers.reduce((a, c) => {
a.push(c.sid);
return a;
}, [])
: [numberToAssign];
Result:
- Sole proprietor: Array with 1 number SID
- Other types: Array with all unassigned number SIDs
Assign All Numbers in Parallel:
await Promise.all(
finalList.map(sid => {
return a2p.assignNumber(metadata, account.twilio_account.a2p?.messaging_service?.sid, sid);
}),
);
Effect: All numbers assigned simultaneously for speed.
4. Twilio API Integrationโ
API Call (per number):
POST https://messaging.twilio.com/v1/Services/{serviceSid}/PhoneNumbers
Body: { phoneNumberSid: id }
Response: Phone number resource created.
Database Update (per number):
await PhoneNumber.updateMany(
{ sid: id },
{
$set: {
assignedService: service,
},
},
);
Effect: Marks number as assigned, preventing future reassignment.
๐จ Error Handlingโ
Common Error Scenariosโ
Number Already Assignedโ
Scenario: Number already assigned to another messaging service
Handling: Twilio API error thrown, job fails immediately (no retries)
Impact: Job fails, number not reassigned
Note: Service-level filtering should prevent this, but can occur if number assigned externally.
Invalid Number SIDโ
Scenario: Number SID invalid or deleted
Handling: Twilio API error thrown, job fails
Impact: Assignment fails for all numbers (entire job fails)
Note: Should be rare; numbers typically exist if in database.
Messaging Service Not Foundโ
Scenario: Messaging service SID invalid or deleted
Handling: Twilio API error thrown, job fails
Impact: Assignment fails for all numbers
Note: Should be rare; service created by brand check job.
No SMS-Capable Numbersโ
Scenario: Sole proprietor account has no SMS-capable numbers
Handling: Fallback to first available number (even if not SMS-capable)
Impact: Number assigned, but SMS may not work (Twilio will reject)
Note: Rare edge case; most numbers have SMS capability.
Database Update Failureโ
Scenario: MongoDB connection issue, validation error
Handling: Error thrown, job fails
Impact: Number assigned in Twilio but not marked in database (inconsistent state)
Note: Can cause repeated assignment attempts on next run.
Failed Job Callbackโ
const failedCb = async (job, err) => {
logger.error({
initiator: 'QM/communication/a2p/number-assignment/queue',
message: `Failed to assign one or more numbers`,
error: err,
job: job.id,
job_data: job.data,
});
};
Action: Log error with job details.
Completed Job Callbackโ
const completedCb = async job => {
logger.log({
initiator: 'QM/communication/a2p/number-assignment/queue',
message: `Numbers assigned successfully`,
job: job.id,
job_data: job.data,
});
};
Action: Log success with job details.
๐ Monitoring & Loggingโ
Success Loggingโ
Queue Level:
- Success log on job completion with job ID and account data
Error Loggingโ
Cron Level:
- Error in cron initialization
Service Level:
- Error aggregating accounts
- Error queuing individual accounts
Queue Level:
- Error starting queue
- Failed job with error details
Performance Metricsโ
- Number Assignment (Twilio API): 1-2 seconds per number
- Database Update: less than 1 second per number
- Total Job Time (SOLE_PROPRIETOR): 2-3 seconds (1 number)
- Total Job Time (Other): 3-10 seconds (multiple numbers, parallel)
๐ Integration Pointsโ
Triggers This Jobโ
- Cron Schedule: Every 30 seconds (no external triggers)
- Campaign Check Job: Sets
campaignStatus: 'VERIFIED'(prerequisite)
External Dependenciesโ
- Twilio Messaging Service API: Number assignment endpoint
- Twilio Credentials: Stored in
twilio_account.sidandtwilio_account.authToken
Jobs That Depend On Thisโ
- SMS Sending: Requires assigned numbers for 10DLC compliance
- Renewal Job: Monitors assigned numbers for renewal requirements
Related Featuresโ
- SMS Sending: Uses assigned numbers for outbound messages
- Number Management Dashboard: Displays assignment status
โ ๏ธ Important Notesโ
Side Effectsโ
- โ ๏ธ Number Assignment: Phone numbers bound to messaging service (affects SMS routing)
- โ ๏ธ Database Writes: Updates
assignedServicefield (permanent until unassigned) - โ ๏ธ SMS Capability: Enables SMS sending for assigned numbers
Performance Considerationsโ
- High Frequency: 30-second interval ensures fast assignment
- Parallel Assignment: Multiple numbers assigned simultaneously
- Service Filtering: Reduces unnecessary API calls by filtering at service level
- Queue Filtering: Further reduces API calls by filtering already-assigned numbers
- No Retries: Default job configuration has no retry logic (fails immediately)
- Job Cleanup:
removeOnComplete: trueprevents queue bloat
Business Logicโ
Why Every 30 Seconds?
- Fast turnaround after campaign verification (minutes, not hours)
- Balances speed with API efficiency
- 120 checks per hour is reasonable for background job
- Minimal API costs (only accounts with verified campaigns checked)
Why Single Number for Sole Proprietor?
- Twilio 10DLC compliance requirement for sole proprietor campaigns
- Sole proprietors limited to 1 number per campaign
- Other campaign types can use multiple numbers
Why Prefer Default SMS-Capable Number?
- Default number typically the primary business number
- SMS capability required for 10DLC compliance
- Fallback to any SMS-capable number ensures functionality
- Final fallback to first number prevents job failure (though may not work for SMS)
Why Assign All Numbers for Other Types?
- Business/enterprise campaigns can use multiple numbers
- Maximizes SMS sending capacity
- Reduces need for multiple campaigns
Why No Retries?
- Assignment failures typically require manual intervention (invalid credentials, deleted service, etc.)
- Rapid cron schedule (30 seconds) provides natural retry mechanism
- Prevents infinite retry loops for permanent failures
Maintenance Notesโ
- Cron Schedule: 30 seconds hardcoded (consider environment variable)
- Use Case Values: 'SOLE_PROPRIETOR' hardcoded (other values not explicitly listed)
- No Retry Logic: Default Bull settings (consider adding exponential backoff)
- Database Inconsistency Risk: Number assigned in Twilio but DB update fails
- Return Value Bug:
assignNumberreturnscampaignvariable (doesn't exist) - doesn't break functionality but should be fixed
Code Quality Issuesโ
Issue 1: Return Value Bug
const assignNumber = async (metadata, service, id) => {
// ... assignment logic ...
return Promise.resolve(campaign); // 'campaign' is not defined
};
Issue: Function returns undefined variable (copy/paste error).
Impact: Return value not used by caller, so no functional impact.
Suggestion: Remove return value or return meaningful data:
return Promise.resolve({ success: true, phoneNumberSid: id });
Issue 2: No Retry Logic
{
removeOnComplete: true;
}
Issue: No retry configuration for transient failures (network issues, rate limiting).
Suggestion: Add exponential backoff:
{
attempts: 3,
backoff: {
type: "exponential",
delay: 60000 // 60s, 120s, 240s
},
removeOnComplete: true
}
Issue 3: Service-Level Filter Complexity
accounts = accounts.filter(a => {
if (a.phone_numbers.every(p => p.assignedService)) return false;
if (
a.twilio_account.a2p.messaging_campaign.usAppToPersonUsecase == 'SOLE_PROPRIETOR' &&
a.phone_numbers.find(p => p.assignedService)
)
return false;
return true;
});
Issue: Complex filtering logic buried in service layer; hard to read/maintain.
Suggestion: Extract to named function:
const needsNumberAssignment = account => {
const allAssigned = account.phone_numbers.every(p => p.assignedService);
if (allAssigned) return false;
const isSoleProprietor =
account.twilio_account.a2p.messaging_campaign.usAppToPersonUsecase === 'SOLE_PROPRIETOR';
const hasAssignedNumber = account.phone_numbers.some(p => p.assignedService);
if (isSoleProprietor && hasAssignedNumber) return false;
return true;
};
accounts = accounts.filter(needsNumberAssignment);
Issue 4: Queue-Level Filter Redundancy
account.phone_numbers = account.phone_numbers.filter(p => !p.assignedService);
Issue: Service already filters accounts with all numbers assigned, but queue still filters.
Why: Necessary because:
- Non-sole-proprietor accounts may have mix of assigned/unassigned numbers
- Service filtering is coarse (account-level), queue filtering is fine (number-level)
Impact: Not an issue, just adds slight performance overhead.
Issue 5: Fallback to Non-SMS Number
if (!number) number = account.phone_numbers[0];
Issue: If no SMS-capable number found, assigns first number (which may not support SMS).
Impact: Assignment succeeds, but SMS sending may fail at Twilio.
Suggestion: Fail job explicitly if no SMS-capable number:
if (!number) {
throw new Error('No SMS-capable number found for SOLE_PROPRIETOR campaign');
}
Issue 6: Missing Edge Case Handling
let finalList = numberToAssign == 'all' ? account.phone_numbers.reduce(...) : [numberToAssign];
Issue: If numberToAssign is undefined (no numbers after filtering), final list is [undefined].
Impact: Twilio API call fails with invalid number SID.
Suggestion: Add validation:
if (!numberToAssign) {
throw new Error('No numbers available for assignment');
}
๐งช Testingโ
Manual Triggerโ
# Via API (if QM_HOOKS=true)
POST http://localhost:6002/api/trigger/communication/a2p/numberAssignment
Simulate Campaign Verificationโ
const Account = require('./models/account');
// Set campaign to VERIFIED (prerequisite)
await Account.findByIdAndUpdate(accountId, {
'twilio_account.a2p.messaging_campaign.campaignStatus': 'VERIFIED',
'twilio_account.a2p.messaging_campaign.usAppToPersonUsecase': 'SOLE_PROPRIETOR',
'twilio_account.a2p.messaging_service.sid': 'MGXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
});
console.log('Campaign verified, waiting for number assignment');
// Wait for next cron run (up to 30 seconds)
Verify Number Assignmentโ
const PhoneNumber = require('./models/twilio-number');
// Check phone number assignment status
const numbers = await PhoneNumber.find({ account_id: accountId });
console.log(
'Phone numbers:',
numbers.map(n => ({
sid: n.sid,
default: n.default,
capabilities: n.capabilities,
assignedService: n.assignedService,
})),
);
// Verify sole proprietor has 1 number assigned
const assignedCount = numbers.filter(n => n.assignedService).length;
console.log('Assigned numbers:', assignedCount); // Should be 1 for SOLE_PROPRIETOR
Test Number Selection Logicโ
// Test sole proprietor number selection priority
// Priority 1: Default SMS-capable number
const phoneNumbers = [
{ sid: 'PN111', default: true, capabilities: { sms: true } }, // Should be selected
{ sid: 'PN222', default: false, capabilities: { sms: true } },
{ sid: 'PN333', default: false, capabilities: { sms: false } },
];
// Priority 2: Any SMS-capable number
const phoneNumbers2 = [
{ sid: 'PN111', default: false, capabilities: { sms: false } },
{ sid: 'PN222', default: false, capabilities: { sms: true } }, // Should be selected
{ sid: 'PN333', default: false, capabilities: { sms: false } },
];
// Priority 3: First number (fallback)
const phoneNumbers3 = [
{ sid: 'PN111', default: false, capabilities: { sms: false } }, // Should be selected (first)
{ sid: 'PN222', default: false, capabilities: { sms: false } },
];
Test Multiple Campaign Typesโ
// Test LOW_VOLUME_STANDARD (all numbers)
await Account.findByIdAndUpdate(accountId, {
'twilio_account.a2p.messaging_campaign.usAppToPersonUsecase': 'LOW_VOLUME_STANDARD',
});
// After assignment, verify all numbers assigned
const numbers = await PhoneNumber.find({ account_id: accountId });
const allAssigned = numbers.every(n => n.assignedService);
console.log('All numbers assigned:', allAssigned); // Should be true
Monitor Queue Processingโ
# Watch logs during number assignment
tail -f logs/queue-manager.log | grep "number-assignment"
# Expected outputs:
# [INFO] Numbers assigned successfully
# [ERROR] Failed to assign one or more numbers (if errors)
Test Service-Level Filteringโ
// Test filtering logic
// Case 1: All numbers assigned (should be excluded)
const account1 = {
phone_numbers: [
{ sid: 'PN111', assignedService: 'MGXXX' },
{ sid: 'PN222', assignedService: 'MGXXX' },
],
};
console.log(
'Should exclude:',
account1.phone_numbers.every(p => p.assignedService),
); // true
// Case 2: Sole proprietor with 1 assigned (should be excluded)
const account2 = {
twilio_account: {
a2p: {
messaging_campaign: {
usAppToPersonUsecase: 'SOLE_PROPRIETOR',
},
},
},
phone_numbers: [
{ sid: 'PN111', assignedService: 'MGXXX' },
{ sid: 'PN222', assignedService: null },
],
};
const isSoleWithAssigned =
account2.twilio_account.a2p.messaging_campaign.usAppToPersonUsecase === 'SOLE_PROPRIETOR' &&
account2.phone_numbers.find(p => p.assignedService);
console.log('Should exclude:', isSoleWithAssigned); // true
// Case 3: Other campaign type with mix (should be included)
const account3 = {
twilio_account: {
a2p: {
messaging_campaign: {
usAppToPersonUsecase: 'LOW_VOLUME_STANDARD',
},
},
},
phone_numbers: [
{ sid: 'PN111', assignedService: 'MGXXX' },
{ sid: 'PN222', assignedService: null }, // Unassigned
],
};
console.log('Should include:', !account3.phone_numbers.every(p => p.assignedService)); // true
Job Type: Scheduled with Account-Level Queuing
Execution Frequency: Every 30 seconds
Average Duration: 2-10 seconds per account (depending on number count)
Status: Active