@unboundcx/task-router-sdk
v1.1.1
Published
Official SDK for the Task Router service - intelligent task assignment and worker management for contact centers
Readme
Task Router SDK
Official SDK for the Task Router service - intelligent task assignment and worker management for contact centers.
Installation
npm install @unboundcx/task-router-sdkAuthentication
The Task Router SDK requires a shared secret token for service-to-service authentication.
Environment Variable Required:
TASK_ROUTER_TOKEN=your-shared-secret-tokenThe SDK automatically reads process.env.TASK_ROUTER_TOKEN and includes it as a Bearer token in all requests. Ensure this environment variable is set in all services using the SDK.
Quick Start
import TaskRouterSDK from '@unboundcx/task-router-sdk';
// Initialize SDK (token auto-loaded from process.env.TASK_ROUTER_TOKEN)
const taskRouter = new TaskRouterSDK({
namespace: 'your-account-namespace',
});
// Add NATS transport (recommended for performance)
taskRouter.addTransport(natsTransport);
// Create a worker
const worker = await taskRouter.workers.create({
accountId: '001...',
userId: 'usr_123',
recordTypeId: 'rec_456',
status: 'available',
capabilities: {
phoneCall: { enabled: true, concurrentLimit: 1 },
chat: { enabled: true, concurrentLimit: 3 },
},
metadata: {
languages: ['English'],
queues: ['support'],
skills: ['technical'],
},
});
// Create a task
const task = await taskRouter.tasks.create({
accountId: '001...',
recordTypeId: 'rec_456',
type: 'phoneCall',
queueId: 'support',
status: 'pending',
requirements: {
required: {
languages: ['English'],
skills: ['technical'],
},
},
});Transport Configuration
The SDK supports multiple transport layers with automatic fallback:
NATS Transport (Recommended)
import { connect } from 'nats';
// Connect to NATS
const nc = await connect({ servers: process.env.NATS_URL });
// Create NATS transport
const natsTransport = {
name: 'NATS',
getPriority: () => 1, // Higher priority than HTTP
isAvailable: async () => true,
request: async (endpoint, method, params, metadata) => {
const subject = `taskRouter.request`;
const requestObject = {
method,
url: endpoint,
headers: { 'x-namespace': metadata.namespace, ...params.headers },
query: params.query || {},
body: params.body,
};
const response = await nc.request(subject, JSON.stringify(requestObject));
return JSON.parse(response.data);
},
};
taskRouter.addTransport(natsTransport);HTTP Fallback
HTTP transport is built-in and automatically used if NATS is unavailable.
// Force HTTP (skip NATS)
taskRouter.debug(true); // Enable debug logging
const result = await taskRouter.workers.get(workerId, { accountId }, true); // Last param forces HTTPAPI Reference
Workers Service
workers.create(options)
Create a new worker (agent or bot).
Parameters:
options.accountId(string, required) - Account IDoptions.recordTypeId(string, required) - Record type for permissionsoptions.userId(string, optional) - Link to user accountoptions.status(string, optional) - 'available', 'offline', 'busy' (default: 'offline')options.capabilities(object, required) - Task type capabilitiesphoneCall:{ enabled: boolean, concurrentLimit: number }chat:{ enabled: boolean, concurrentLimit: number }email:{ enabled: boolean, concurrentLimit: number }other:{ enabled: boolean, concurrentLimit: number }
options.metadata(object, optional) - Custom metadatalanguages(string[]) - Languages (e.g., ['English', 'Spanish'])queues(string[]) - Queue membership (e.g., ['support', 'sales'])skills(string[]) - Skills (e.g., ['technical', 'billing'])workerType(string) - 'human' or 'bot'userName(string) - Display nameuserEmail(string) - Email address
Returns: Worker object with id and all worker data
Example:
const agent = await taskRouter.workers.create({
accountId: '001...',
recordTypeId: 'rec...',
userId: 'usr_abc',
status: 'offline',
capabilities: {
phoneCall: { enabled: true, concurrentLimit: 1 },
chat: { enabled: true, concurrentLimit: 3 },
email: { enabled: true, concurrentLimit: 5 },
},
metadata: {
languages: ['English', 'Spanish'],
queues: ['support'],
skills: ['technical', 'billing'],
workerType: 'human',
userName: 'John Agent',
userEmail: '[email protected]',
},
});workers.update(workerId, options)
Update worker status, capabilities, or metadata.
Parameters:
workerId(string, required) - Worker IDoptions.accountId(string, required) - Account IDoptions.status(string, optional) - New statusoptions.capabilities(object, optional) - Update capabilities (merged)options.metadata(object, optional) - Update metadata (merged)
Example:
// Set worker available
await taskRouter.workers.update('wkr_123', {
accountId: '001...',
status: 'available',
});
// Update skills
await taskRouter.workers.update('wkr_123', {
accountId: '001...',
metadata: {
skills: ['technical', 'billing', 'sales'],
},
});workers.get(workerId, options)
Retrieve a specific worker.
Example:
const worker = await taskRouter.workers.get('wkr_123', {
accountId: '001...',
});workers.list(options)
List workers with optional filters.
Parameters:
options.accountId(string, required)options.status(string, optional) - Filter by statusoptions.queueId(string, optional) - Filter by queue
Example:
const availableWorkers = await taskRouter.workers.list({
accountId: '001...',
status: 'available',
queueId: 'support',
});workers.delete(workerId, options)
Delete a worker.
Example:
await taskRouter.workers.delete('wkr_123', {
accountId: '001...',
});Tasks Service
tasks.create(options)
Create a new task. Tasks are automatically assigned to available workers.
Parameters:
options.accountId(string, required) - Account IDoptions.recordTypeId(string, required) - Record type IDoptions.type(string, required) - 'phoneCall', 'chat', 'email', 'other'options.queueId(string, required) - Queue/department IDoptions.status(string, optional) - Initial status (default: 'pending')options.priority(number, optional) - Priority (higher = more urgent, default: 0)options.requirements(object, optional) - Worker requirementsrequired.languages(string[]) - Required languagesrequired.skills(string[]) - Required skillsoptional.languages(string[]) - Preferred languagesoptional.skills(string[]) - Preferred skills
options.metadata(object, optional) - Custom metadata
Returns: Task object with id and all task data
Example:
const task = await taskRouter.tasks.create({
accountId: '001...',
recordTypeId: 'rec...',
type: 'phoneCall',
queueId: 'support',
priority: 5,
requirements: {
required: {
languages: ['English'],
skills: ['technical'],
},
},
metadata: {
callerId: '+14155551234',
callerName: 'John Doe',
},
});tasks.update(taskId, options)
Update task status or metadata.
Parameters:
taskId(string, required) - Task IDoptions.accountId(string, required) - Account IDoptions.status(string, optional) - New statusoptions.priority(number, optional) - New priorityoptions.metadata(object, optional) - Update metadata
Example:
// Update status to connected
await taskRouter.tasks.update('tsk_123', {
accountId: '001...',
status: 'connected',
});
// Complete task
await taskRouter.tasks.update('tsk_123', {
accountId: '001...',
status: 'completed',
});tasks.get(taskId, options)
Retrieve a specific task.
Example:
const task = await taskRouter.tasks.get('tsk_123', {
accountId: '001...',
});tasks.list(options)
List tasks with optional filters.
Parameters:
options.accountId(string, required)options.status(string, optional) - Filter by statusoptions.queueId(string, optional) - Filter by queueoptions.type(string, optional) - Filter by type
Example:
const pendingTasks = await taskRouter.tasks.list({
accountId: '001...',
status: 'pending',
queueId: 'support',
});tasks.claimPendingSlot(options)
Router-internal only. Atomically claims a reservation against a
queue+channel capacity cap (queue-capacity-overflow-plan.md). Pass the
returned reservationId to tasks.create(); never call from
client-facing code.
Parameters: options.accountId, options.queueId, options.channel,
options.cap (all required)
Returns: { admitted, reservationId, pending }
tasks.releasePendingSlot(options)
Releases a reservation from claimPendingSlot that was never converted
into a committed task (the create/transfer it was claimed for failed or
was aborted).
Parameters: options.accountId, options.queueId, options.channel,
options.reservationId (all required)
Returns: { released }
Queues Service
queues.getPendingCounts(options)
Live pending counts for a queue, per channel. Backs the "Overflow & limits" card and wallboard capacity segment.
Parameters: options.accountId, options.queueId (both required)
Returns: { [channel]: { pending, asyncWait, voicemail } }
Example:
const counts = await taskRouter.queues.getPendingCounts({
accountId: '001...',
queueId: 'support',
});NATS Events
The Task Router publishes real-time events to NATS that you can subscribe to:
Event Subjects
worker.created- New worker createdworker.updated- Worker updated (status, capabilities, etc.)worker.deleted- Worker deletedtask.created- New task createdtask.updated- Task updatedtask.assigned- Task assigned to a workertask.route.check- Task routing initiated
Subscribing to Events
import { connect, StringCodec } from 'nats';
const nc = await connect({ servers: process.env.NATS_URL });
const sc = StringCodec();
// Listen for task assignments
nc.subscribe('task.assigned', (err, msg) => {
const event = JSON.parse(sc.decode(msg.data));
const { taskId, workerId, worker, task } = event.data;
console.log(`Task ${taskId} assigned to worker ${workerId}`);
console.log('Worker details:', worker);
console.log('Task details:', task);
// Route call to worker, etc.
});
// Listen for worker status changes
nc.subscribe('worker.updated', (err, msg) => {
const event = JSON.parse(sc.decode(msg.data));
const { id, status } = event.data;
console.log(`Worker ${id} status changed to ${status}`);
});Complete Examples
Example 1: Human Agent Worker
const agent = await taskRouter.workers.create({
accountId: '001d01202311287peayoktj3hcdhcex',
recordTypeId: '042d03202312207peayokn1jbenoeqq',
userId: 'usr_human_123',
status: 'offline', // Starts offline, user logs in later
capabilities: {
phoneCall: { enabled: true, concurrentLimit: 1 },
chat: { enabled: true, concurrentLimit: 3 },
email: { enabled: true, concurrentLimit: 5 },
other: { enabled: false, concurrentLimit: 0 },
},
metadata: {
accountId: '001d01202311287peayoktj3hcdhcex',
languages: ['English', 'Spanish'],
queues: ['support', 'sales'],
skills: ['technical-support', 'billing', 'product-specialist'],
workerType: 'human',
userName: 'Jane Agent',
userEmail: '[email protected]',
},
});
console.log('Agent created:', agent.id);Example 2: AI Bot Worker
const bot = await taskRouter.workers.create({
accountId: '001d01202311287peayoktj3hcdhcex',
recordTypeId: '042d03202312207peayokn1jbenoeqq',
userId: 'bot_ai_456',
status: 'available', // Bots are always available
capabilities: {
phoneCall: { enabled: false, concurrentLimit: 0 }, // Bots don't handle calls
chat: { enabled: true, concurrentLimit: 50 },
email: { enabled: true, concurrentLimit: 100 },
other: { enabled: true, concurrentLimit: 25 },
},
metadata: {
accountId: '001d01202311287peayoktj3hcdhcex',
languages: ['English', 'Spanish', 'French', 'German'],
queues: ['support'],
skills: ['faq', 'account-lookup', 'basic-troubleshooting'],
workerType: 'bot',
botEngine: 'gpt-4',
userName: 'Support Bot',
},
});
console.log('Bot created:', bot.id);Example 3: Complete Task Lifecycle
// 1. Create a task
const task = await taskRouter.tasks.create({
accountId: '001d01202311287peayoktj3hcdhcex',
recordTypeId: '042d03202312207peayokn1jbenoeqq',
type: 'phoneCall',
queueId: 'support',
priority: 5,
requirements: {
required: {
languages: ['English'],
skills: ['technical-support'],
},
},
metadata: {
callerId: '+14155552671',
callerName: 'John Doe',
companyId: '035d01202311287peayoktj3hcdhcex',
},
});
console.log('Task created:', task.id);
// 2. Subscribe to assignment event
nc.subscribe('task.assigned', async (err, msg) => {
const { taskId, workerId, worker } = JSON.parse(sc.decode(msg.data)).data;
if (taskId === task.id) {
console.log(`Task assigned to worker ${workerId}`);
// Route call to worker's SIP endpoint
await routeCallToWorker(worker);
}
});
// 3. When call connects, update status
await taskRouter.tasks.update(task.id, {
accountId: '001d01202311287peayoktj3hcdhcex',
status: 'connected',
});
// 4. When call ends, complete task
await taskRouter.tasks.update(task.id, {
accountId: '001d01202311287peayoktj3hcdhcex',
status: 'completed',
});Task Lifecycle States
Tasks progress through the following states:
- pending - Task created, awaiting worker assignment
- assigned - Assigned to worker, worker's phone is ringing
- connected - Active conversation with worker
- hold - Task on hold
- wrapUp - Post-call work
- completed - Task finished
Worker Status Values
- available - Ready to receive tasks
- offline - Not available for tasks
- busy - Manually set to busy (won't receive new tasks)
Best Practices
1. Use NATS for Performance
NATS transport provides significantly better performance than HTTP. Always add a NATS transport when available.
2. Handle Assignment Events
Subscribe to task.assigned events rather than polling for assignments.
3. Worker Metadata
Store useful information in worker metadata:
languages,skills,queues- Used for task routinguserEmail,userName- For display/loggingworkerType- Distinguish between humans and bots
4. Task Priority
Use priority levels to ensure urgent tasks are assigned first:
- 0-3: Normal priority
- 4-7: High priority
- 8-10: Critical/VIP
5. Error Handling
try {
const worker = await taskRouter.workers.create(options);
} catch (error) {
if (error.status === 400) {
console.error('Validation error:', error.body);
} else if (error.status === 500) {
console.error('Server error:', error.message);
}
}Troubleshooting
401 Unauthorized Errors
If you're getting 401 errors, verify that TASK_ROUTER_TOKEN is set correctly:
# Check if token is set
echo $TASK_ROUTER_TOKEN
# Set the token (must match Task Router service's TASK_ROUTER_TOKEN)
export TASK_ROUTER_TOKEN=your-shared-secret-tokenThe token must be identical across:
- Task Router service (server)
- All consuming services (SDK clients)
SDK Methods Not Working
Check that NATS connection is established or HTTP endpoints are accessible.
// Enable debug mode
taskRouter.debug(true);
// Check transport availability
const hasNATS = await natsTransport.isAvailable();
console.log('NATS available:', hasNATS);Tasks Not Being Assigned
Verify:
- Workers have matching capabilities for task type
- Workers are in correct queue
- Workers have required skills/languages
- Workers are in 'available' status
- Workers haven't exceeded concurrency limits
Support
For issues or questions, contact the Platform team or file an issue in the internal repository.
License
Internal use only - Proprietary
