npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@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-sdk

Authentication

The Task Router SDK requires a shared secret token for service-to-service authentication.

Environment Variable Required:

TASK_ROUTER_TOKEN=your-shared-secret-token

The 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 HTTP

API Reference

Workers Service

workers.create(options)

Create a new worker (agent or bot).

Parameters:

  • options.accountId (string, required) - Account ID
  • options.recordTypeId (string, required) - Record type for permissions
  • options.userId (string, optional) - Link to user account
  • options.status (string, optional) - 'available', 'offline', 'busy' (default: 'offline')
  • options.capabilities (object, required) - Task type capabilities
    • phoneCall: { enabled: boolean, concurrentLimit: number }
    • chat: { enabled: boolean, concurrentLimit: number }
    • email: { enabled: boolean, concurrentLimit: number }
    • other: { enabled: boolean, concurrentLimit: number }
  • options.metadata (object, optional) - Custom metadata
    • languages (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 name
    • userEmail (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 ID
  • options.accountId (string, required) - Account ID
  • options.status (string, optional) - New status
  • options.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 status
  • options.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 ID
  • options.recordTypeId (string, required) - Record type ID
  • options.type (string, required) - 'phoneCall', 'chat', 'email', 'other'
  • options.queueId (string, required) - Queue/department ID
  • options.status (string, optional) - Initial status (default: 'pending')
  • options.priority (number, optional) - Priority (higher = more urgent, default: 0)
  • options.requirements (object, optional) - Worker requirements
    • required.languages (string[]) - Required languages
    • required.skills (string[]) - Required skills
    • optional.languages (string[]) - Preferred languages
    • optional.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 ID
  • options.accountId (string, required) - Account ID
  • options.status (string, optional) - New status
  • options.priority (number, optional) - New priority
  • options.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 status
  • options.queueId (string, optional) - Filter by queue
  • options.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 created
  • worker.updated - Worker updated (status, capabilities, etc.)
  • worker.deleted - Worker deleted
  • task.created - New task created
  • task.updated - Task updated
  • task.assigned - Task assigned to a worker
  • task.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:

  1. pending - Task created, awaiting worker assignment
  2. assigned - Assigned to worker, worker's phone is ringing
  3. connected - Active conversation with worker
  4. hold - Task on hold
  5. wrapUp - Post-call work
  6. 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 routing
  • userEmail, userName - For display/logging
  • workerType - 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-token

The 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:

  1. Workers have matching capabilities for task type
  2. Workers are in correct queue
  3. Workers have required skills/languages
  4. Workers are in 'available' status
  5. 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