A Next.js application that pairs referral seekers with employees who can provide referrals, using a credit-based escrow system to ensure accountability and reduce ghosting.
- Dual-sided Marketplace: Match "Ask" (job seekers) with "Give" (employees offering referrals)
- Credit-based Escrow: Automatic credit escrow when matches are created, released upon successful completion
- Proof of Referral: Upload and review referral evidence before releasing credits
- Reputation System: Track completion rates to incentivize quality referrals
- Match Deadlines: Automatic expiration of inactive matches with credit returns
- Real-time Chat: In-match messaging for coordination
- Next.js 15 with App Router
- Prisma ORM with SQLite (dev) / PostgreSQL (prod ready)
- NextAuth.js for Google & GitHub OAuth
- Vercel Blob for file uploads
- TypeScript throughout
- Tailwind CSS with shadcn/ui components
- Node.js 18+ and npm
- Google OAuth credentials
- GitHub OAuth credentials (optional)
- Vercel Blob storage token (for proof uploads)
-
Clone the repository
git clone <repository-url> cd ReferHub
-
Install dependencies
npm install
-
Set up environment variables Create a
.env.localfile in the root directory:# Database DATABASE_URL="file:./prisma/dev.db" # SQLite for dev # DATABASE_URL="postgresql://..." # PostgreSQL for production # NextAuth AUTH_SECRET="your-random-secret-here" # Generate with: openssl rand -base64 32 NEXTAUTH_URL="http://localhost:3000" # Your app URL # Google OAuth (required) GOOGLE_CLIENT_ID="your-google-client-id" GOOGLE_CLIENT_SECRET="your-google-client-secret" # GitHub OAuth (optional) GITHUB_ID="your-github-client-id" GITHUB_SECRET="your-github-client-secret" # File Storage (Vercel Blob) BLOB_READ_WRITE_TOKEN="your-vercel-blob-token"
-
Set up the database
npm run prisma:generate npm run prisma:migrate
-
Run the development server
npm run dev
-
Open your browser Navigate to http://localhost:3000
ReferHub/
├── src/
│ ├── app/ # Next.js App Router pages and API routes
│ │ ├── api/ # Backend API endpoints
│ │ │ ├── auth/ # NextAuth configuration
│ │ │ ├── credits/ # Credit management
│ │ │ ├── listings/ # Listing CRUD
│ │ │ ├── matches/ # Match lifecycle
│ │ │ ├── notifications/ # Notification system
│ │ │ ├── profile/ # User profile
│ │ │ └── proofs/ # Referral proof review
│ │ ├── dashboard/ # User dashboard
│ │ ├── listings/ # Listing management
│ │ ├── explore/ # Browse and request matches
│ │ ├── matches/ # Match rooms with chat
│ │ ├── settings/ # User settings
│ │ └── page.tsx # Landing page
│ ├── components/
│ │ ├── ui/ # shadcn/ui components
│ │ └── site-header.tsx # Main navigation
│ └── lib/
│ ├── auth.ts # NextAuth configuration
│ ├── prisma.ts # Prisma client
│ ├── notifications.ts # Notification helpers
│ └── utils.ts # Utility functions
├── prisma/
│ ├── schema.prisma # Database schema
│ └── migrations/ # Database migrations
└── public/ # Static assets
- Sign up with Google/GitHub OAuth
- Create an "Ask" listing with role, level, and target company
- Browse "Give" listings and request a match (costs 1 credit)
- Wait for acceptance (48-hour deadline)
- Chat with referrer to provide details
- Review proof of referral when submitted
- Approve proof to release escrow and complete the match
- Sign up with Google/GitHub OAuth
- Create a "Give" listing offering referrals
- Accept match requests (commits to helping)
- Chat with candidate to gather information
- Submit proof of referral (screenshot, email, etc.)
- Earn credit when proof is approved
- Profile: User accounts with reputation (completionRate, totalMatches, successfulMatches)
- Listing: Ask/Give posts with role, level, company info
- Match: Connections between Ask/Give with status tracking and deadlines
- Credit: Escrow system (AVAILABLE → ESCROWED → SPENT/RETURNED)
- ReferralProof: Uploaded evidence with approval workflow
- Message: In-match chat
- Notification: User alerts (in-app)
1. Asker requests match
2. System finds available credit
3. Credit status: AVAILABLE → ESCROWED
4. Match created with deadlines:
- acknowledgeBy: 48 hours
- submitBy: 7 days (reset when accepted)
5. Giver receives notification
1. Giver accepts match
2. Match status: PENDING → ACCEPTED
3. submitBy deadline reset to +7 days
4. Giver's totalMatches incremented
5. Asker receives notification
6. Chat enabled
1. Giver uploads proof file
2. Proof status: SUBMITTED
3. Asker reviews proof
4. If approved:
- Proof status: APPROVED
- Credit status: ESCROWED → SPENT
- New credit created for giver (AVAILABLE, source: EARNED)
- Giver's successfulMatches incremented
- Completion rate recalculated
- Both parties notified
5. If rejected:
- Proof status: REJECTED
- Escrow remains locked (manual intervention needed)
Run /api/matches/expire via cron (e.g., Vercel Cron)
1. Find matches past deadlines:
- PENDING matches past acknowledgeBy
- ACCEPTED matches past submitBy (no approved proof)
2. For each expired match:
- Match status: → EXPIRED
- Credit status: ESCROWED → RETURNED
- New AVAILABLE credit granted to asker
- Parties notified
POST /api/auth/signin- OAuth sign-inPOST /api/auth/signout- Sign out
GET /api/listings- Get user's listingsPOST /api/listings- Create listingPATCH /api/listings/[id]- Update listingDELETE /api/listings/[id]- Delete listingGET /api/listings/public- Browse public listings (filtered)
GET /api/matches- Get user's matchesPOST /api/matches- Request a match (escrows credit)GET /api/matches/[id]- Get match detailsPOST /api/matches/[id]/accept- Accept match (giver only)POST /api/matches/[id]/decline- Decline match (returns credit)POST /api/matches/expire- Expire matches past deadlines (cron)
GET /api/matches/[id]/messages- Get chat messagesPOST /api/matches/[id]/messages- Send message
GET /api/matches/[id]/proofs- Get proofs for matchPOST /api/matches/[id]/proofs- Submit proof (giver only)PATCH /api/proofs/[id]- Approve/reject proof (asker only)
GET /api/credits- Get credit countsPOST /api/credits- Grant dev credit (dev only)
GET /api/profile- Get current user profilePATCH /api/profile- Update profile
GET /api/notifications- Get notificationsPATCH /api/notifications- Mark notifications as read
POST /api/upload- Upload file to Vercel Blob (10MB max)
- Connect repository to Vercel
- Set environment variables in Vercel dashboard
- Update DATABASE_URL to PostgreSQL connection string
- Add Vercel Blob storage
- Deploy!
Add to vercel.json:
{
"crons": [
{
"path": "/api/matches/expire",
"schedule": "0 */6 * * *"
}
]
}# Update DATABASE_URL in .env
DATABASE_URL="postgresql://user:password@host:5432/referhub"
# Run migrations
npm run prisma:migrate- OAuth authentication (Google, GitHub)
- Listing creation and management
- Match request and acceptance
- Credit escrow system
- In-match chat
- Referral proof upload and review
- Reputation system (completion rates)
- Match deadlines and auto-expiry
- Profile settings
- Notification infrastructure
- In-app notification UI (bell icon)
- Email notifications
- Admin dashboard
- Employment verification (domain checks)
- Advanced search and filtering
- Reporting system
- Rate limiting
- Analytics dashboard
- Mobile app
- Slack/Discord integrations
This is a private project. For bug reports or feature requests, please contact the maintainers.
Proprietary - All rights reserved
Built with ❤️ using Next.js, Prisma, and TypeScript