Bluesky Integration
This integration allows users to track mentions of specific keywords on Bluesky for each website.
Features
- Add up to 5 keywords per website
- Automatic hourly fetching of matching posts
- Dashboard to view all mentions (Development mode only)
- Filter by keyword
- Direct links to original posts on Bluesky
- Settings accessible in all environments
Development Mode
⚠️ Important: The Bluesky dashboard page (/dashboard/[id]/bluesky) is currently only accessible in development mode while the feature is being tested and refined.
- Settings tab: Available in all environments (add/remove keywords)
- Dashboard viewing: Only accessible when
NODE_ENV=development - API endpoints: Fully functional in all environments
- Cron job: Runs in all environments (when configured)
Architecture
Models
- BskyKeyword: Stores keywords to track for each website
- BskyPost: Stores posts matching keywords (duplicates allowed per website)
API Endpoints
Keyword Management
GET /api/bsky/keywords?websiteId=xyz- List keywords for a websitePOST /api/bsky/keywords- Add a new keywordDELETE /api/bsky/keywords/[id]- Remove a keyword
Posts
GET /api/bsky/posts?websiteId=xyz&keyword=claude&limit=50&offset=0- Fetch posts for dashboard
Cron Job
GET /api/bsky/search- Fetches posts for all keywords (runs hourly)
Components
Settings
BlueskyIntegrationCard- Main integration card in settingsBlueskyKeywordItem- Individual keyword item with remove button
Dashboard
BlueskyDashboard- Main dashboard pageBlueskyPostCard- Individual post card component
Environment Variables
Add these to your .env.local file:
# Bluesky API Configuration
BSKY_PDS=https://bsky.social
BSKY_IDENTIFIER=your-bot@bsky.social
BSKY_APP_PASSWORD=your-bot-app-password
# Cron Security (optional but recommended)
CRON_SECRET=your-random-secret-here
Getting Bluesky Credentials
- Create a Bluesky account for your bot
- Go to Settings → App Passwords
- Create a new app password
- Use your handle and app password in the env vars
Cron Configuration
Vercel
The vercel.json file is already configured to run the cron job hourly:
{
"crons": [
{
"path": "/api/bsky/search",
"schedule": "0 * * * *"
}
]
}
Alternative: External Cron Service
If not using Vercel, you can use services like:
- EasyCron
- GitHub Actions
- Upstash QStash
Make sure to include the CRON_SECRET in the Authorization header:
Authorization: Bearer YOUR_CRON_SECRET
Usage
For Users
- Navigate to Dashboard → Settings → Integrations
- Find the Bluesky Mentions card
- Add keywords (up to 5 per website)
- Wait for the next hourly cron run (or trigger manually)
- View mentions in Dashboard → Bluesky Mentions
Manual Cron Trigger (Development)
curl -H "Authorization: Bearer YOUR_CRON_SECRET" \
https://your-domain.com/api/bsky/search
Storage Strategy
The integration uses Option A: Duplicates Allowed:
- Same post can exist multiple times in database (once per website)
- Example: "claude" post matches 3 websites → saved 3 times with different websiteIds
- Unique constraint:
{ uri + websiteId }composite
Why Duplicates?
- Simple queries (no joins needed)
- Clear ownership per website
- Easy deletions (user deletes website, posts are gone)
- Independent metrics per website
Performance Considerations
Deduplication in Cron
The cron job searches each unique keyword only once across all websites:
Website A: ["claude", "anthropic"]
Website B: ["claude", "nextjs"]
Website C: ["anthropic"]
Unique searches: ["claude", "anthropic", "nextjs"] = 3 API calls
Posts are then duplicated in the database for each website that tracks that keyword.
Indexes
{ websiteId, keyword }- Unique composite on BskyKeyword{ uri, websiteId }- Unique composite on BskyPost{ websiteId, createdAt }- Dashboard queries{ websiteId, keyword }- Filtering
Testing
Manual Testing Checklist
- Add keyword to website
- Add 6th keyword (should fail)
- Add duplicate keyword (should fail)
- Remove keyword
- Run cron endpoint manually
- Verify posts saved to database
- View posts in dashboard
- Filter by keyword
- Links to Bluesky work
Edge Cases
- Empty keyword string
- Very long keyword (>100 chars)
- Special characters in keyword
- Multiple websites with same keyword
- User doesn't own website (auth check)
- Bluesky API down (error handling)
- No posts found for keyword
- Posts with missing fields
Future Enhancements
Email Notifications
Email users when new mentions are found:
- Add
emailNotificationsfield to BskyKeyword - Check if post is new in cron job
- Send email digest
Sentiment Analysis
Analyze sentiment of mentions:
- Use OpenAI API or local model
- Add
sentimentfield to BskyPost - Display indicator in dashboard
Analytics Charts
Visualize mention trends:
- Line chart: Mentions per day
- Bar chart: Engagement metrics
- Pie chart: Sentiment distribution
Reply from Dashboard
Reply to posts directly from TrackFox:
- Requires Bluesky OAuth
- Store user's Bluesky credentials
- Add reply functionality to post cards
Webhook Integration
Send webhooks when new mentions found:
- Support Slack, Discord, custom webhooks
- Configure in website settings
Troubleshooting
Posts not appearing
- Check if keywords are added in settings
- Wait for next hourly cron run
- Check cron logs for errors
- Verify Bluesky credentials are correct
Cron not running
- Verify
vercel.jsonis deployed - Check Vercel dashboard → Cron Jobs
- Ensure
CRON_SECRETmatches in deployment
Authentication errors
- Verify
BSKY_IDENTIFIERandBSKY_APP_PASSWORDare correct - Check if app password is still valid on Bluesky
- Try creating a new app password
API Rate Limits
Bluesky API rate limits (as of 2024):
- 3000 requests per 5 minutes per IP
- 5000 requests per hour per account
The cron job should stay well within these limits with the deduplication strategy.
Security
Cron Secret
Always set CRON_SECRET in production to prevent unauthorized cron triggers:
CRON_SECRET=$(openssl rand -base64 32)
Keyword Validation
Keywords are validated on the server side:
- 1-100 characters
- No injection attacks (MongoDB escaping)
- Case-insensitive matching
Access Control
All endpoints use checkWebsiteAccess or checkSettingsAccess:
- Only owners can add/remove keywords
- Only users with access can view posts
- Team members have read-only access
Database Size Considerations
With 100 active websites, 5 keywords each, 50 posts per keyword per day:
- 100 websites × 5 keywords × 50 posts = 25,000 posts/day
- ~750,000 posts/month
- ~9M posts/year
Each post is ~1KB, so yearly storage: ~9GB
Consider adding a cleanup job to remove posts older than X months if needed.
Need help? Contact us for assistance.
Suggest features? We'd love your feedback