Building a Call Scheduling Service with Twilio and NestJS

April 24, 2025
Written by
Eluda Laaroussi
Contributor
Opinions expressed by Twilio contributors are their own
Reviewed by

Building a Call Scheduling Service with Twilio and NestJS

 

Introduction

This guide demonstrates how to build a call scheduling service using Twilio Programmable Voice and the NestJS framework, emphasizing its modular architecture with Controllers, Services, and Modules. This structure supports scalability, maintainability, and microservices readiness, integrating Twilio APIs for scheduling voice calls while ensuring quality through unit tests

Prerequisites

To follow along, ensure you have the following:

- Node.js (v16 or later) and npm installed.

- An active Twilio account.

- Twilio credentials: Account SID, Auth Token, and a Twilio phone number.

- A code editor (e.g., VS Code).

Refer to the GitHub Repository for the final code.

Installing the Nest CLI

Run the following command on your system to install the Nest CLI, which we’ll use to scaffold a project template and add resources to our application as we go:

npm install -g @nestjs/cli

Scaffold the Project

On your terminal, navigate to your preferred project directory and start by creating a new NestJS project named twilio-scheduler:

nest new twilio-scheduler

When prompted, choose yarn as the package manager, as we’ll be using it for this tutorial. You’re also free to pick another option that you’re more comfortable with, but if doing so please note that you’ll have to use its own yarn-equivalents for most of the commands in this guide.

Once the nest installation is complete, navigate into the project directory:

cd twilio-scheduler

Install the required dependencies:

yarn add twilio @nestjs/schedule @nestjs/config class-validator

 

Integrating Twilio

Setting Up Twilio Credentials

Create a file named .env in the project root and add your Twilio credentials to it:

# .env
TWILIO_ACCOUNT_SID=your_account_sid
TWILIO_AUTH_TOKEN=your_auth_token
TWILIO_PHONE_NUMBER=your_twilio_phone_number

These credentials can be found on your Twilio Console.

Twilio dashboard showing account SID, token, and phone number.

Also, create a file at src/common/enums/environment.enum.ts to hold the string keys of these variables:

export enum Environment {
    TWILIO_ACCOUNT_SID = 'TWILIO_ACCOUNT_SID',
    TWILIO_AUTH_TOKEN = 'TWILIO_AUTH_TOKEN',
    TWILIO_PHONE_NUMBER = 'TWILIO_PHONE_NUMBER',
}

And to access these environment variables in your Nest application, update the src/app.module.ts file:

// other imports …
import { ConfigModule } from '@nestjs/config';
@Module({
  imports: [ConfigModule.forRoot()],
 // other code
})
export class AppModule { }

Creating the Twilio Service

Start off by generating the Twilio service using the CLI:

nest g service twilio

Writing unit tests for the Twilio Service

Before implementing the Twilio service, we write tests to ensure it behaves as expected:

Unit tests are typically written before implementation to define expected behaviors and validate functionality as development progresses. NestJS uses the JEST framework for writing tests, which provides a powerful and flexible testing environment. Jest integrates seamlessly with NestJS, offering tools for mocking, assertions, and running isolated tests. Its support for dependency injection aligns well with NestJS’s modular architecture. Test files follow this naming pattern: [identifier]/[identifier].[type].spec.tsFor the Twilio service, that translates to: twilio/twilio.service.spec.ts, which we’ll update with the following:
// twilio/twilio.service.spec.ts
import { Test, TestingModule } from '@nestjs/testing';
import { ConfigService } from '@nestjs/config';
import { TwilioService } from './twilio.service';
describe('TwilioService', () => {
  let service: TwilioService;
  let configService: ConfigService;
  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        TwilioService,
        {
          provide: ConfigService,
          useValue: {
            get: jest.fn((key: string) => {
              const mockEnv = {
                TWILIO_ACCOUNT_SID: 'AC12345678901234567890123456789012',
                TWILIO_AUTH_TOKEN: 'testAuthToken',
                TWILIO_PHONE_NUMBER: '+1234567890',
              };
              return mockEnv[key];
            }),
          },
        },
      ],
    }).compile();
    service = module.get<TwilioService>(TwilioService);
    configService = module.get<ConfigService>(ConfigService);
  });
  it('should be defined', () => {
    expect(service).toBeDefined();
  });
  it('should initialize the Twilio client with correct credentials', () => {
    const client = service.getClient();
    expect(client).toBeDefined();
    expect(configService.get).toHaveBeenCalledWith('TWILIO_ACCOUNT_SID');
    expect(configService.get).toHaveBeenCalledWith('TWILIO_AUTH_TOKEN');
  });
  it('should return the configured Twilio phone number', () => {
    const phoneNumber = service.getTwilioPhoneNumber();
    expect(phoneNumber).toBe('+1234567890');
    expect(configService.get).toHaveBeenCalledWith('TWILIO_PHONE_NUMBER');
  });
  it('should throw an error if any environment variable is missing', () => {
    jest.spyOn(configService, 'get').mockReturnValueOnce(undefined);
    expect(() => new TwilioService(configService)).toThrowError('Twilio configuration is missing!');
  });
});

