Skip to content

Repository files navigation

SAM Bid Alerts

A multi-tenant SaaS platform for tracking federal contract opportunities from SAM.gov with automated email notifications.

Features

  • 🔍 Custom Watchlists - Create filters based on NAICS codes, states, keywords, procurement types, and set-aside codes
  • 📧 Smart Notifications - Choose immediate, daily, or weekly email digests
  • 🏢 Multi-Tenant - Perfect for agencies managing multiple client watchlists
  • 🔄 Automatic Syncing - Hourly ingestion of new opportunities from SAM.gov
  • 🔐 Secure Authentication - Powered by Clerk

Tech Stack

  • Framework: Next.js 15 (App Router)
  • Language: TypeScript
  • Database: Neon Postgres + Drizzle ORM
  • Authentication: Clerk
  • Email: Resend + React Email
  • Styling: Tailwind CSS + shadcn/ui
  • Validation: Zod
  • Deployment: Vercel

Prerequisites

  • Node.js 18+ installed
  • pnpm package manager
  • Accounts for:

Getting Started

1. Clone and Install

git clone <your-repo>
cd leadmagnet-site
pnpm install

2. Environment Variables

Copy .env.example to .env.local and fill in your credentials:

cp .env.example .env.local

Required variables:

# Clerk Authentication
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_...
CLERK_SECRET_KEY=sk_test_...

# Database (Neon Postgres)
DATABASE_URL=postgresql://...

# SAM.gov API (from https://api.data.gov/signup/)
SAM_API_KEY=...

# Resend Email
RESEND_API_KEY=re_...

# App Configuration
NEXT_PUBLIC_APP_URL=http://localhost:3000

# Cron Job Secret (generate a random string)
CRON_SECRET=your-random-secret-here

3. Set Up Database

Generate and run migrations:

pnpm db:generate
pnpm db:migrate

Or push schema directly (development):

pnpm db:push

4. Configure Clerk

  1. Create a new application in Clerk Dashboard
  2. Enable email/password authentication
  3. Copy your publishable and secret keys to .env.local
  4. Set up redirect URLs:
    • Sign in: /sign-in
    • Sign up: /sign-up
    • After sign in: /dashboard
    • After sign up: /dashboard

5. Get SAM.gov API Key

  1. Register at api.data.gov
  2. You'll receive an API key via email
  3. Add it to your .env.local as SAM_API_KEY

6. Configure Resend

  1. Create account at Resend
  2. Verify your sending domain
  3. Create an API key
  4. Add to .env.local as RESEND_API_KEY
  5. Update email "from" address in src/lib/resend.ts to match your verified domain

7. Run Development Server

pnpm dev

Visit http://localhost:3000

Deployment

Deploy to Vercel

  1. Push your code to GitHub
  2. Import project in Vercel
  3. Add all environment variables
  4. Deploy

Configure Cron Jobs

Cron jobs are automatically configured via vercel.json:

  • SAM Ingest (/api/cron/sam-ingest): Runs hourly at :00
  • Match Watchlists (/api/cron/match-watchlists): Runs hourly at :15

In Vercel dashboard:

  1. Go to Settings → Environment Variables
  2. Add CRON_SECRET (same value as local)
  3. Cron jobs will use this secret for authentication

Database Scripts

# Generate migrations from schema changes
pnpm db:generate

# Run migrations
pnpm db:migrate

# Push schema directly (development)
pnpm db:push

# Open Drizzle Studio (database GUI)
pnpm db:studio

Project Structure

src/
├── app/
│   ├── (auth)/              # Authentication pages
│   ├── (marketing)/         # Public marketing pages
│   ├── dashboard/           # Protected dashboard pages
│   └── api/                 # API routes
│       ├── watchlists/      # Watchlist CRUD
│       └── cron/            # Scheduled jobs
├── components/
│   ├── ui/                  # shadcn/ui components
│   ├── emails/              # React Email templates
│   └── app-sidebar.tsx      # Dashboard sidebar
├── db/
│   ├── schema.ts            # Drizzle schema
│   └── index.ts             # Database client
└── lib/
    ├── sam.ts               # SAM.gov API client
    ├── resend.ts            # Email helpers
    ├── validators.ts        # Zod schemas
    └── utils.ts             # Utility functions

API Routes

Watchlists

  • GET /api/watchlists - List organization's watchlists
  • POST /api/watchlists - Create new watchlist
  • GET /api/watchlists/[id] - Get single watchlist
  • PUT /api/watchlists/[id] - Update watchlist
  • DELETE /api/watchlists/[id] - Soft delete watchlist
  • GET /api/watchlists/[id]/preview - Preview SAM.gov results

Cron Jobs

  • POST /api/cron/sam-ingest - Ingest opportunities from SAM.gov
  • POST /api/cron/match-watchlists - Match watchlists and send notifications

SAM.gov API Notes

  • Rate limit: Unknown, implement delays between requests
  • Date range: Maximum 1 year between postedFrom and postedTo
  • Pagination: Max 1000 records per request, use offset for more
  • Date format: MM/dd/yyyy (required)
  • Active filter: Only opportunities with active === "Yes" are stored

Database Schema

See src/db/schema.ts for full schema. Key tables:

  • users - User accounts (synced from Clerk)
  • organizations - Multi-tenant organizations
  • organization_members - User-org relationships
  • subscriptions - Plan limits and billing
  • watchlists - Saved searches with filters
  • opportunities - Cached SAM.gov opportunities
  • user_opportunity_notifications - Match tracking
  • email_logs - Email delivery tracking
  • usage_events - Analytics and billing events

Development Tips

  • Use pnpm db:studio to inspect database during development
  • Check cron job logs in Vercel dashboard under "Logs" tab
  • Test email templates using Resend's test mode
  • Use Clerk's development mode for easier testing

License

MIT

Support

For issues or questions, please open a GitHub issue.

About

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages