Skip to main content

๐Ÿ“ž 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:

  1. Cron Initialization: queue-manager/crons/communication/a2p/numberAssignment.js
  2. Service Processing: queue-manager/services/communication/a2p.js (numberAssignment)
  3. 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.number collection
  • 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 credentials
  • service: Messaging service SID
  • id: 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 data
    • campaignStatus: 'VERIFIED' (required for number assignment)
    • usAppToPersonUsecase: 'SOLE_PROPRIETOR' | 'LOW_VOLUME_STANDARD' | etc.
  • messaging_service: Object - Messaging service data
    • sid: 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._id
  • default: Boolean - Default number for account
  • capabilities: Object - Number capabilities
    • sms: Boolean - SMS capability
    • voice: 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:

  1. Default number with SMS capability
  2. Any number with SMS capability
  3. 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.sid and twilio_account.authToken

Jobs That Depend On Thisโ€‹

  • SMS Sending: Requires assigned numbers for 10DLC compliance
  • Renewal Job: Monitors assigned numbers for renewal requirements
  • 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 assignedService field (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: true prevents 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: assignNumber returns campaign variable (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:

  1. Non-sole-proprietor accounts may have mix of assigned/unassigned numbers
  2. 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

๐Ÿ’ฌ

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