Skip to main content

Environment Configuration

Configure DutyCall for local development and production deployment.

Overview

DutyCall uses environment variables to configure backend and frontend behavior. This guide covers:

  • Local development configuration (.env and .env.local)
  • Production deployment configuration (Railway + Vercel)
  • Environment-specific differences

Backend Environment (.env)

Local Development

Location: backend/.env (copy from backend/.env.example)

# Application
APP_NAME="Duty Call - Agent Workspace Platform"
APP_ENV=local
APP_DEBUG=true
APP_KEY=base64:... # Generated by php artisan key:generate
APP_URL=http://localhost:8090

# Frontend (for CORS)
FRONTEND_URL=http://localhost:3000

# Database (MySQL for local)
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=dutycall_local
DB_USERNAME=root
DB_PASSWORD= # Your MySQL password (often empty)

# Twilio Configuration
TWILIO_ACCOUNT_SID=ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
TWILIO_AUTH_TOKEN=your_auth_token_here
TWILIO_PHONE_NUMBER_DEV=+18316033889
TWILIO_PHONE_NUMBER_PROD=+16282373889

# WebRTC (for browser-based calling)
TWILIO_API_KEY=SKxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
TWILIO_API_SECRET=your_api_secret_here
TWILIO_TWIML_APP_SID=APf62bdf0bc5c380c61e8534b43ee6479e
TWILIO_EDGE=ashburn # Optimize routing

# Ngrok (updated automatically by dev-start.sh)
NGROK_URL=https://your-ngrok-url.ngrok-free.app

# Google OAuth (optional)
GOOGLE_CLIENT_ID=your_client_id
GOOGLE_CLIENT_SECRET=your_client_secret
GOOGLE_CALLBACK_URL=http://localhost:8090/auth/google/callback

# Google Sheets API (optional, for contact imports)
GOOGLE_SERVICE_ACCOUNT_JSON=storage/app/google/service-account.json
Important Notes
  • Never commit .env to version control (already in .gitignore)
  • NGROK_URL is automatically updated by ./dev-start.sh
  • Twilio credentials are for dev environment - contact project lead for full values
  • APP_KEY must be unique - generate with php artisan key:generate

Production (Railway)

Location: Railway Dashboard → Environment Variables

# Application
APP_ENV=production
APP_DEBUG=false # CRITICAL: Must be false in production
APP_KEY=base64:... # Generate new key for production
APP_URL=https://dutycall-production.up.railway.app

# Frontend
FRONTEND_URL=https://dutycall.com # Your production domain

# Database (PostgreSQL - provided by Railway)
DB_CONNECTION=pgsql
DB_HOST=${PGHOST} # Railway provides these
DB_PORT=${PGPORT}
DB_DATABASE=${PGDATABASE}
DB_USERNAME=${PGUSER}
DB_PASSWORD=${PGPASSWORD}

# Twilio (production credentials)
TWILIO_ACCOUNT_SID=your_prod_account_sid
TWILIO_AUTH_TOKEN=your_prod_auth_token
TWILIO_PHONE_NUMBER=+16282373889 # Production number
TWILIO_API_KEY=your_prod_api_key
TWILIO_API_SECRET=your_prod_api_secret
TWILIO_TWIML_APP_SID=your_prod_twiml_app_sid
TWILIO_EDGE=ashburn

# NO NGROK_URL in production!
# Code falls back to APP_URL automatically
Production Security
  • Never use local credentials in production
  • Never enable APP_DEBUG=true in production
  • Always use HTTPS for APP_URL and FRONTEND_URL
  • Use strong, unique passwords for all services

Frontend Environment (.env.local)

Local Development

Location: frontend/.env.local (copy from frontend/.env.example)

# Backend API URL
NEXT_PUBLIC_API_URL=http://localhost:8090

# Environment
NEXT_PUBLIC_APP_ENV=local
Simple Configuration

Frontend only needs to know where the backend API is located. All other configuration happens on the backend.

Production (Vercel)

Location: Vercel Dashboard → Environment Variables

# Backend API URL (Railway)
NEXT_PUBLIC_API_URL=https://dutycall-production.up.railway.app

# Environment
NEXT_PUBLIC_APP_ENV=production
Deployment

