An automated interview screening system powered by ElevenLabs AI, Twilio, and OpenAI. Built with Deno and React Email.
- 🎙️ Automated voice interviews using ElevenLabs AI
- 📞 Phone integration with Twilio
- 📧 Email communications via React Email & Resend
- 🤖 Natural language processing with OpenAI
- 🔄 Webhook integration for dynamic interview content
- 📊 Automated interview evaluation and feedback
- 💾 Interview transcription and storage
- 🗄️ Supabase for data persistence
- Deno 1.37 or higher
- ngrok for webhook testing
- Supabase account and project
- API keys for:
- ElevenLabs
- Twilio
- Resend
- OpenAI
- Supabase
- Clone the repository
- Create a
.envfile in the root directory:
RESEND_API_KEY=your_resend_api_key
FROM_EMAIL=your_sender_email
ELEVENLABS_API_KEY=your_elevenlabs_api_key
OPENAI_API_KEY=your_openai_api_key
TWILIO_ACCOUNT_SID=your_twilio_sid
TWILIO_AUTH_TOKEN=your_twilio_token
SUPABASE_URL=your_supabase_url
SUPABASE_KEY=your_supabase_keydeno install/
├── src/
│ ├── controllers/ # Request handlers
│ │ ├── email/
│ │ └── webhook/
│ ├── routes/ # API routes
│ ├── types/ # TypeScript types
│ ├── services/ # Business logic
│ │ ├── elevenlabs/ # Voice & transcript services
│ │ ├── openai/ # Interview evaluation
│ │ └── db/ # Data storage
│ └── middleware/ # Custom middleware
├── emails/ # Email templates
├── tests/ # Test files
└── main.ts # Application entry
# Load .env
source .env
# Start development server with watch mode
deno task dev
# Start production server
deno task start
# Run tests
deno task testPOST /api/jobs- Create a new job postingGET /api/jobs- List all active jobsPUT /api/jobs/:jobId- Update job detailsDELETE /api/jobs/:jobId- Delete a job posting
POST /api/applications- Submit job applicationGET /api/jobs/:jobId/applications- List applications for a jobGET /api/applications/:applicationId- Get single applicationPUT /api/applications/:applicationId- Update application status
POST /api/interviews- Create interview recordGET /api/interviews/:interviewId- Get interview with analysis
POST /api/analysis- Create skill analysisGET /api/analysis/:analysisId- Get specific analysis
POST /api/email/send-invite- Send interview invitationPOST /api/email/send-reminder- Send interview reminder
POST /api/webhook/twilio/interview- Handle Twilio interview callsPOST /api/webhook/twilio/status- Process call status updates
- Recruiter creates job posting
- Candidate applies for job
- Recruiter reviews application and triggers interview
- Candidate receives interview invitation
- Candidate calls the Twilio number
- ElevenLabs AI conducts the interview
- When call completes:
- Call transcript is fetched
- OpenAI evaluates the interview
- Results are stored
- Notifications are sent
- Recruiter reviews interview results and updates application status
- Start the development server:
deno task dev- In a new terminal, start ngrok:
ngrok http 8000- Use the ngrok URLs in your Twilio configuration:
# Interview webhook (goes in elevenlabs)
https://your-ngrok-url.ngrok.io/api/webhook/twilio/interview
# Status webhook (goes in twillio)
https://your-ngrok-url.ngrok.io/api/webhook/twilio/status
curl -X POST http://localhost:8000/api/webhook/twilio/status \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "CallSid=CA202d9ee8b1bb71f2ee332b80cb6f9b3d&CallStatus=completed&Duration=5&Caller=%2B16172512600&Timestamp=Sun%2C%2023%20Feb%202025%2001%3A09%3A55%20%2B0000"curl -X POST http://localhost:8000/api/email/send-invite \
-H "Content-Type: application/json" \
-d '{
"email": "candidate@example.com",
"invitationData": {
"company": {
"name": "ElevenLabs",
"logoUrl": "https://eleven-public-cdn.elevenlabs.io/payloadcms/9trrmnj2sj8-logo-logo.svg",
"recruiterCompany": "HireLabz"
},
"candidate": {
"name": "John Doe"
},
"position": {
"title": "Software Engineer",
"department": "Engineering"
}
}
}'- Create a new job:
curl -X POST http://localhost:8000/api/jobs \
-H "Content-Type: application/json" \
-d '{
"job_name": "Senior Software Engineer",
"job_description": "We are looking for a Senior Software Engineer with strong experience in distributed systems and cloud architecture.",
"section_description": "Technical Interview: System Design and Architecture",
"sections": {
"technical": ["System Design", "Coding", "Architecture"],
"behavioral": ["Leadership", "Communication", "Problem Solving"]
}
}'- List all active jobs:
curl http://localhost:8000/api/jobs- Update a job:
curl -X PUT http://localhost:8000/api/jobs/123 \
-H "Content-Type: application/json" \
-d '{
"job_name": "Lead Software Engineer",
"job_description": "Updated role focusing on team leadership and distributed systems",
"section_description": "Technical Interview: Leadership and System Design",
"status": true
}'- Delete a job:
curl -X DELETE http://localhost:8000/api/jobs/123- Apply for a job:
curl -X POST http://localhost:8000/api/applications \
-H "Content-Type: application/json" \
-d '{
"job_id": "123",
"first_name": "John",
"last_name": "Doe",
"email": "john.doe@example.com",
"resume_url": "https://example.com/resume.pdf",
"linkedin_url": "https://linkedin.com/in/johndoe",
"portfolio_url": "https://johndoe.dev",
"github_url": "https://github.com/johndoe"
}'- Get single application:
curl http://localhost:8000/api/applications/456- List all applications:
curl http://localhost:8000/api/applications- Update application status:
curl -X PUT http://localhost:8000/api/applications/456 \
-H "Content-Type: application/json" \
-d '{
"status": "interviewed",
"resume_url": "https://example.com/updated-resume.pdf",
"linkedin_url": "https://linkedin.com/in/johndoe",
"portfolio_url": "https://johndoe.dev",
"github_url": "https://github.com/johndoe"
}'- Send interview invitation:
curl -X POST http://localhost:8000/api/email/send-invite \
-H "Content-Type: application/json" \
-d '{
"email": "candidate@example.com",
"invitationData": {
"company": {
"name": "ElevenLabs",
"logoUrl": "https://eleven-public-cdn.elevenlabs.io/payloadcms/9trrmnj2sj8-logo-logo.svg",
"recruiterCompany": "HireLabz"
},
"candidate": {
"name": "John Doe"
},
"position": {
"title": "Software Engineer",
"department": "Engineering"
}
}
}'- Test Twilio status webhook:
curl -X POST http://localhost:8000/api/webhook/twilio/status \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "CallSid=CA202d9ee8b1bb71f2ee332b80cb6f9b3d&CallStatus=completed&Duration=5&Caller=%2B16172512600&Timestamp=Sun%2C%2023%20Feb%202025%2001%3A09%3A55%20%2B0000"- Create an interview record:
curl -X POST http://localhost:8000/api/interviews \
-H "Content-Type: application/json" \
-d '{
"applicant_id": "123",
"transcript": "Interview transcript text...",
"overall_rating": 85,
"summary": "Strong candidate with excellent technical skills..."
}'- Get interview with analysis:
curl http://localhost:8000/api/interviews/456- Create a skill analysis:
curl -X POST http://localhost:8000/api/analysis \
-H "Content-Type: application/json" \
-d '{
"interview_id": "456",
"skill_name": "System Design",
"skill_score": 90,
"skill_reasoning": "Demonstrated strong understanding of distributed systems..."
}'- Get specific analysis:
curl http://localhost:8000/api/analysis/789{
"data": {
"id": "456",
"applicant_id": "123",
"transcript": "Interview transcript text...",
"overall_rating": 85,
"summary": "Strong candidate with excellent technical skills...",
"created_at": "2024-02-23T01:09:55.976Z",
"analysis": [
{
"id": "789",
"interview_id": "456",
"skill_name": "System Design",
"skill_score": 90,
"skill_reasoning": "Demonstrated strong understanding of distributed systems...",
"created_at": "2024-02-23T01:10:55.976Z"
}
]
}
}- Error response:
{
"error": "Failed to create job",
"details": "Error message details"
}- Replace
123and456with actual IDs from your database - Use
jqfor pretty-printed JSON responses:
curl http://localhost:8000/api/jobs | jq- For local testing, ensure the server is running:
deno task dev- For webhook testing, ensure ngrok is running:
ngrok http 8000This project uses Deno's security features with explicit permissions:
--allow-env: For accessing environment variables--allow-net: For API endpoints and external services--allow-read: For reading configuration files