EchoMail is a powerful, modern email marketing platform that seamlessly integrates with Gmail API to send personalized bulk emails. Built with Next.js 15 and TypeScript, it offers a professional-grade solution for businesses and individuals looking to manage email campaigns with Gmail-like formatting and reliability.
- Native Gmail API integration for authentic email delivery
- OAuth 2.0 authentication with Google
- Sends emails directly through your Gmail account
- Maintains Gmail's deliverability and reputation
- Automatic token refresh for uninterrupted sending
- Advanced rich text editor with TipTap
- Gmail-style formatting and spacing
- Real-time email preview with Gmail appearance
- Support for formatting: bold, italic, underline, lists, links, images
- Text alignment and heading styles
- Email signatures support
- CSV file upload for bulk recipients
- Manual contact entry with validation
- Contact groups for organized recipient management
- Appwrite-powered contact management and file storage
- Placeholder personalization ({{name}}, {{email}}, {{company}}, and custom fields)
- Duplicate contact detection and merging
- Draft auto-save functionality
- Real-time sending progress tracking
- Email open and click tracking
- Campaign history and analytics dashboard
- A/B testing for subject lines and content
- Performance comparison between variants
- Create and manage teams
- Invite members with role-based permissions (Owner, Admin, Member, Viewer)
- Team settings for shared resources
- Collaborative campaign management
- Secure OAuth 2.0 authentication
- No email credentials stored
- End-to-end encrypted API communications
- GDPR Compliance Tools:
- Data export (download all your data as JSON)
- Data deletion (right to be forgotten)
- Consent management (marketing, analytics, data processing)
- Privacy settings dashboard
- Audit Logs:
- Track all account activities
- Filterable log viewer with date ranges
- IP address and user agent tracking
- Save and reuse email templates
- Create multiple email signatures
- Set default signature for all emails
- Quick template insertion while composing
- Configure webhook notifications for email events
- Real-time event callbacks (sent, opened, clicked, bounced)
- Custom webhook endpoints with secret verification
Ctrl+/- Show keyboard shortcuts helpCtrl+N- New compose emailEscape- Close dialogs- Page-specific shortcuts for enhanced productivity
- Responsive design for all devices
- Dark/Light theme support
- Clean, intuitive interface
- Real-time feedback and error handling
- Professional email previews
- Frontend: Next.js 15, React 19, TypeScript
- Styling: Tailwind CSS, shadcn/ui components, Radix UI
- Authentication: NextAuth.js with Google OAuth
- Database & Storage: Appwrite (Cloud or Self-hosted)
- Email: Gmail API with automatic token refresh
- Rich Text: TipTap editor
- Icons: Lucide React
- Date Handling: date-fns
- Notifications: Sonner toast
- Testing: Vitest, Playwright
- Deployment: Vercel
Before you begin, ensure you have:
- Node.js 20+ installed
- Gmail account
- Google Cloud Project with Gmail API enabled
- Appwrite project set up (Cloud or Self-hosted)
git clone https://github.com/Aditya190803/echomail.git
cd echomailnpm installCreate a .env.local file in the root directory:
# NextAuth Configuration
NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET=your-secret-key-here
# Google OAuth (Gmail API)
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret
# Appwrite Configuration
NEXT_PUBLIC_APPWRITE_ENDPOINT=https://cloud.appwrite.io/v1
NEXT_PUBLIC_APPWRITE_PROJECT_ID=your-project-id
NEXT_PUBLIC_APPWRITE_DATABASE_ID=your-database-id
APPWRITE_API_KEY=your-appwrite-api-key
# Appwrite Collections
NEXT_PUBLIC_APPWRITE_CONTACTS_COLLECTION_ID=contacts
NEXT_PUBLIC_APPWRITE_CAMPAIGNS_COLLECTION_ID=campaigns
NEXT_PUBLIC_APPWRITE_TEMPLATES_COLLECTION_ID=templates
NEXT_PUBLIC_APPWRITE_CONTACT_GROUPS_COLLECTION_ID=contact_groups
NEXT_PUBLIC_APPWRITE_DRAFT_EMAILS_COLLECTION_ID=draft_emails
NEXT_PUBLIC_APPWRITE_SIGNATURES_COLLECTION_ID=signatures
NEXT_PUBLIC_APPWRITE_UNSUBSCRIBES_COLLECTION_ID=unsubscribes
NEXT_PUBLIC_APPWRITE_WEBHOOKS_COLLECTION_ID=webhooks
NEXT_PUBLIC_APPWRITE_TRACKING_EVENTS_COLLECTION_ID=tracking_events
NEXT_PUBLIC_APPWRITE_AB_TESTS_COLLECTION_ID=ab_tests
NEXT_PUBLIC_APPWRITE_ATTACHMENTS_BUCKET_ID=attachments
# Teams & Organization
NEXT_PUBLIC_APPWRITE_TEAMS_COLLECTION_ID=teams
NEXT_PUBLIC_APPWRITE_TEAM_MEMBERS_COLLECTION_ID=team_members
# GDPR & Compliance
NEXT_PUBLIC_APPWRITE_AUDIT_LOGS_COLLECTION_ID=audit_logs
NEXT_PUBLIC_APPWRITE_CONSENTS_COLLECTION_ID=consents
# Upstash Redis (for caching - optional but recommended)
# Get these from https://console.upstash.com/
UPSTASH_REDIS_REST_URL=https://your-region.upstash.io
UPSTASH_REDIS_REST_TOKEN=your-upstash-token- Go to Google Cloud Console
- Create a new project or select existing one
- Enable the Gmail API
- Create OAuth 2.0 credentials
- Add authorized redirect URIs:
http://localhost:3000/api/auth/callback/google(development)https://echomail.adityamer.live/api/auth/callback/google(production)
- Create an Appwrite project at Appwrite Console or self-host
- Create a Database
- Run the setup script to create all collections:
npx tsx scripts/setup-appwrite.ts
- Create a Storage Bucket for email attachments (or let the script create it)
- Generate an API Key with appropriate permissions
- Copy the generated environment variables to your
.env.local
The setup script creates all required collections:
- Core: contacts, campaigns, templates, contact_groups, draft_emails
- Features: signatures, unsubscribes, webhooks, tracking_events, ab_tests
- Teams: teams, team_members
- Compliance: audit_logs, consents
npm run devOpen http://localhost:3000 to see your application.
- Sign in with your Google account
- Grant Gmail API permissions
- Your credentials are securely managed by NextAuth.js
- Navigate to
/compose - Enter subject line and compose your message
- Use the rich text editor for formatting
- Add personalization placeholders:
{{name}},{{email}},{{company}}
- CSV Upload: Upload a CSV file with email, name, and other fields
- Manual Entry: Add individual recipients manually
- Contacts: Select from saved contacts
- Review your email with Gmail-style preview
- Check recipient list and personalization
- Send emails with real-time progress tracking
- Monitor campaign performance at
/analytics - View delivery status and engagement metrics
- Create A/B tests at
/ab-testing - Compare subject line and content variants
- Create teams at
/settings/teams - Invite members with specific roles
- Collaborate on campaigns
- Manage privacy settings at
/settings/gdpr - Export or delete your data
- View activity history at
/settings/audit-logs
EchoMail includes automatic token refresh for long-running campaigns:
- Monitors token expiry during bulk email sends
- Automatically refreshes tokens every 10 emails
- Prompts for re-authentication when tokens expire
- Prevents campaign interruption due to expired credentials
The compose form automatically saves drafts:
- Saves to localStorage every 30 seconds
- Persists subject, body, and recipient list
- Prompts to restore draft on page load
- Clear draft option available
| Issue | Quick Solution |
|---|---|
| App won't start | rm -rf node_modules && npm install |
| Auth not working | Clear cookies, sign out/in |
| Emails not sending | Check Gmail API quota |
| Database errors | Run npm run appwrite:setup |
| Build fails | npm run lint then fix errors |
Problem: "Invalid_grant" or token refresh fails
Error: invalid_grant
Solution:
- Clear browser cookies and localStorage
- Sign out and sign back in
- Verify your Google Cloud OAuth consent screen is properly configured
- Check that your OAuth credentials aren't expired
- Ensure your OAuth app is not in "Testing" mode with expired test users
Problem: Redirect URI mismatch Solution:
- Ensure redirect URIs in Google Cloud Console match exactly:
- Development:
http://localhost:3000/api/auth/callback/google - Production:
https://echomail.adityamer.live/api/auth/callback/google
- Development:
- Note: URIs are case-sensitive and must not have trailing slashes
Problem: "Access blocked: This app's request is invalid" Solution:
- Go to Google Cloud Console β APIs & Services β OAuth consent screen
- Add your email to test users (if app is in Testing mode)
- Or publish your app for production use
Problem: Emails not sending / rate limited Solution:
- Gmail API has daily limits (500 emails/day for free accounts, 2000 for Google Workspace)
- Add delays between bulk sends (EchoMail adds 1 second automatically)
- Check the sending progress in
/dashboard - Wait 24 hours if quota exceeded
Problem: "Quota exceeded for quota metric 'Messages sent'" Solution:
- You've hit Gmail's daily sending limit
- Wait until the next day (Pacific Time) for quota reset
- Consider using Google Workspace for higher limits
Problem: MJML compilation errors Solution:
- MJML is server-side only; ensure you're not importing it in client components
- Run
npm installto ensure mjml dependencies are installed - Check for syntax errors in your email HTML template
Problem: Attachments not sending Solution:
- Check total attachment size (Gmail limit: 25MB)
- Ensure files are properly uploaded to Appwrite storage
- Verify file URLs are accessible
Problem: Appwrite connection fails Solution:
- Verify all Appwrite environment variables are set correctly
- Run
npm run appwrite:setupto create collections - Check that your Appwrite API key has proper permissions
- Ensure the Appwrite endpoint is accessible
- Check Appwrite project status in the dashboard
Problem: Collection not found errors Solution:
- Run the setup script:
npm run appwrite:setup - Verify collection IDs match in
.env.local - Check if collections were deleted in Appwrite console
Problem: "401 Unauthorized" from Appwrite Solution:
- Regenerate your Appwrite API key
- Ensure API key has all required scopes (documents.read, documents.write, files.read, files.write)
- Check if the API key has expired
Problem: Build fails with type errors
npm run lint # Check for linting issues
npm run build # Attempt production buildProblem: "Module not found" errors Solution:
rm -rf node_modules .next
npm install
npm run devProblem: Large bundle size
npm run build:analyze # Opens bundle analyzerProblem: CORS errors in development Solution:
- Ensure you're running on
http://localhost:3000 - Check
next.config.mjsheaders configuration - For API routes, verify CORS headers are set
Problem: Hydration mismatch errors Solution:
- Check for browser-only code running on server
- Use
useEffectfor client-only operations - Wrap client components with proper boundaries
Problem: App is slow / high memory usage Solution:
- Clear browser cache and localStorage
- Check for memory leaks in dev tools
- Reduce number of recipients per batch
- Close unused browser tabs
Problem: Email preview not loading Solution:
- Check browser console for errors
- Verify API route
/api/format-email-previewis working - Clear cache and reload
Problem: Environment variables not loading Solution:
- Ensure
.env.localfile exists in project root - Restart the development server after changes
- Verify variable names match exactly (case-sensitive)
- Check for invisible characters or extra spaces
Problem: Different behavior in production vs development Solution:
- Verify production environment variables are set in Vercel/hosting platform
- Check for
process.env.NODE_ENVdependent code - Review build logs for warnings
Enable debug logging by adding to .env.local:
DEBUG=trueFor verbose API logging:
LOG_LEVEL=debug- Browser Console: Client-side errors and warnings
- Terminal: Server-side logs during development
- Vercel Logs: Production server logs
- Appwrite Console: Database and storage operations
- Check the API documentation
- Review the Developer Guide
- Search existing GitHub issues
- Create a new issue with:
- Steps to reproduce
- Expected vs actual behavior
- Environment details (OS, Node version, etc.)
- Error messages and stack traces
- Browser and version (for frontend issues)
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request