Vercel automatically rebuilds when environment variables change. No need to manually redeploy.


The NGROK_URL Pattern

Why NGROK_URL Exists

Twilio sends webhook callbacks to your server when calls happen. In production, your server has a public URL (Railway). In local development, your laptop doesn't have a public URL - that's where ngrok comes in.

The Problem:

  • Twilio needs to reach your backend to deliver webhooks
  • Your laptop (localhost:8090) is not accessible from the internet
  • Twilio can't call http://localhost:8090/api/twilio/inbound

The Solution:

  • Ngrok creates a public URL that tunnels to your localhost
  • Example: https://abc123.ngrok-free.applocalhost:8090
  • Twilio calls the ngrok URL, which forwards to your laptop

How It Works in Code

Backend (TwilioWebhookController.php):

// Generate callback URL
$waitUrl = env('NGROK_URL', env('APP_URL')) . '/api/twilio/queue-wait';

// Local: Uses NGROK_URL → https://abc123.ngrok-free.app/api/twilio/queue-wait
// Production: Falls back to APP_URL → https://dutycall-production.up.railway.app/api/twilio/queue-wait

This pattern appears throughout the codebase wherever Twilio callbacks are generated.

Automatic Configuration

The ./dev-start.sh script handles everything:

  1. Starts ngrok tunnel to localhost:8090
  2. Extracts the public URL from ngrok API
  3. Updates .env with NGROK_URL=https://abc123.ngrok-free.app
  4. Configures Twilio dev number to use the ngrok URL
  5. Keeps ngrok running in the foreground

You never have to manually configure this!


Environment-Specific Differences

Local vs Production Comparison