This test suite ensures the TwilioService behaves as expected before implementation, focusing on key functionalities and error handling. Using TestingModule, it mocks the ConfigService to provide environment variables such as TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN, and TWILIO_PHONE_NUMBER. This isolates the service from external dependencies, making the tests reliable and predictable.

Key aspects of these tests include:

  • Client Initialization: Verifies that the Twilio client is created with the correct credentials by ensuring the mocked environment variables are accessed as expected.
  • Phone Number Retrieval: Confirms that the service correctly returns the Twilio phone number from the configuration.
  • Error Handling: Tests the service’s behavior when a required environment variable is missing, ensuring it throws a meaningful error. This helps identify configuration issues early.

Implementing the Twilio Service

In src/twilio/twilio.service.ts, implement the base Twilio logic:

// twilio/twilio.service.ts
import { Injectable, Logger } from '@nestjs/common';
import { Twilio } from 'twilio';
import { ConfigService } from '@nestjs/config';
import { Environment } from '../common/enums/environment.enum';
@Injectable()
export class TwilioService {
    private readonly logger = new Logger(TwilioService.name);
    private readonly twilioClient: Twilio;
    private readonly twilioPhoneNumber: string;
    constructor(private readonly configService: ConfigService) {
        const accountSid = this.getRequiredConfig(Environment.TWILIO_ACCOUNT_SID);
        const authToken = this.getRequiredConfig(Environment.TWILIO_AUTH_TOKEN);
        this.twilioPhoneNumber = this.getRequiredConfig(Environment.TWILIO_PHONE_NUMBER);
        this.twilioClient = new Twilio(accountSid, authToken);
        this.logger.log('Twilio client initialized.');
    }      private getRequiredConfig(key: Environment): string {
        const val = this.configService.get<string>(key);
        if (!val) throw new Error('Twilio configuration is missing!');
        return val;
   }
    getClient(): Twilio {
        return this.twilioClient;
    }
    getTwilioPhoneNumber(): string {
        return this.twilioPhoneNumber;
    }
}

This code implements the core logic for the TwilioService, responsible for interacting with the Twilio API. It encapsulates Twilio client initialization and provides access to its key components.

Key highlights of the implementation:

  • Dependency Injection: The ConfigService is injected into the constructor to retrieve essential environment variables such as TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN, and TWILIO_PHONE_NUMBER. These values are used to configure the Twilio client.
  • Validation: The constructor checks if all required environment variables are defined. If any are missing, it throws an error, preventing the service from running with incomplete configuration.
  • Initialization: The Twilio client is instantiated with the accountSid and authToken, ensuring the service is ready to make API calls. A Logger instance logs the successful initialization for debugging and monitoring purposes.
  • Getter Methods:
  • getClient() provides access to the initialized Twilio client for making API calls.
  • getTwilioPhoneNumber() returns the configured Twilio phone number, ensuring this value is accessible elsewhere in the application.

