Slotify is a scheduling and booking platform inspired by modern calendar products.
Built with Next.js 16 App Router, React 19, Prisma 7.5, PostgreSQL, Better Auth, Razorpay, and Resend.
- Public scheduling pages with host usernames and event types
- Timezone-aware availability computation with UTC storage
- Booking lifecycle: create, cancel, and reschedule-safe flows
- Google Calendar integration with Google Meet link generation
- Subscription billing with Razorpay orders, verification, and webhook processing
- Booking notifications with Resend and delivery-status webhook sync
- SEO baseline: metadata, structured data, robots, sitemap, and manifest
- Framework: Next.js 16, React 19, TypeScript
- Styling: Tailwind CSS v4, Radix/shadcn UI
- ORM/DB: Prisma 7.5 + PostgreSQL
- Auth: Better Auth (Google OAuth)
- Payments: Razorpay
- Email: Resend
- Runtime package manager: Bun
- Install dependencies.
bun install- Create your env file.
# Windows (PowerShell)
Copy-Item .env.example .env
# macOS/Linux
cp .env.example .env-
Fill all required environment variables from the table below.
-
Generate Prisma client and run migrations.
bun run db:generate
bun run db:migrate- Start the development server.
bun run devbun run dev- Start local development serverbun run build- Build production bundlebun run start- Start production serverbun run lint- Run ESLintbun run db:generate- Generate Prisma clientbun run db:migrate- Create/apply Prisma migrations in developmentbun run db:push- Push schema directly to database (no migration files)bun run db:studio- Open Prisma Studio
The app validates core env vars via lib/env.ts. Some integrations are optional at boot and enforced when their features are used.
| Variable | Required | Used By | Notes |
|---|---|---|---|
DATABASE_URL |
Yes | Prisma adapter, migrations | PostgreSQL connection string |
GOOGLE_CLIENT_ID |
Yes | Better Auth, Google token refresh | Google OAuth client ID |
GOOGLE_CLIENT_SECRET |
Yes | Better Auth, Google token refresh | Google OAuth client secret |
BETTER_AUTH_SECRET |
Yes | Better Auth runtime | Strong random secret, min 32 chars recommended |
BETTER_AUTH_URL |
Yes | Better Auth runtime | App base URL, example: http://localhost:3000 |
NEXT_PUBLIC_APP_URL |
Recommended | SEO metadata, robots, sitemap | Public canonical base URL |
RAZORPAY_KEY_ID |
Required for payments | Razorpay order creation | Needed when using subscription checkout |
RAZORPAY_KEY_SECRET |
Required for payments | Razorpay signature verification | Used for verify endpoint and SDK |
RAZORPAY_WEBHOOK_SECRET |
Required for Razorpay webhook | Razorpay webhook signature validation | Must match provider webhook secret |
RESEND_API_KEY |
Required for booking email | Resend email client | Needed for notification sends |
RESEND_FROM_EMAIL |
Required for booking email | Sender identity | Example: Slotify <noreply@yourdomain.com> |
RESEND_WEBHOOK_SECRET |
Required for Resend webhook | Resend webhook verification | Must match provider webhook secret |
- Endpoint:
POST /api/subscription/razorpay/webhook - File:
app/api/subscription/razorpay/webhook/route.ts - Signature header:
x-razorpay-signature - Secret used:
RAZORPAY_WEBHOOK_SECRET - Events handled:
payment.capturedorder.paidpayment.failed
- Behavior:
- Validates signature against raw body
- Uses transaction +
SELECT ... FOR UPDATElock onSubscriptionOrder - Idempotent processing for already paid orders
- Activates user subscription and records coupon redemption safely
- Endpoint:
POST /api/webhooks/resend - File:
app/api/webhooks/resend/route.ts - Required headers:
svix-idsvix-timestampsvix-signature
- Secret used:
RESEND_WEBHOOK_SECRET - Events handled:
email.sentemail.deliveredemail.failedemail.bouncedemail.complainedemail.suppressed
- Behavior:
- Verifies webhook signature
- Resolves notification log by
notification_log_idtag - Updates
NotificationLogstatus and failure metadata
- Create webhook pointing to
https://<your-domain>/api/subscription/razorpay/webhook. - Subscribe to the payment events listed above.
- Copy webhook secret into
RAZORPAY_WEBHOOK_SECRET.
- Create webhook pointing to
https://<your-domain>/api/webhooks/resend. - Enable email lifecycle events listed above.
- Copy webhook secret into
RESEND_WEBHOOK_SECRET.
| Method | Route | Purpose |
|---|---|---|
GET/POST |
/api/auth/[...all] |
Better Auth handlers |
GET |
/api/availability |
Availability slots for host/event type/date window |
GET/POST |
/api/bookings |
Host bookings list and booking creation |
PATCH/DELETE |
/api/bookings/[bookingId] |
Update/cancel booking |
POST |
/api/bookings/public/[bookingId]/cancel |
Public cancel flow |
GET/POST |
/api/event-types |
List/create event types |
GET/PATCH/DELETE |
/api/event-types/[slug] |
Event type by slug |
POST |
/api/onboarding/complete |
Finish onboarding |
GET |
/api/pricing |
Public plans and active coupons |
POST |
/api/subscription/razorpay/order |
Create Razorpay order |
POST |
/api/subscription/razorpay/verify |
Verify payment signature |
POST |
/api/subscription/razorpay/webhook |
Razorpay webhook |
POST |
/api/webhooks/resend |
Resend webhook |
- Global metadata:
app/layout.tsx - Home metadata + JSON-LD:
app/page.tsx - Robots:
app/robots.ts - Sitemap:
app/sitemap.ts - Manifest:
app/manifest.ts
Make sure NEXT_PUBLIC_APP_URL points to your production domain for correct canonical links and sitemap host.
- Time handling:
- Persist all date-time values in UTC.
- Compute availability in host timezone, render in attendee timezone.
- Concurrency:
- Booking creation relies on DB-level uniqueness and conflict handling.
- Payment webhooks and payment verification use transaction row locks for idempotency.
- Integrations:
- Google account reconnection flows are handled when token refresh fails.
- Email sending degrades gracefully when Resend env vars are missing.
- Set all required env vars in deployment platform.
- Configure Google OAuth redirect URIs and consent screen.
- Configure Razorpay webhook and verify signature behavior.
- Configure Resend domain, sender, and webhook.
- Run migrations in production database.
- Set
NEXT_PUBLIC_APP_URLto the live HTTPS domain. - Verify
/robots.txt,/sitemap.xml, and/manifest.webmanifest.