FeatureLocalProduction
Backendlocalhost:8090Railway (https://dutycall-production.up.railway.app)
Frontendlocalhost:3000Vercel (https://dutycall.com)
DatabaseMySQL (localhost)PostgreSQL (Railway)
Twilio Number+1 831 603 3889 (dev)+1 628 237 3889 (prod)
Webhook DeliveryNgrok tunnelDirect to Railway
Debug ModeAPP_DEBUG=trueAPP_DEBUG=false
SSL/HTTPSNot requiredRequired
Google OAuthlocalhost callbackProduction domain callback

What Changes Between Environments

Backend .env Changes:

# Local                              # Production
APP_ENV=local APP_ENV=production
APP_DEBUG=true APP_DEBUG=false
APP_URL=http://localhost:8090 APP_URL=https://dutycall-production.up.railway.app
DB_CONNECTION=mysql DB_CONNECTION=pgsql
NGROK_URL=https://xxx.ngrok.io # Not set in production

Frontend .env.local Changes:

# Local                              # Production
NEXT_PUBLIC_API_URL=http://localhost:8090 NEXT_PUBLIC_API_URL=https://dutycall-production.up.railway.app
NEXT_PUBLIC_APP_ENV=local NEXT_PUBLIC_APP_ENV=production

Twilio Configuration

Phone Number Configuration

Twilio phone numbers need to know where to send webhook callbacks. This configuration differs between local and production.

Local Development (automated by dev-start.sh):

Phone Number: +1 831 603 3889 (dev)
Voice URL: https://YOUR-NGROK-URL.ngrok-free.app/api/twilio/inbound (POST)
Status Callback: https://YOUR-NGROK-URL.ngrok-free.app/api/twilio/status (POST)

Production (manual configuration in Twilio Console):

Phone Number: +1 628 237 3889 (prod)
Voice URL: https://dutycall-production.up.railway.app/api/twilio/inbound (POST)
Status Callback: https://dutycall-production.up.railway.app/api/twilio/status (POST)

TwiML App Configuration

For WebRTC calling, Twilio needs a TwiML App configured:

Local Development:

TwiML App: DutyCall Dev
Voice Request URL: https://YOUR-NGROK-URL.ngrok-free.app/api/twilio/agent-dial-queue (POST)

Production:

TwiML App: DutyCall Production
Voice Request URL: https://dutycall-production.up.railway.app/api/twilio/agent-dial-queue (POST)
Update After Ngrok Restarts

Every time ngrok restarts, the URL changes. The dev-start.sh script automatically updates the phone number configuration, but you may need to manually update the TwiML App Voice Request URL in Twilio Console.

Environment Variables Explained

TWILIO_ACCOUNT_SID: Your Twilio account identifier

  • Find: Twilio Console → Account Info
  • Example: ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

TWILIO_AUTH_TOKEN: Secret key for API authentication

  • Find: Twilio Console → Account Info (click "View")
  • Keep secret! Never commit to version control

TWILIO_API_KEY and TWILIO_API_SECRET: For Access Token generation

  • Find: Twilio Console → Account → API Keys → Create new
  • Used for WebRTC browser calling

TWILIO_TWIML_APP_SID: TwiML Application for WebRTC routing

  • Find: Twilio Console → Voice → TwiML Apps
  • Example: APf62bdf0bc5c380c61e8534b43ee6479e

TWILIO_EDGE: Latency optimization (optional)

  • Options: ashburn, dublin, sydney, tokyo
  • Default: ashburn (US East Coast)

CORS Configuration

Understanding CORS

CORS (Cross-Origin Resource Sharing) controls which domains can access your backend API. Since the frontend and backend run on different domains (localhost:3000 and localhost:8090), CORS must be configured.

Backend (config/cors.php):

return [
'paths' => ['api/*', 'sanctum/csrf-cookie', 'dashboard/*', 'auth/*', 'analytics/*'],
'allowed_origins' => [env('FRONTEND_URL', 'http://localhost:3000')],
'allowed_methods' => ['*'],
'allowed_headers' => ['*'],
'supports_credentials' => true,
];

Local Development

# Backend .env
FRONTEND_URL=http://localhost:3000

This allows requests from http://localhost:3000 to access the backend API.

Production

# Backend .env (Railway)
FRONTEND_URL=https://dutycall.com

This restricts API access to only your production frontend domain.

Security

Never use 'allowed_origins' => ['*'] in production - this allows any website to access your API!


Common Configuration Issues

Issue: Frontend Can't Connect to Backend

Symptoms: API calls fail with network errors or 404s

Check:

  1. Backend running on localhost:8090?
  2. Frontend .env.local has NEXT_PUBLIC_API_URL=http://localhost:8090?
  3. CORS configured in backend/config/cors.php?

Fix:

# Backend
cd backend
php artisan config:clear
php artisan cache:clear

# Frontend
cd frontend
rm -rf .next
npm run dev

Issue: Twilio Webhooks Failing

Symptoms: Calls fail, webhooks return 404 or 500 errors

Check:

  1. .env has correct NGROK_URL?
  2. Ngrok tunnel active? curl http://localhost:4040/api/tunnels
  3. Backend restarted after .env changes?
  4. Twilio phone number pointing to ngrok URL?

Fix:

# Restart ngrok and backend
pkill -f ngrok
pkill -f "php artisan serve"
./dev-start.sh # In one terminal
php artisan serve --port=8090 # In another terminal

Issue: Device Not Registering (WebRTC)

Symptoms: "Device not registered" errors in frontend

Check:

  1. Twilio credentials correct in backend .env?
  2. Token endpoint working? curl http://localhost:8090/api/voice/token
  3. Browser console showing errors?
  4. TwiML App configured with correct URL?

Fix:

# Test token generation
curl -X POST http://localhost:8090/api/voice/token \
-H "Authorization: Bearer YOUR_AUTH_TOKEN" \
-H "Content-Type: application/json" \
-d '{"recording_enabled": false}'

Best Practices

Security

  • Never commit .env files to version control
  • Use different credentials for local and production
  • Disable debug mode in production (APP_DEBUG=false)
  • Use HTTPS for all production URLs
  • Rotate Twilio credentials periodically

Development Workflow

  • Keep .env.example updated when adding new variables
  • Document all environment variables in this guide
  • Use dev-start.sh for ngrok automation
  • Clear Laravel cache after changing .env: php artisan config:clear
  • Rebuild frontend after changing NEXT_PUBLIC_* variables

Production Deployment

  • Test locally first before deploying to production
  • Use Railway environment variables (not hardcoded values)
  • Monitor logs after deployment for configuration errors
  • Keep production credentials secret - don't share in Slack/email

Next Steps


Need Help? Contact the project lead or check backend/README.md for detailed configuration reference.