Let’s run the tests to ensure functionality. But before we can do so, we must configure JEST to be compatible with our src/* imports.

In the Typescript configuration, add the following:

// tsconfig.json
{
  "compilerOptions": {
    // existing code …
    "paths": {
      "src/*": ["./src/*"]
    }
  }
}

And in the JEST configuration in the package.json file:

// package.json
{
  // existing code…
  "jest": {
    // existing code…
    "moduleNameMapper": {
      "^src/(.*)$": "<rootDir>/$1"
    }
  },
}

With that done, you can now run tests:

yarn test
Screenshot of PowerShell window showing list of installed packages and creation of files.

Integrating Programmable Voice

Creating the Programmable Voice Service

Let’s use the CLI to scaffold the programmable voice service files:

nest g service twilio/twilio-programmable-voice

Like before, we’ll begin by writing unit tests for the Twilio Programmable Voice service:

// twilio/twilio-programmable-voice/twilio-programmable-voice.service.spec.ts
import { Test, TestingModule } from '@nestjs/testing';
import { TwilioProgrammableVoiceService } from './twilio-programmable-voice.service';
import { TwilioService } from '../twilio.service';
describe('TwilioProgrammableVoiceService', () => {
  let service: TwilioProgrammableVoiceService;
  let twilioService: TwilioService;
  beforeEach(async () => {
    const mockTwilioClient = {
      calls: {
        create: jest.fn().mockResolvedValue({ sid: 'testCallSid' }), // mock sid
      },
    };
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        TwilioProgrammableVoiceService,
        {
          provide: TwilioService,
          useValue: {
            getClient: jest.fn(() => mockTwilioClient),
            getTwilioPhoneNumber: jest.fn(() => '+1234567890'),
          },
        },
      ],
    }).compile();
    service = module.get<TwilioProgrammableVoiceService>(TwilioProgrammableVoiceService);
    twilioService = module.get<TwilioService>(TwilioService);
  });
  it('should be defined', () => {
    expect(service).toBeDefined();
  });
  it('should call Twilio API with correct parameters', async () => {
    const mockTwilioClient = twilioService.getClient();
    const to = '+1987654321';
    const message = 'Hello, this is a test call!';
    const from = twilioService.getTwilioPhoneNumber();
    await service.makeVoiceCall(to, message);
    expect(mockTwilioClient.calls.create).toHaveBeenCalledWith({
      to,
      from,
      twiml: `<Response><Say>${message}</Say></Response>`,
    });
  });
});

This test suite verifies the functionality of TwilioProgrammableVoiceService, focusing on its interaction with the Twilio API. It uses NestJS's TestingModule to mock the TwilioService dependency, ensuring the tests remain isolated from external services.

  • Mocking Twilio Client:
    • A mock Twilio client is provided to simulate the behavior of the real API.
    • The calls.create method is mocked to return a resolved value containing a sid for test validation, avoiding actual API calls.
  • Service Initialization:
    • The TwilioService is mocked to provide predefined responses for getClient() and getTwilioPhoneNumber(). This ensures the TwilioProgrammableVoiceService can operate with the mocked Twilio client.
  • Tests:
    • Service Definition: Verifies that the TwilioProgrammableVoiceService is defined, ensuring the test setup is correct.
    • API Call Verification:
      • Tests the makeVoiceCall method to confirm it interacts with the Twilio API correctly.
      • Validates that the calls.create method is called with the appropriate parameters, including the to number, from number, and the generated TwiML for the call message.

Implementing the Programmable Voice Service

In the Twilio Programmable Voice service source file, define the logic for initiating calls:

// twilio/twilio-programmable-voice/twilio-programmable-voice.service.ts
import { Injectable, Logger } from '@nestjs/common';
import { Twilio } from 'twilio';
import { TwilioService } from '../twilio.service';
@Injectable()
export class TwilioProgrammableVoiceService {
    private readonly logger = new Logger(TwilioProgrammableVoiceService.name);
    constructor(private twilioService: TwilioService) { }
    async makeVoiceCall(to: string, message: string): Promise<void> {
        try {
            const client = this.twilioService.getClient();
            const from = this.twilioService.getTwilioPhoneNumber();
            this.logger.log(`Initiating voice call to ${to} from ${from}`);
            const call = await client.calls.create({
                to,
                from,
                twiml: `<Response><Say>${message}</Say></Response>`,
            });
            this.logger.log(`Voice call initiated successfully. Call SID: ${call.sid}`);
        } catch (error) {
            this.logger.error(`Failed to make voice call to ${to}: ${error.message}`, error.stack);
            throw error;
        }
    }
}

This implementation of TwilioProgrammableVoiceService handles voice call initiation using the Twilio SDK. The makeVoiceCall method takes a to number and message, retrieves the client and phone number from TwilioService, and uses them to create a call with a dynamically generated <Say> message. logging tracks call initiation, success (logging the call SID), and errors, which are re-thrown after being logged for debugging. This ensures clean, modular, and maintainable call logic

Run the tests now to ensure functionality:

yarn test
Terminal displaying executed commands, test results, file creation logs, and path details.

Creating the Reminders Resource

We will create a reminders resource to handle scheduling logic. For simplicity, we’ll only add an endpoint to create a reminder, but as an exercise you can add endpoints to fetch, update, and delete reminders.

Generate the Resource

Run the following command to scaffold the resource:

nest g resource reminders

Choose REST API and enable CRUD endpoints. This will generate the necessary files for a controller, service, and DTOs.

Controller: Handles incoming HTTP requests and routes them to the appropriate service methods. Service: Contains the core business logic and interacts with data sources or other services. DTO (Data Transfer Object): Defines the shape of data for requests and responses, ensuring consistency and validation.

Writing Tests for the Reminders Controller

Before implementing the controller, write tests to validate its functionality in the Reminders Controller spec file:

// reminders/reminders.controller.spec.ts
import { Test, TestingModule } from '@nestjs/testing';
import { RemindersController } from './reminders.controller';
import { RemindersService } from './reminders.service';
describe('RemindersController', () => {
  let controller: RemindersController;
  let service: RemindersService;
  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      controllers: [RemindersController],
      providers: [
        {
          provide: RemindersService,
          useValue: {
            create: jest.fn(),
          },
        },
      ],
    }).compile();
    controller = module.get<RemindersController>(RemindersController);
    service = module.get<RemindersService>(RemindersService);
  });
  it('should be defined', () => {
    expect(controller).toBeDefined();
  });
  describe('createReminder', () => {
    it('should call RemindersService.create with correct parameters', async () => {
      const createReminderDto = {
        phone: '+1234567890',
        message: 'Test message',
        scheduledTime: '2024-12-30T15:00:00Z',
      };
      jest.spyOn(service, 'create').mockResolvedValue({
        success: true,
        message: 'Reminder scheduled successfully.',
      });
      const result = await controller.create(createReminderDto);
      expect(service.create).toHaveBeenCalledWith(createReminderDto);
      expect(result).toEqual({
        success: true,
        message: 'Reminder scheduled successfully.',
      });
    });
  });
});

This test suite shows how the RemindersController interacts with the RemindersService. The beforeEach block sets up a testing module with mock dependencies, isolating the controller to focus purely on its behavior. This ensures the tests remain independent and targeted.

The createReminder test confirms that the controller correctly delegates tasks to the service. By mocking the service and verifying input/output behavior, we validate the controller's functionality without relying on external components. Such practices strengthen code quality and ensure scalable APIs in NestJS.

Implementing the Reminders Controller

With the tests written, implement the RemindersController in its source file:

// reminders/reminders.controller.ts
import { Controller, Post, Body } from '@nestjs/common';
import { RemindersService } from './reminders.service';
import { CreateReminderDto } from './dto/create-reminder.dto';
@Controller('reminders')
export class RemindersController {
  constructor(private readonly remindersService: RemindersService) {}
  @Post()
  async create(@Body() createReminderDto: CreateReminderDto) {
    return await this.remindersService.create(createReminderDto);
  }
}

This code defines a NestJS controller for handling reminders, specifically creating them via a POST request to /reminders. It uses the RemindersService (which will be implemented later) to handle business logic and a CreateReminderDto to validate the request body, ensuring clean separation of concerns and modularity.

Let’s also write the code for the CreateReminderDto class:

// reminders/dto/create-reminder.dto.ts
import { IsNotEmpty, IsPhoneNumber, IsDateString } from 'class-validator'
export class CreateReminderDto {
    @IsNotEmpty()
    @IsPhoneNumber()
    phone: string;
    @IsNotEmpty()
    message: string;
    @IsNotEmpty()
    @IsDateString()
    scheduledTime: string;
}
@IsNotEmpty() ensures the fields are not empty. @IsPhoneNumber() validates the phone number format for the phone field. @IsDateString() ensures the scheduledTime field is a valid ISO 8601 date string.

Implementing the Reminders Service

We’ll now move on to implementing the business logic for the reminders controller. In NestJS lingo, we’ll be implementing a “service”.

Writing Tests for the Service

Like we did before, we’ll start by writing the unit tests for the Reminders service:

// reminders/reminders.service.spec.ts
import { Test, TestingModule } from '@nestjs/testing';
import { RemindersService } from './reminders.service';
import { SchedulerRegistry } from '@nestjs/schedule';
import { TwilioProgrammableVoiceService } from 'src/twilio/twilio-programmable-voice/twilio-programmable-voice.service';
describe('RemindersService', () => {
  let service: RemindersService;
  let schedulerRegistry: SchedulerRegistry;
  let twilioService: TwilioProgrammableVoiceService;
  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        RemindersService,
        {
          provide: SchedulerRegistry,
          useValue: {
            addCronJob: jest.fn(),
          },
        },
        {
          provide: TwilioProgrammableVoiceService,
          useValue: {
            makeVoiceCall: jest.fn(),
          },
        },
      ],
    }).compile();
    service = module.get<RemindersService>(RemindersService);
    schedulerRegistry = module.get<SchedulerRegistry>(SchedulerRegistry);
    twilioService = module.get<TwilioProgrammableVoiceService>(TwilioProgrammableVoiceService);
  });
  it('should be defined', () => {
    expect(service).toBeDefined();
  });
  describe('triggerCall', () => {
    it('should call TwilioProgrammableVoiceService.makeVoiceCall', async () => {
      const phone = '+1234567890';
      const message = 'Test message';
      jest.spyOn(twilioService, 'makeVoiceCall').mockResolvedValue();
      await (service as any).triggerCall(phone, message);
      expect(twilioService.makeVoiceCall).toHaveBeenCalledWith(phone, message);
    });
  });
});

This test suite for RemindersService effectively isolates the service logic by using TestingModule to mock dependencies like SchedulerRegistry and TwilioProgrammableVoiceService. Mocking with jest.fn() replaces real implementations, allowing the tests to run quickly and predictably without relying on external systems.

A notable part of the suite is the test for the triggerCall method. It uses jest.spyOn() to mock and track calls to makeVoiceCall in the TwilioProgrammableVoiceService, ensuring the method behaves correctly without triggering actual API calls. This approach keeps the tests focused and allows for validating interactions between components while maintaining modularity and clarity. The setup lays a solid foundation for ensuring the reliability of the service.

Implementing the Service

With tests written, implement the RemindersService in the source file:

// reminders/reminders.service.ts
import { Injectable, Logger } from '@nestjs/common';
import { CreateReminderDto } from './dto/create-reminder.dto';
import { CronJob } from 'cron';
import { SchedulerRegistry } from '@nestjs/schedule';
import { TwilioProgrammableVoiceService } from 'src/twilio/twilio-programmable-voice/twilio-programmable-voice.service';
@Injectable()
export class RemindersService {
  private readonly logger = new Logger(RemindersService.name);
  constructor(
    private schedulerRegistry: SchedulerRegistry,
    private twilioProgrammableVoiceService: TwilioProgrammableVoiceService,
  ) {}
  async create(createReminderDto: CreateReminderDto) {
    const { phone, message, scheduledTime } = createReminderDto;
    const date = new Date(scheduledTime);
    if (date < new Date()) {
      throw new Error('Scheduled time must be in the future.');
    }
    const job = new CronJob(date, () => {
      this.triggerCall(phone, message);
    });
    this.schedulerRegistry.addCronJob(`reminder-${phone}-${date.getTime()}`, job);
    job.start();
    this.logger.log(
      `Reminder created: phone=${phone}, message="${message}", scheduledTime=${scheduledTime}`,
    );
    return { success: true, message: 'Reminder scheduled successfully.' };
  }
  private async triggerCall(phone: string, message: string) {
    this.logger.log(`Triggering call: phone=${phone}, message="${message}"`);
    try {
      await this.twilioProgrammableVoiceService.makeVoiceCall(phone, message);
    } catch (error) {
      this.logger.error(`Failed to trigger call: ${error.message}`);
    }
  }
}

This implementation of RemindersService manages the creation and scheduling of reminders using cron jobs. The create method validates that the scheduledTime is in the future, then sets up a CronJob to execute the triggerCall method at the specified time. The job is registered with the SchedulerRegistry using a unique identifier and started immediately. Logging provides details about the created reminder.

The triggerCall method handles voice call execution through TwilioProgrammableVoiceService. It logs call attempts and captures any errors, ensuring robust error handling. This design cleanly separates scheduling from execution, making the service modular and easy to debug.

Before this can run, we must register the Task Scheduler and Reminders modules in the App Module file:

// app.module.ts
import { Module } from '@nestjs/common';
import { ScheduleModule } from '@nestjs/schedule';
import { RemindersController } from './reminders/reminders.controller';
import { RemindersService } from './reminders/reminders.service';
import { TwilioService } from './twilio/twilio.service';
import { ConfigModule } from '@nestjs/config';
import { TwilioProgrammableVoiceService } from './twilio/twilio-programmable-voice/twilio-programmable-voice.service';
@Module({
  imports: [ConfigModule.forRoot(), ScheduleModule.forRoot()],
  controllers: [RemindersController],
  providers: [RemindersService, TwilioService, TwilioProgrammableVoiceService],
})
export class AppModule { }

Run tests once more to ensure functionality:

yarn test
Terminal displaying test results and code updates for a Node.js application with Pass/Fail statuses.

Testing the Application

Run the application in development mode:

yarn start:dev
Running in development mode tells Nest to “ hot-reload” whenever any piece of code changes, making the whole process much more efficient from a DX point of view.

Adding a Caller ID for Trial Accounts

If you are using a Twilio trial account, you must verify your phone number as a Caller ID to allow Twilio to call it. Follow these steps:

A screenshot of the Twilio dashboard showing a welcome message and instructions for making your first call.

1. Log in to the Twilio Console..

2. Navigate to the Phone Numbers section under the Manage tab.

3. Select Verified Caller IDs.

4. Click + Add a new Caller ID.

5. Enter your phone number and click Verify.

6. Twilio will text or call your number and provide a verification code. Enter the code to complete the verification.

Once your number is verified, you can use it in the phone field when testing the /reminders endpoint.

Trial accounts can only call verified numbers. To remove this limitation, upgrade your Twilio account.

Test the POST /reminders endpoint using curl:

For MacOS command line:

curl -X POST http://localhost:3000/reminders \
-H "Content-Type: application/json" \
-d '{
  "phone": "+1234567890",
  "message": "This is your reminder 30 seconds from now!",
  "scheduledTime": "'"$(date -u -v+30S +"%Y-%m-%dT%H:%M:%SZ")"'"
}'

Alternatively, for Linux users: (the date command is different)

curl -X POST http://localhost:3000/reminders \
-H "Content-Type: application/json" \
-d '{
  "phone": "+1234567890",
  "message": "This is your reminder 30 seconds from now!",
  "scheduledTime": "'"$(date -u -d '+30 seconds' --iso-8601=seconds)"'"
}'

Alternatively, for Windows users:

$scheduledTime = (Get-Date).AddSeconds(30).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")
$jsonBody = @"
{
    "phone": "+1234567890",
    "message": "This is your reminder 30 seconds from now!",
    "scheduledTime": "$scheduledTime"
}
"@
curl -X POST http://localhost:3000/reminders `
-H "Content-Type: application/json" `
-d $jsonBody

You should replace "+1234567890" with your actual phone number that will receive the call.

A dark-themed messaging app displaying chat history, contact list, and call notifications on a desktop screen.

[ demo video on vimeo]

Conclusion

You have successfully built a call scheduling service using NestJS and Twilio. This modular approach ensures scalability and maintainability, allowing for easy expansion into features like SMS reminders, microservices architecture, or recurring schedules.

Further Resources

To deepen your understanding of the technologies used in this project, explore the following resources:

- NestJS Documentation: Learn more about the NestJS framework and its powerful features.

- Twilio Documentation: Explore the Twilio platform, including Programmable Voice and other APIs.

- Programmable Voice API Reference: Understand how to fully leverage Twilio's voice capabilities.

- Jest Documentation: Learn more about writing and running tests with Jest.

By combining these resources with hands-on practice, you can continue to build robust and scalable applications.