|
|
3 months ago | |
|---|---|---|
| cli | 5 months ago | |
| .gitignore | 3 months ago | |
| README.md | 3 months ago | |
| app.py | 3 months ago | |
| dashboard.html | 3 months ago | |
| pytest.ini | 5 months ago | |
| requirements-test.txt | 5 months ago | |
| requirements.txt | 5 months ago | |
| setup_encryption.sh | 5 months ago | |
| test-lines.bash | 5 months ago | |
| test_api.sh | 5 months ago |
Letta now provides built-in scheduling! This project has been simplified to a web dashboard that calls Letta's API directly.
🎛️ Web Dashboard: https://letta--switchboard-api.modal.run/dashboard
📖 Letta Scheduling Docs: https://docs.letta.com/guides/agents/scheduling/
💻 GitHub: https://github.com/cpfiffer/letta-switchboard
The dashboard now calls Letta's API directly! Just continue using it - no changes needed.
If you were calling the old Switchboard API, you need to migrate to Letta's native API:
Old Endpoint (Deprecated):
POST https://letta--switchboard-api.modal.run/schedules/one-time
{
"agent_id": "agent-xxx",
"execute_at": "2025-11-13T09:00:00Z",
"message": "Hello",
"role": "user"
}
New Endpoint (Use This):
POST https://api.letta.com/v1/agents/agent-xxx/schedule
{
"schedule": {
"type": "one-time",
"scheduled_at": 1731499200000
},
"messages": [{
"role": "user",
"content": "Hello"
}]
}
Key Changes:
execute_at (ISO string) → scheduled_at (Unix milliseconds)message (string) → messages (array of message objects)Full Documentation: https://docs.letta.com/guides/agents/scheduling/
Visit https://letta--switchboard-api.modal.run/dashboard
Features:
For programmatic access, use Letta's scheduling API:
Create a one-time schedule:
curl -X POST https://api.letta.com/v1/agents/agent-xxx/schedule \
-H "Authorization: Bearer YOUR_LETTA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"schedule": {
"type": "one-time",
"scheduled_at": 1731499200000
},
"messages": [{
"role": "user",
"content": "Hello!"
}]
}'
Create a recurring schedule:
curl -X POST https://api.letta.com/v1/agents/agent-xxx/schedule \
-H "Authorization: Bearer YOUR_LETTA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"schedule": {
"type": "recurring",
"cron_expression": "0 9 * * 1-5"
},
"messages": [{
"role": "user",
"content": "Daily standup"
}]
}'
List schedules:
curl https://api.letta.com/v1/agents/agent-xxx/schedule \
-H "Authorization: Bearer YOUR_LETTA_API_KEY"
Delete a schedule:
curl -X DELETE https://api.letta.com/v1/agents/agent-xxx/schedule/SCHEDULE_ID \
-H "Authorization: Bearer YOUR_LETTA_API_KEY"
Full Documentation: https://docs.letta.com/guides/agents/scheduling/
A simple web dashboard for managing Letta agent schedules. The dashboard calls Letta's native scheduling API directly from your browser.
✅ Web Dashboard - Clean interface for managing schedules
✅ Direct Letta Integration - Calls Letta API from browser
✅ No Backend - Pure client-side (except for hosting the HTML)
✅ Timezone Support - Schedule in local time or UTC
✅ Free to Use - Just visit the URL
Easy to Use
Simple interface for creating and managing Letta schedules without writing API calls.
No Installation
Just visit the URL - no setup, no configuration, no CLI to install.
Direct Letta API
Your schedules are stored and executed by Letta Cloud. Check execution history at https://app.letta.com
Architecture:
# Relative time
--execute-at "in 5 minutes"
--execute-at "in 2 hours"
--execute-at "in 3 days"
# Tomorrow
--execute-at "tomorrow at 9am"
--execute-at "tomorrow at 14:30"
# Next weekday
--execute-at "next monday at 3pm"
--execute-at "next friday at 10:00"
# ISO 8601 (still works)
--execute-at "2025-11-12T19:30:00Z"
# Minutes
--cron "every 5 minutes"
--cron "every 30 minutes"
# Hourly/Daily
--cron "every hour"
--cron "daily at 9am"
--cron "daily at 14:30"
# Weekdays
--cron "every monday"
--cron "every friday at 3pm"
--cron "every weekday" # Mon-Fri at 9am
# Traditional cron (still works)
--cron "*/5 * * * *" # Every 5 minutes
This will test all endpoints (create, list, get, delete) for both recurring and one-time schedules.
**Features:**
- Validates API key before running
- Shows configuration at startup
- Tests create, list, get, and delete operations
- Pretty prints all responses
### Bash Test Script
```bash
./test_api.sh
Same functionality using curl commands.
Example with inline variables:
LETTA_API_KEY=sk-xxx LETTA_AGENT_ID=agent-yyy python test_api.py
The easiest way to interact with letta-switchboard is via the CLI:
# Send a message immediately
letta-switchboard send --agent-id agent-xxx --message "Hello!"
# Send a message later
letta-switchboard send --agent-id agent-xxx --message "Reminder" --execute-at "tomorrow at 9am"
# Create recurring schedule
letta-switchboard recurring create --agent-id agent-xxx --message "Daily standup" --cron "every weekday at 9am"
# List schedules
letta-switchboard onetime list
letta-switchboard recurring list
# View results
letta-switchboard results list
See CLI Documentation for installation and full usage guide.
Base URL: https://letta--schedules-api.modal.run
All endpoints require Bearer token authentication using your Letta API key:
curl -H "Authorization: Bearer your-letta-api-key" https://your-modal-app.modal.run/schedules/recurring
Security Model:
list agents call with limit=1)Error Codes:
401 Unauthorized: Invalid or expired Letta API key403 Forbidden: Valid API key, but trying to access someone else's schedule404 Not Found: Schedule doesn't existSchedule a message to be sent on a cron schedule:
curl -X POST https://your-modal-app.modal.run/schedules/recurring \
-H "Content-Type: application/json" \
-d '{
"agent_id": "agent-123",
"api_key": "your-letta-api-key",
"cron": "0 9 * * *",
"message": "Good morning! Time for your daily check-in.",
"role": "user"
}'
Cron Format: minute hour day month day_of_week
0 9 * * * - Every day at 9:00 AM*/15 * * * * - Every 15 minutes0 */2 * * * - Every 2 hours0 0 * * 0 - Every Sunday at midnightSchedule a message for a specific time:
curl -X POST https://your-modal-app.modal.run/schedules/one-time \
-H "Content-Type: application/json" \
-d '{
"agent_id": "agent-123",
"api_key": "your-letta-api-key",
"execute_at": "2025-11-07T14:30:00-05:00",
"message": "Reminder: Meeting in 30 minutes",
"role": "user"
}'
Timestamp Format: ISO 8601 with timezone
2025-11-07T14:30:00-05:00 (EST)2025-11-07T14:30:00Z (UTC)# List your recurring schedules
curl -H "Authorization: Bearer your-letta-api-key" \
https://your-modal-app.modal.run/schedules/recurring
# List your one-time schedules
curl -H "Authorization: Bearer your-letta-api-key" \
https://your-modal-app.modal.run/schedules/one-time
# Get recurring schedule
curl -H "Authorization: Bearer your-letta-api-key" \
https://your-modal-app.modal.run/schedules/recurring/{schedule_id}
# Get one-time schedule
curl -H "Authorization: Bearer your-letta-api-key" \
https://your-modal-app.modal.run/schedules/one-time/{schedule_id}
# Delete recurring schedule
curl -X DELETE -H "Authorization: Bearer your-letta-api-key" \
https://your-modal-app.modal.run/schedules/recurring/{schedule_id}
# Delete one-time schedule
curl -X DELETE -H "Authorization: Bearer your-letta-api-key" \
https://your-modal-app.modal.run/schedules/one-time/{schedule_id}
# List all execution results
curl -H "Authorization: Bearer your-letta-api-key" \
https://your-modal-app.modal.run/results
# Get result for specific schedule
curl -H "Authorization: Bearer your-letta-api-key" \
https://your-modal-app.modal.run/results/{schedule_id}
Result Format:
{
"schedule_id": "uuid",
"schedule_type": "recurring",
"run_id": "run_abc123",
"agent_id": "agent-123",
"message": "The scheduled message",
"executed_at": "2025-11-07T00:15:00"
}
Note: Results are stored when the message is queued to Letta. To check the actual run status, use the Letta API:
# Get the run_id from results
RESULT=$(curl -H "Authorization: Bearer your-letta-api-key" \
https://your-modal-app.modal.run/results/{schedule_id})
RUN_ID=$(echo $RESULT | jq -r '.run_id')
# Check run status with Letta
curl -H "Authorization: Bearer your-letta-api-key" \
https://api.letta.com/v1/runs/$RUN_ID
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"agent_id": "agent-123",
"cron": "0 9 * * *",
"message": "Good morning!",
"role": "user",
"created_at": "2025-11-06T10:00:00",
"last_run": "2025-11-06T09:00:00"
}
Note: API keys are stored securely and never returned in responses.
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"agent_id": "agent-123",
"execute_at": "2025-11-07T14:30:00-05:00",
"message": "Reminder!",
"role": "user",
"created_at": "2025-11-06T10:00:00"
}
Note:
/results endpoint for execution history)last_run timestamp in schedule fileRace Condition Prevention:
Schedules and execution results are stored in a hash-based directory structure:
/data/
├── schedules/
│ ├── recurring/
│ │ ├── {api_key_hash}/ # SHA256 hash of API key (first 16 chars)
│ │ │ ├── {uuid-1}.json.enc # Encrypted schedule files
│ │ │ └── {uuid-2}.json.enc
│ │ └── {another_hash}/
│ │ └── {uuid-3}.json.enc
│ └── one-time/
│ ├── 2025-11-06/ # Date bucket
│ │ ├── 14/ # Hour bucket (00-23)
│ │ │ ├── {api_key_hash}/
│ │ │ │ └── {uuid}.json.enc
│ │ │ └── {another_hash}/
│ │ │ └── {uuid}.json.enc
│ │ └── 15/
│ └── 2025-11-07/
└── results/
├── {api_key_hash}/
│ ├── {schedule_uuid}.json.enc # Execution results with run_id
│ └── {schedule_uuid}.json.enc
└── {another_hash}/
Security Features:
Performance Benefits:
View logs in Modal dashboard:
modal app logs letta-switchboard
Or watch logs in real-time:
modal app logs letta-switchboard --follow
Modal pricing (as of 2024):
Free tier: 30 credits/month (~$30 value)
Want to run your own instance? Switchboard is fully self-hostable on Modal.
Clone the repository:
git clone https://github.com/cpfiffer/letta-switchboard.git
cd letta-switchboard
Install Modal CLI:
pip install modal
modal setup
1. Set up encryption (required):
# Automated setup
./setup_encryption.sh
# Or manually
ENCRYPTION_KEY=$(python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())")
echo "Save this key: $ENCRYPTION_KEY"
modal secret create letta-switchboard-encryption \
LETTA_SWITCHBOARD_ENCRYPTION_KEY="$ENCRYPTION_KEY"
2. Deploy to Modal:
modal deploy app.py
3. Get your API URL:
modal app list
# Look for 'switchboard' and note the URL
4. Configure CLI to use your instance:
letta-switchboard config set-url https://your-instance.modal.run
Run locally with hot reloading:
# Enable dev mode (no encryption)
export LETTA_SWITCHBOARD_DEV_MODE=true
# Start local server
modal serve app.py
Dev Mode Features:
View files in dev mode:
# View a schedule
cat /tmp/letta-switchboard-volume/schedules/recurring/abc123/uuid.json | jq
# List all schedules
ls -la /tmp/letta-switchboard-volume/schedules/
Set environment variables:
export LETTA_API_KEY="sk-..."
export LETTA_AGENT_ID="agent-xxx"
export LETTA_SWITCHBOARD_URL="https://your-instance.modal.run"
Run tests:
# Python test suite
python test_api.py
# Bash test script
./test_api.sh
# Unit tests
pytest -m "not e2e"
# Full E2E tests (requires modal serve running)
pytest
Running your own instance on Modal:
The hosted service at letta--switchboard-api.modal.run is free to use!
MIT