An AI-powered professional assistant built with Laravel 13 and the Laravel AI SDK. It represents James Gifford in conversations with potential employers, recruiters, and hiring managers through REST API, SMS, and email interfaces.
This application uses multiple AI providers (Anthropic Claude primary, OpenAI GPT-4 fallback) with automatic failover and health monitoring. Conversation history is persisted per session, enabling multi-turn conversations across all channels.
- REST API —
POST /api/chatfor direct integration and testing - SMS — Twilio webhook at
POST /webhook/smsfor text message conversations - Email — Resend webhook at
POST /webhook/resend/inboundfor email conversations
Each conversation and individual message is tagged with a channel value to distinguish the origin:
'web'— Messages from the browser-based chat UI'api'— Messages from the REST API'sms'— Messages from Twilio SMS'email'— Messages from Resend inbound email
This allows conversations that span multiple channels to track the origin of each individual message.
- PHP 8.3+
- Composer
- SQLite (default) or MySQL
- Node.js & npm (for frontend assets)
git clone <repo-url>
cd professional-assistant
composer install
npm installcp .env.example .env
php artisan key:generateEdit .env with your credentials:
# AI Providers (required — at least one)
ANTHROPIC_API_KEY=sk-ant-...
OPENAI_API_KEY=sk-...
AI_PRIMARY_PROVIDER=anthropic
AI_FALLBACK_PROVIDER=openai
# Twilio (required for SMS)
TWILIO_ACCOUNT_SID=AC...
TWILIO_AUTH_TOKEN=...
TWILIO_PHONE_NUMBER=+15551234567
# Resend (required for email)
RESEND_API_KEY=re_...
RESEND_INBOUND_ADDRESS=ask@jamesgifford.ai
RESEND_WEBHOOK_SECRET=whsec_...
# Mail (uses Resend as the transport)
MAIL_MAILER=resend
MAIL_FROM_ADDRESS=ask@jamesgifford.ai
MAIL_FROM_NAME="James Gifford's AI Assistant"php artisan migratenpm run buildphp artisan servecurl -X POST http://localhost:8000/api/chat \
-H "Content-Type: application/json" \
-d '{"session_key": "test-1", "message": "Tell me about James'\''s experience"}'curl -X POST http://localhost:8000/api/chat \
-H "Content-Type: application/json" \
-d '{"session_key": "test-1", "message": "What are his salary expectations?"}'curl -X POST http://localhost:8000/api/chat \
-H "Content-Type: application/json" \
-d '{"session_key": "test-1", "message": "How was this assistant built?"}'curl -X POST http://localhost:8000/api/chat \
-H "Content-Type: application/json" \
-d '{"session_key": "test-2", "message": "What is James'\''s home address?"}'curl http://localhost:8000/api/chat/testcurl http://localhost:8000/api/healthphp artisan ai:health- Install ngrok:
brew install ngrok - Start your server:
php artisan serve - Start ngrok:
ngrok http 8000 - Configure Twilio webhook URL to
https://<ngrok-url>/webhook/sms - Text your Twilio phone number from your phone
Webhook signature validation is bypassed in local and testing environments.
- Sign up at resend.com and get your API key
- Go to Domains and add your domain (e.g.,
jamesgifford.ai) - Configure the required DNS records:
- MX record pointing to Resend's inbound servers
- SPF, DKIM, and DMARC records for outbound delivery
- Go to Webhooks and create a new webhook:
- URL:
https://<your-domain>/webhook/resend/inbound - Events: Select
email.received - Copy the signing secret to
RESEND_WEBHOOK_SECRETin.env
- URL:
RESEND_API_KEY=re_... # Your Resend API key
RESEND_INBOUND_ADDRESS=ask@jamesgifford.ai # The email address receiving inbound mail
RESEND_WEBHOOK_SECRET=whsec_... # Webhook signing secret from Resend dashboard
MAIL_MAILER=resend # Use Resend as the mail transport
MAIL_FROM_ADDRESS=ask@jamesgifford.ai
MAIL_FROM_NAME="James Gifford's AI Assistant"# Start your server
php artisan serve
# In another terminal, start ngrok
ngrok http 8000
# In Resend dashboard → Webhooks → Add webhook:
# URL: https://<ngrok-url>/webhook/resend/inbound
# Events: email.received
# Send an email to your Resend inbound address
# Verify the AI reply arrives in your inbox and threads correctly# Check the webhook route exists
php artisan route:list --path=webhook/resend
# Verify a conversation was created with channel='email'
php artisan tinker --execute="echo App\Models\Conversation::where('channel', 'email')->count();"
# Verify messages have channel='email'
php artisan tinker --execute="echo App\Models\Conversation::where('channel', 'email')->first()?->messages()->pluck('channel');"The application includes a health monitoring system for AI providers:
- Scheduled health checks run every 5 minutes via
php artisan ai:health - Each check sends a test prompt to each provider and measures response time
- Results are cached with a 5-minute TTL
- If the primary provider is marked as "down", requests route directly to the fallback
- The
GET /api/healthendpoint returns current provider status
{
"providers": {
"anthropic": {
"status": "up",
"latency_ms": 823,
"last_checked": "2026-03-19T10:30:00Z"
},
"openai": {
"status": "up",
"latency_ms": 641,
"last_checked": "2026-03-19T10:30:00Z"
}
},
"active_provider": "anthropic"
}Add this to your crontab for automatic health checks:
* * * * * cd /path-to-project && php artisan schedule:run >> /dev/null 2>&1-
Add the provider's API key to
.env:GEMINI_API_KEY=...
-
The provider is already configured in
config/ai.php(the Laravel AI SDK ships with support for Anthropic, OpenAI, Gemini, Groq, Mistral, DeepSeek, and more). -
To use it as the primary or fallback provider:
AI_PRIMARY_PROVIDER=gemini AI_FALLBACK_PROVIDER=anthropic
-
Update the health check model mapping in
app/Services/AiProviderService.phpif needed.
# All tests
php artisan test --compact
# Specific test file
php artisan test --compact tests/Feature/Api/ChatTest.php
# Filter by name
php artisan test --compact --filter="handles an incoming SMS"app/
├── Ai/Agents/ProfessionalAssistant.php # AI agent with system prompt
├── Console/Commands/AiHealthCheck.php # Health check artisan command
├── Http/
│ ├── Controllers/
│ │ ├── Api/
│ │ │ ├── ChatController.php # REST API endpoint
│ │ │ └── HealthController.php # Health check endpoint
│ │ └── Webhook/
│ │ ├── SmsController.php # Twilio SMS webhook
│ │ └── EmailController.php # Resend email webhook
│ ├── Middleware/
│ │ ├── VerifyTwilioSignature.php # Twilio webhook auth
│ │ └── VerifyResendSignature.php # Resend webhook auth (Svix)
│ └── Requests/Api/
│ └── ChatRequest.php # Chat validation
├── Mail/ProfessionalAssistantReply.php # Email reply mailable
├── Models/Conversation.php # Conversation persistence
└── Services/AiProviderService.php # Provider failover logic