Back to Home
More Features
Email system, background jobs with Inngest, admin dashboard, and database schema.
Additional built-in features including transactional email, background jobs, admin dashboard, and the full database schema.
Email System
Transactional emails via Resend. All emails use dynamic brand configuration from brand.ts.
Available Templates
- sendWelcomeEmail() — Triggered on user sign up (onboarding email)
- sendInvoiceEmail() — Triggered on payment received (receipt with PDF attachment)
- sendInviteEmail() — Triggered on team invitation (organization invite with token)
- sendOrganizationWelcomeEmail() — Triggered on org created (confirmation to founder)
- sendSubscriptionActivatedEmail() — Triggered on sub starts (plan details + next billing)
- sendSubscriptionChargedEmail() — Triggered on recurring charge (receipt + next billing)
- sendSubscriptionCancelledEmail() — Triggered on sub cancelled (end date + resubscribe link)
- sendPlanChangedEmail() — Triggered on plan upgrade/downgrade (old plan → new plan details)
Email Configuration
Email sender addresses are configured in brand.email:
TypeScript
brand.email.fromSupport // Support emails
brand.email.fromBilling // Billing/payment emails
brand.email.fromNoReply // Automated notifications
brand.email.replyTo // Reply-to addressBackground Jobs (Inngest)
Async job queue for event-driven tasks.
Event Types
TypeScript
'user.signed-up' // New user registration
'license.purchased' // License purchase
'organization.created' // Org creation
'organization.member-invited' // Team invitation
'invoice.generated' // Invoice ready
'subscription.charged' // Recurring payment
'subscription.cancelled' // Subscription ended
'credits.low-balance' // Credits below threshold
'credits.purchased' // Credit pack boughtSending Events
TypeScript
import { inngest } from '@/lib/inngest';
await inngest.send({
name: 'user.signed-up',
data: { userId: user.id, email: user.email, name: 'John Doe' },
});Job handlers are defined in src/lib/inngest/functions/ and automatically discovered.
Admin Dashboard
Superadmin control panel at /admin. Requires is_super_admin: true on the user's profile.
Admin Pages
- /admin — Overview with key metrics
- /admin/users — User management and browsing
- /admin/subscriptions — Subscription management with filters
- /admin/contacts — Support form submissions
- /admin/analytics — Revenue and signup trends
- /admin/waitlist — Waitlist management
Overview Metrics
- Total users
- Active subscriptions
- Monthly revenue
- Unread contact submissions
- Waitlist pending
- Organizations count (if multi-tenancy)
- Credits granted this month (if credits)
- Recent signups and subscriptions
Database Schema
Core Tables
- profiles — User profiles (extends auth.users) — Always present
- plans — Subscription plan definitions — Always present
- subscriptions — Active subscriptions — Always present
- licenses — License tracking — Always present
- invoices — Invoice records — Always present
- audit_logs — Audit trail — Always present
- webhook_events — Webhook event log — Always present
- admin_activity_log — Admin actions — Always present
- contact_submissions — Contact form data — Always present
- waitlist — Waitlist entries — Always present
Optional Tables
- organizations — Feature: multiTenancy — Organization records
- organization_members — Feature: multiTenancy — User-org membership with roles
- organization_invites — Feature: multiTenancy — Pending invitations
- credit_balances — Feature: credits — Current balance per tenant
- credit_transactions — Feature: credits — Full audit log
Row Level Security
All tables have RLS enabled. Policies ensure:
- Users can only access their own data
- In multi-tenant mode, access is checked via organization_members
- Admin operations use supabaseAdmin (service role) to bypass RLS
Migrations
Located in supabase/migrations/:
- core/001 through core/019 — Core schema (profiles, plans, subscriptions, credits, admin, storage, payment abstraction)
- optional/multi-tenancy/001-003 — Organization tables
Authentication
Built on Supabase Auth with Row Level Security.
Server-Side Auth
TypeScript
import { createClient } from '@/lib/supabase/supabase-server';
const supabase = await createClient();
const { data: { user } } = await supabase.auth.getUser();Client-Side Auth
TypeScript
import { createClient } from '@/lib/supabase/supabase-browser';
const supabase = createClient();
const { data: { user } } = await supabase.auth.getUser();Supported Auth Methods
- Email/password
- Magic link
- OAuth providers (Google, GitHub, etc.)
- Password reset flow
Protected Routes
All /dashboard/* routes require authentication. The auth middleware redirects unauthenticated users to /login.