A scalable, cloud-native voicemail service built on Cloudflare Workers that provides automated voice recording, storage, and management capabilities. The service offers multi-provider support with robust error handling and comprehensive logging.
- 🚀 Serverless Architecture: Built on Cloudflare Workers for global edge deployment
- 📞 Multi-Provider Support: Currently supports Twilio with extensible architecture for additional providers
- 🔊 Flexible Recording Options: Support for both URL-based audio prompts and text-to-speech
- 💾 Cloud Storage: Automatic recording storage using Cloudflare R2
- 📋 Comprehensive Logging: Detailed call metadata and recording indexing
- 🔧 Configuration-Driven: Environment-based configuration for easy deployment
- 📖 OpenAPI Documentation: Auto-generated API documentation with interactive interface using Chanfana
- 🛡️ Type Safety: Full TypeScript implementation with Zod validation
- ✅ Comprehensive Testing: Unit and integration tests with Vitest
The service is built using modern web technologies:
- Runtime: Cloudflare Workers
- Framework: Hono with Chanfana for OpenAPI support
- Storage: Cloudflare R2 for recording files and metadata
- Validation: Zod for runtime type checking
- Voice Processing: Provider-specific SDKs (Twilio)
- Testing: Vitest with comprehensive test coverage
- Type Safety: TypeScript with strict configuration
- Cloudflare Workers account (free tier sufficient)
- Node.js 16+ (specified in package.json engines)
- pnpm (package manager - required)
- Wrangler CLI
-
Clone the repository
git clone https://github.com/YasogaN/voicemail-cf.git cd voicemail-cf -
Install dependencies
pnpm install
-
Authenticate with Cloudflare
wrangler login
-
Setup Providers
Choose and configure your voice service provider:
Twilio
Create a Twilio API key with restricted scopes for security:
- Log in to your Twilio Console
- Navigate to API keys & tokens
- Click Create API key
- Set the key type to Restricted
- Configure the following scopes:
voice.calls:readvoice.recordings:readvoice.recordings:delete
- Save the API Key SID and Secret (you won't be able to see the secret again)
- Purchase a phone number in your Twilio Console
- Configure the webhook URL for your phone number:
- Voice webhook:
https://your-worker.workers.dev/incoming - HTTP method:
GET
- Voice webhook:
Configure the following Twilio-specific environment variables:
# Twilio API credentials wrangler secret put twilio_api_key # Your Twilio API Key SID wrangler secret put twilio_api_secret # Your Twilio API Key Secret
-
Configure General Environment Variables
Set the following general environment variables that apply to all providers:
# Provider configuration wrangler secret put provider # Your chosen provider (e.g., twilio) wrangler secret put endpoint # Your deployed worker URL # Phone numbers (comma-separated for multiple numbers) wrangler secret put numbers # +1234567890,+0987654321 # Recording configuration wrangler secret put recording_type # url or text wrangler secret put recording_url # URL to audio file (if type=url) wrangler secret put recording_text # Text to speak (if type=text) wrangler secret put recording_max_length # Maximum recording duration in seconds
-
Create Cloudflare R2 bucket
Create an R2 bucket to store voicemail recordings:
# Create the R2 bucket (replace 'recordings' with your preferred name) wrangler r2 bucket create recordingsAlternatively, you can create the bucket through the Cloudflare dashboard:
- Go to Cloudflare Dashboard
- Navigate to R2 Object Storage
- Click "Create bucket"
- Enter a unique bucket name (e.g.,
recordings) - Choose your preferred location
- Click "Create bucket"
Note: Make sure to update your
wrangler.jsoncfile to reference the bucket name you created. -
Deploy to Cloudflare Workers
wrangler deploy
The project includes comprehensive testing with Vitest:
- Unit Tests: Individual endpoint and provider logic testing
- Provider-Specific Tests: Organized by provider (e.g.,
twilio/) for better maintainability - Integration Tests: End-to-end workflow testing (placeholder directory ready)
- Configuration Tests: Environment variable validation testing
- Mock Support: R2 bucket mocking with
cloudflare-test-utils - Test Utilities: Centralized test configuration and provider-specific helpers
# Run all tests
pnpm test
# Run tests with coverage report
pnpm run test:coverage
# Run tests in watch mode during development
pnpm run test:watchThe test suite covers:
- All API endpoints (
/health,/incoming,/record,/hangup,/store) - Provider implementations (Twilio) with dedicated test suites
- Configuration validation and parsing
- Error handling scenarios
- TwiML response generation
Tests are organized by provider for better maintainability:
src/test/
├── setup.ts # Global test setup and mock environment
├── types.test.ts # Type validation tests
├── endpoint/ # Provider-specific endpoint tests
│ └── twilio/ # Twilio-specific test implementations
│ ├── hangup.test.ts
│ ├── health.test.ts
│ ├── incoming.test.ts
│ ├── record.test.ts
│ └── store.test.ts
├── lib/
│ └── config.test.ts # Configuration validation tests
└── utils/
└── test-config.ts # Test utilities and provider configurations
-
Start local development server
pnpm run dev
This starts the Wrangler development server with hot reloading.
-
Access the API documentation Open
http://localhost:8787/to view the interactive OpenAPI documentation powered by Chanfana. -
Test endpoints The Swagger interface allows you to test all endpoints directly from the browser, or use tools like curl/Postman.
-
Run tests during development
pnpm run test:watch
-
Generate TypeScript types
pnpm run cf-typegen
This generates types based on your Cloudflare Workers environment.
| Variable | Description | Required | Example |
|---|---|---|---|
provider |
Voice service provider | Yes | twilio |
endpoint |
Base URL of your deployed worker | Yes | https://voicemail.your-domain.workers.dev |
twilio_api_key |
Twilio API Key SID (restricted) | Yes | SKxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx |
twilio_api_secret |
Twilio API Key Secret | Yes | your-api-key-secret |
numbers |
Comma-separated list of authorized numbers | Yes | +1234567890,+0987654321 |
recording_type |
Type of recording prompt | Yes | url or text |
recording_url |
URL to audio prompt (if type=url) | Conditional | https://example.com/prompt.mp3 |
recording_text |
Text-to-speech prompt (if type=text) | Conditional | "Please leave a message after the beep" |
recording_max_length |
Maximum recording duration in seconds | No | 30 |
| Endpoint | Method | Description |
|---|---|---|
/incoming |
GET | Handles incoming calls and routing |
/record |
GET | Provides recording instructions and prompts |
/hangup |
GET | Terminates calls after recording |
/store |
POST | Processes and stores completed recordings |
/health |
GET | Health check endpoint |
The current implementation provides the following call flow:
-
Incoming Call →
/incoming- Checks if caller number is in authorized numbers list
- Authorized numbers: Redirected to
/menu(currently returns 404 - menu system planned) - Unauthorized numbers: Redirected to
/recordfor voicemail recording
-
Recording →
/record- Plays configured prompt (audio file or text-to-speech)
- Initiates voice recording with specified parameters
- Automatically proceeds to hangup after recording
-
Completion →
/hangup- Terminates call after recording completion
- Returns TwiML hangup instruction
-
Storage →
/store- Receives recording callback from provider (currently Twilio)
- Downloads recording metadata and file
- Stores recording in Cloudflare R2 bucket
- Updates central index with call metadata
Note: The menu system for authorized callers is planned but not yet implemented. Currently, authorized numbers will receive a 404 response when redirected to /menu.
recordings/
├── index.json # Central metadata index
└── recordings/
├── RE1234567890.mp3 # Individual recording files
├── RE0987654321.mp3
└── ...
{
"recordingSid": "RE1234567890",
"callSid": "CA0987654321",
"start_time": "2025-01-15T10:30:00Z",
"duration": "45",
"from": "+1234567890",
"timestamp": "2025-01-15T10:31:00Z",
"mediaFile": "recordings/RE1234567890.mp3"
}src/
├── index.ts # Main application router with OpenAPI setup
├── types.ts # TypeScript type definitions and Zod schemas
├── endpoint/ # API endpoint handlers
│ ├── health.ts # Health check endpoint
│ ├── incoming.ts # Incoming call handler
│ ├── record.ts # Recording endpoint with TwiML generation
│ ├── hangup.ts # Call termination handler
│ └── store.ts # Recording storage and metadata handler
├── lib/
│ ├── config.ts # Environment configuration management
│ └── providers/ # Voice service provider implementations
│ ├── base.ts # Base provider abstract class
│ ├── index.ts # Provider factory and exports
│ └── twilio.ts # Twilio provider implementation
└── test/ # Comprehensive test suite
├── setup.ts # Global test setup and mock environment
├── types.test.ts # Type validation tests
├── endpoint/ # Provider-specific endpoint tests
│ └── twilio/ # Twilio-specific test implementations
│ ├── hangup.test.ts
│ ├── health.test.ts
│ ├── incoming.test.ts
│ ├── record.test.ts
│ └── store.test.ts
├── lib/
│ └── config.test.ts # Configuration validation tests
└── utils/
└── test-config.ts # Test utilities and provider configurations
- Create a new file in
src/endpoint/ - Implement the
OpenAPIRouteclass from Chanfana - Define the OpenAPI schema with proper Zod validation
- Register the route in
src/index.ts
- Create a new provider class in
src/lib/providers/ - Extend the
BaseProviderabstract class - Implement all required methods for call handling
- Add the provider to the factory function in
src/lib/providers/index.ts - Update the
ProviderConfigdiscriminated union insrc/types.ts
All endpoints implement comprehensive error handling:
- Input validation using Zod schemas
- Provider-specific error handling
- Graceful degradation for service failures
- Detailed error logging
The project includes comprehensive testing with Vitest:
# Run all tests
pnpm test
# Run tests with coverage
pnpm run test:coverage
# Run tests in watch mode
pnpm run test:watch
# Run development server
pnpm run dev
# Generate TypeScript types from Wrangler
pnpm run cf-typegen
# Deploy to production
pnpm run deployThe project currently includes:
- ✅ Core Voicemail Functionality: Complete recording, storage, and retrieval system
- ✅ Twilio Integration: Full support for Twilio voice services with dedicated test suite
- ✅ Cloudflare R2 Storage: Automatic recording and metadata storage
- ✅ OpenAPI Documentation: Interactive API documentation with Chanfana
- ✅ Comprehensive Testing: Modular unit and integration tests with Vitest, organized by provider
- ✅ Type Safety: Full TypeScript implementation with Zod validation
- ✅ Modern Package Management: Using pnpm for faster, more efficient dependency management
- Plivo Integration: SDK integration and webhook handlers
- Telnyx Integration: Call Control API integration
- SignalWire Integration: LaML response format support
- Bandwidth Integration: Voice API and BXML support
- Interactive Voice Menu: DTMF input handling and menu navigation
- Message Management: Play, save, delete, and forward capabilities
- Authentication System: PIN-based access control
- Multi-language Support: Localized prompts and menus
- AI-Powered Transcription: Using Cloudflare AI for speech-to-text
- Email Notifications: Recording attachments via email
- SMS Notifications: Text alerts for new voicemails
- Analytics Dashboard: Call volume and usage statistics
- Webhook Integration: External system notifications
We welcome contributions! Here's how to get started:
- Fork the repository
- Clone your fork and navigate to the project directory
- Install dependencies:
pnpm install - Set up your environment variables for testing
- Run tests to ensure everything works:
pnpm test - Start the development server:
pnpm dev
- Create a feature branch from main
- Make your changes with appropriate tests
- Ensure all tests pass:
pnpm test - Verify type safety:
pnpm cf-typegen - Test your changes in the development environment
- Submit a pull request with a clear description
- Follow TypeScript best practices
- Add comprehensive tests for new functionality
- Use Zod schemas for validation
- Follow the existing code structure and patterns
- Ensure OpenAPI documentation is updated for new endpoints
For security concerns, please email security@yasogan.dev instead of using the issue tracker.
This project is licensed under the Apache-2.0 License - see the LICENSE file for details.
Built with ❤️ using Cloudflare Workers, Hono, Chanfana, and TypeScript