Docs/GENERAL/Bluesky Integration

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 website
  • POST /api/bsky/keywords - Add a new keyword
  • DELETE /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 settings
  • BlueskyKeywordItem - Individual keyword item with remove button

Dashboard

  • BlueskyDashboard - Main dashboard page
  • BlueskyPostCard - 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

  1. Create a Bluesky account for your bot
  2. Go to Settings → App Passwords
  3. Create a new app password
  4. 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

  1. Navigate to Dashboard → Settings → Integrations
  2. Find the Bluesky Mentions card
  3. Add keywords (up to 5 per website)
  4. Wait for the next hourly cron run (or trigger manually)
  5. 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 emailNotifications field 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 sentiment field 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

  1. Check if keywords are added in settings
  2. Wait for next hourly cron run
  3. Check cron logs for errors
  4. Verify Bluesky credentials are correct

Cron not running

  1. Verify vercel.json is deployed
  2. Check Vercel dashboard → Cron Jobs
  3. Ensure CRON_SECRET matches in deployment

Authentication errors

  1. Verify BSKY_IDENTIFIER and BSKY_APP_PASSWORD are correct
  2. Check if app password is still valid on Bluesky
  3. 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