This document explains all environment variables needed for the bulk email sender application and exactly where to obtain them.
# Copy the example file
cp .env.example .env.local
# Fill in all variables (see below for instructions)# Supabase Configuration
NEXT_PUBLIC_SUPABASE_URL=
NEXT_PUBLIC_SUPABASE_ANON_KEY=
# Gmail API Configuration
GMAIL_CLIENT_ID=
GMAIL_CLIENT_SECRET=
GMAIL_REDIRECT_URI=
GMAIL_REFRESH_TOKEN=
GMAIL_USER=
# PostHog Analytics
NEXT_PUBLIC_POSTHOG_KEY=
NEXT_PUBLIC_POSTHOG_HOST=
# Optional: Database (if using)
DATABASE_URL=
# Optional: App Configuration
NEXT_PUBLIC_APP_URL=What it is: Your Supabase project URL
Required: β
Yes
Example: https://abcdefghijklmnop.supabase.co
- Go to https://supabase.com/dashboard
- Click on your project (or create a new one)
- Click "Settings" in the left sidebar
- Click "API" under Settings
- Find "Project URL" at the top
- Copy the URL
Screenshot Location:
Dashboard β Your Project β Settings β API β Project URL
What it is: Your Supabase anonymous/public API key
Required: β
Yes
Example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
- In the same "Settings β API" page (from above)
- Scroll down to "Project API keys"
- Find "anon public" key
- Click the copy icon
- Paste in your
.env.local
Screenshot Location:
Dashboard β Your Project β Settings β API β Project API keys β anon public
- Go to https://console.cloud.google.com
- Click "Select a project" dropdown at the top
- Click "New Project"
- Enter project name (e.g., "Bulk Email Sender")
- Click "Create"
- Wait for project creation (~30 seconds)
- Select your new project from the dropdown
- In Google Cloud Console, go to "APIs & Services" β "Library"
- Search for "Gmail API"
- Click on "Gmail API" in results
- Click "Enable" button
- Wait for API to be enabled (~10 seconds)
Direct Link: https://console.cloud.google.com/apis/library/gmail.googleapis.com
- Go to "APIs & Services" β "OAuth consent screen"
- Select "Internal" (if you have Google Workspace) or "External"
- Click "Create"
Fill in required fields:
- App name:
Bulk Email Sender(or your app name) - User support email: Your email
- Developer contact email: Your email
- Click "Save and Continue"
Scopes (Step 2):
- Click "Add or Remove Scopes"
- Search for:
https://www.googleapis.com/auth/gmail.send - Check the box next to "Gmail API .../auth/gmail.send"
- Click "Update"
- Click "Save and Continue"
Test Users (if External):
- Click "Add Users"
- Add your Gmail address
- Click "Save and Continue"
Summary:
- Review and click "Back to Dashboard"
- Go to "APIs & Services" β "Credentials"
- Click "Create Credentials" β "OAuth 2.0 Client ID"
- Application type: "Web application"
- Name:
Bulk Email Sender(or your choice)
Authorized redirect URIs:
- Click "Add URI"
- Enter:
http://localhost:3000/api/auth/callback - (For production, add:
https://yourdomain.com/api/auth/callback)
- Click "Create"
- A popup will show your credentials
What it is: OAuth 2.0 Client ID
Required: β
Yes
Example: 123456789-abc123def456.apps.googleusercontent.com
From the credentials creation popup (Step 4):
- Copy "Your Client ID"
- Or go to "APIs & Services" β "Credentials"
- Click on your OAuth 2.0 Client
- Copy "Client ID"
What it is: OAuth 2.0 Client Secret
Required: β
Yes
Example: GOCSPX-abc123def456ghi789jkl
From the same location as Client ID:
- Copy "Your Client Secret"
- Or from the credentials page, copy "Client secret"
- Never commit this to Git
- Store securely in
.env.local - Rotate periodically for security
What it is: The OAuth callback URL
Required: βͺ Optional (auto-detected if not set)
Auto-detection:
- If
NEXT_PUBLIC_APP_URLis set:${NEXT_PUBLIC_APP_URL}/api/auth/callback - Development (NODE_ENV !== 'production'):
http://localhost:3000/api/auth/callback - Production (NODE_ENV === 'production'):
https://mail.devmultigroup.com/api/auth/callback
Option 1: Use NEXT_PUBLIC_APP_URL (Recommended)
# Development
NEXT_PUBLIC_APP_URL=http://localhost:3000
# Production
NEXT_PUBLIC_APP_URL=https://mail.devmultigroup.comThe redirect URI will be automatically constructed as ${NEXT_PUBLIC_APP_URL}/api/auth/callback
Option 2: Set GMAIL_REDIRECT_URI manually
# Development
GMAIL_REDIRECT_URI=http://localhost:3000/api/auth/callback
# Production
GMAIL_REDIRECT_URI=https://mail.devmultigroup.com/api/auth/callbackNote: This must match exactly what you added in "Authorized redirect URIs" in Google Cloud Console.
What it is: OAuth refresh token for Gmail API
Required: β
Yes
Example: 1//0abc123def456ghi789jkl...
This requires a special authentication flow. Follow these steps:
Option A: Using Our Helper Script
- Create a file
scripts/get-refresh-token.js:
const { google } = require('googleapis');
const readline = require('readline');
const oauth2Client = new google.auth.OAuth2(
process.env.GMAIL_CLIENT_ID,
process.env.GMAIL_CLIENT_SECRET,
process.env.GMAIL_REDIRECT_URI
);
const scopes = ['https://www.googleapis.com/auth/gmail.send'];
const authUrl = oauth2Client.generateAuthUrl({
access_type: 'offline',
scope: scopes,
prompt: 'consent',
});
console.log('Authorize this app by visiting this url:', authUrl);
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
rl.question('Enter the code from that page here: ', async (code) => {
rl.close();
try {
const { tokens } = await oauth2Client.getToken(code);
console.log('\nβ
Success! Add this to your .env.local:\n');
console.log(`GMAIL_REFRESH_TOKEN=${tokens.refresh_token}`);
} catch (error) {
console.error('Error retrieving access token', error);
}
});- Run the script:
node scripts/get-refresh-token.js- Open the URL shown in your browser
- Sign in with your Gmail account
- Grant permissions
- Copy the authorization code from the URL
- Paste it into the terminal
- Copy the refresh token to your
.env.local
Option B: Using OAuth2 Playground
- Go to https://developers.google.com/oauthplayground
- Click the gear icon (βοΈ) in top right
- Check "Use your own OAuth credentials"
- Enter your Client ID and Client Secret
- Close settings
In the left panel:
- Find "Gmail API v1"
- Check "https://www.googleapis.com/auth/gmail.send"
- Click "Authorize APIs"
- Sign in with your Gmail account
- Click "Allow"
Exchange authorization code:
- Click "Exchange authorization code for tokens"
- Copy the "Refresh token"
- Paste in your
.env.local
- Refresh token is generated only once
- If you lose it, you need to revoke access and generate a new one
- Store it securely
What it is: Your Gmail/Google Workspace email address
Required: β
Yes
Example: your.email@gmail.com or you@yourcompany.com
Simply use the Gmail address you want to send emails from:
GMAIL_USER=your.email@gmail.comRequirements:
- Must be a Google Workspace account for 2000 recipient limit
- Must be the same account used for OAuth
- Must have permission to send emails
PostHog is an open-source product analytics platform that will help you track:
- User behavior and events
- Email sending metrics
- Feature usage
- Error tracking
- User journeys
- Performance monitoring
What it is: Your PostHog project API key
Required: β
Yes (for analytics)
Example: phc_abc123def456ghi789jklmnopqrstuvwxyz
Step 1: Create PostHog Account
- Go to https://app.posthog.com/signup
- Sign up for a free account (no credit card required)
- Choose "PostHog Cloud" (recommended) or self-hosted
- Complete the onboarding
Step 2: Create Project
- After signup, you'll be in your first project
- Or click "Project" in top left β "Create project"
- Enter project name: "Bulk Email Sender"
- Click "Create project"
Step 3: Get Project API Key
- Click "Project settings" (gear icon) in left sidebar
- Or go to: Settings β Project
- Find "Project API Key" section
- Copy the key (starts with
phc_) - Paste in your
.env.local
Screenshot Location:
Dashboard β Settings β Project β Project API Key
Direct Link: https://app.posthog.com/settings/project
What it is: PostHog API endpoint URL
Required: β
Yes
Default for PostHog Cloud: https://app.posthog.com or https://us.posthog.com
For PostHog Cloud (US):
NEXT_PUBLIC_POSTHOG_HOST=https://us.posthog.comFor PostHog Cloud (EU):
NEXT_PUBLIC_POSTHOG_HOST=https://eu.posthog.comFor Self-Hosted:
NEXT_PUBLIC_POSTHOG_HOST=https://your-posthog-instance.comHow to find your host:
- Go to PostHog Settings β Project
- Find "Host" in the snippet code
- Copy the URL
Default: Use https://us.posthog.com if you signed up for PostHog Cloud US
What it is: PostgreSQL connection string (if using Supabase database features)
Required: βͺ Optional
Example: postgresql://postgres:password@db.project.supabase.co:5432/postgres
- Go to Supabase Dashboard
- Click "Settings" β "Database"
- Scroll to "Connection string"
- Select "URI" tab
- Copy the connection string
- Replace
[YOUR-PASSWORD]with your database password
When you need this:
- If storing campaign history in database
- If using Row Level Security features
- If building analytics dashboard
What it is: Your application's URL
Required: βͺ Optional (but recommended)
Example:
- Development:
http://localhost:3000 - Production:
https://mail.devmultigroup.com
# Development
NEXT_PUBLIC_APP_URL=http://localhost:3000
# Production
NEXT_PUBLIC_APP_URL=https://mail.devmultigroup.comUsed for:
- Generating absolute URLs
- Auto-constructing GMAIL_REDIRECT_URI:
${NEXT_PUBLIC_APP_URL}/api/auth/callback - Email links
- Other absolute URL needs
Note: If set, GMAIL_REDIRECT_URI will be automatically constructed from this value.
# ======================
# Supabase Configuration
# ======================
NEXT_PUBLIC_SUPABASE_URL=https://abcdefghijklmnop.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImFiY2RlZmdoaWprbG1ub3AiLCJyb2xlIjoiYW5vbiIsImlhdCI6MTYyMzA4NTQ4NCwiZXhwIjoxOTM4NjYxNDg0fQ.abc123def456
# ============================
# Gmail API Configuration
# ============================
GMAIL_CLIENT_ID=123456789012-abc123def456ghi789jkl.apps.googleusercontent.com
GMAIL_CLIENT_SECRET=GOCSPX-abc123def456ghi789
GMAIL_REDIRECT_URI=http://localhost:3000/api/auth/callback
GMAIL_REFRESH_TOKEN=1//0abc123def456ghi789jkl_mnopqrs-tuv-wxyzABCDEF
GMAIL_USER=your.email@gmail.com
# ============================
# PostHog Analytics
# ============================
NEXT_PUBLIC_POSTHOG_KEY=phc_abc123def456ghi789jklmnopqrstuvwxyz
NEXT_PUBLIC_POSTHOG_HOST=https://us.posthog.com
# ============================
# Optional Configuration
# ============================
DATABASE_URL=postgresql://postgres:your-password@db.abcdefghijklmnop.supabase.co:5432/postgres
NEXT_PUBLIC_APP_URL=http://localhost:3000Before running your application, verify:
- Project created and active
- URL copied correctly
- Anon key copied correctly
- Email authentication enabled
- Project created
- Gmail API enabled
- OAuth consent screen configured
- OAuth credentials created
- Redirect URI matches exactly
- Account created
- Project created
- API key copied correctly
- Host URL set correctly
- All required variables set
- No extra spaces in values
- Quotes removed from values
- File saved as
.env.local(not.env.local.txt) - File in project root directory
- Using Google Workspace account (for 2K limit)
- Account has sending permissions
- Refresh token generated successfully
- Test email sent successfully
Create a test file scripts/test-supabase.js:
const { createClient } = require('@supabase/supabase-js');
require('dotenv').config({ path: '.env.local' });
const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY
);
async function test() {
const { data, error } = await supabase.auth.getSession();
if (error) {
console.error('β Supabase connection failed:', error.message);
} else {
console.log('β
Supabase connection successful!');
}
}
test();Run: node scripts/test-supabase.js
Create a test file scripts/test-gmail.js:
const { google } = require('googleapis');
require('dotenv').config({ path: '.env.local' });
async function test() {
try {
const oauth2Client = new google.auth.OAuth2(
process.env.GMAIL_CLIENT_ID,
process.env.GMAIL_CLIENT_SECRET,
process.env.GMAIL_REDIRECT_URI
);
oauth2Client.setCredentials({
refresh_token: process.env.GMAIL_REFRESH_TOKEN,
});
const gmail = google.gmail({ version: 'v1', auth: oauth2Client });
// Test: Get user profile
const profile = await gmail.users.getProfile({ userId: 'me' });
console.log('β
Gmail API connection successful!');
console.log(`π§ Connected as: ${profile.data.emailAddress}`);
console.log(`π Total messages: ${profile.data.messagesTotal}`);
} catch (error) {
console.error('β Gmail API connection failed:', error.message);
}
}
test();Run: node scripts/test-gmail.js
Create scripts/send-test-email.js:
const { google } = require('googleapis');
require('dotenv').config({ path: '.env.local' });
async function sendTestEmail() {
const oauth2Client = new google.auth.OAuth2(
process.env.GMAIL_CLIENT_ID,
process.env.GMAIL_CLIENT_SECRET,
process.env.GMAIL_REDIRECT_URI
);
oauth2Client.setCredentials({
refresh_token: process.env.GMAIL_REFRESH_TOKEN,
});
const gmail = google.gmail({ version: 'v1', auth: oauth2Client });
const message = [
`From: ${process.env.GMAIL_USER}`,
`To: ${process.env.GMAIL_USER}`,
'Subject: Test Email from Bulk Sender',
'',
'This is a test email. If you receive this, your setup is working!',
].join('\n');
const encodedMessage = Buffer.from(message)
.toString('base64')
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '');
try {
await gmail.users.messages.send({
userId: 'me',
requestBody: {
raw: encodedMessage,
},
});
console.log('β
Test email sent successfully!');
console.log(`π§ Check your inbox: ${process.env.GMAIL_USER}`);
} catch (error) {
console.error('β Failed to send test email:', error.message);
}
}
sendTestEmail();Run: node scripts/send-test-email.js
Create scripts/test-posthog.js:
const { PostHog } = require('posthog-node');
require('dotenv').config({ path: '.env.local' });
async function test() {
try {
const client = new PostHog(
process.env.NEXT_PUBLIC_POSTHOG_KEY,
{ host: process.env.NEXT_PUBLIC_POSTHOG_HOST }
);
// Send a test event
client.capture({
distinctId: 'test-user',
event: 'test_event',
properties: {
test: true,
timestamp: new Date().toISOString(),
},
});
await client.shutdown();
console.log('β
PostHog connection successful!');
console.log('π Check your PostHog dashboard for the "test_event"');
console.log(`π Dashboard: ${process.env.NEXT_PUBLIC_POSTHOG_HOST}/project/events`);
} catch (error) {
console.error('β PostHog connection failed:', error.message);
}
}
test();Run: node scripts/test-posthog.js
Then check your PostHog dashboard to see if the test event appears.
Problem: Client ID or Client Secret is wrong
Solution:
- Go back to Google Cloud Console
- Verify credentials in "APIs & Services" β "Credentials"
- Copy them again carefully
- Remove any extra spaces
Problem: GMAIL_REDIRECT_URI doesn't match Google Cloud settings
Solution:
- Check Google Cloud Console β Credentials β Your OAuth Client
- Ensure redirect URI is exactly:
http://localhost:3000/api/auth/callback - No trailing slash
- Correct protocol (http vs https)
Problem: Refresh token is incorrect or expired
Solution:
- Revoke access in Google Account settings
- Generate a new refresh token
- Use the helper script or OAuth Playground again
Problem: Incorrect URL or anon key
Solution:
- Double-check values in Supabase Dashboard
- Ensure no extra characters or spaces
- Try recreating the Supabase client
Problem: API not enabled in Google Cloud
Solution:
- Go to Google Cloud Console
- "APIs & Services" β "Library"
- Search "Gmail API"
- Click "Enable"
- Wait a few minutes for propagation
Problem: Events not showing in PostHog dashboard
Solution:
- Verify
NEXT_PUBLIC_POSTHOG_KEYis correct - Check
NEXT_PUBLIC_POSTHOG_HOSTmatches your region - Wait 1-2 minutes for events to process
- Check PostHog project is active
- Verify no ad blockers are blocking PostHog
Problem: PostHog key is incorrect
Solution:
- Go to PostHog Dashboard β Settings β Project
- Copy the Project API Key (starts with
phc_) - Ensure no extra spaces or characters
- Restart your development server
- Store
.env.localfile locally only - Add
.env.localto.gitignore - Rotate credentials every 6 months
- Use environment variables in Vercel for production
- Enable 2FA on Google and Supabase accounts
- Limit OAuth scopes to minimum needed
- Commit
.env.localto Git - Share credentials in Slack/email
- Use the same credentials for multiple apps
- Hardcode credentials in source code
- Leave test credentials in production
- Share your refresh token
When deploying to Vercel:
- Go to your Vercel project dashboard
- Click "Settings" β "Environment Variables"
- Add each variable:
- Name:
NEXT_PUBLIC_SUPABASE_URL - Value: Your value
- Environment: Production (and Preview if needed)
- Name:
- Click "Save"
- Repeat for all variables
- Redeploy your application
Production-specific notes:
- Update
GMAIL_REDIRECT_URIto production URL - Add production redirect URI to Google Cloud Console
- Use secure, rotated credentials
- Never use development credentials in production
| Variable | Where to Get It | Required |
|---|---|---|
NEXT_PUBLIC_SUPABASE_URL |
Supabase Dashboard β Settings β API | β |
NEXT_PUBLIC_SUPABASE_ANON_KEY |
Supabase Dashboard β Settings β API | β |
GMAIL_CLIENT_ID |
Google Cloud β APIs & Services β Credentials | β |
GMAIL_CLIENT_SECRET |
Google Cloud β APIs & Services β Credentials | β |
GMAIL_REDIRECT_URI |
Manual entry (callback URL) | β |
GMAIL_REFRESH_TOKEN |
OAuth Playground or helper script | β |
GMAIL_USER |
Your Gmail address | β |
NEXT_PUBLIC_POSTHOG_KEY |
PostHog Dashboard β Settings β Project | β |
NEXT_PUBLIC_POSTHOG_HOST |
PostHog Dashboard (usually https://us.posthog.com) | β |
DATABASE_URL |
Supabase Dashboard β Settings β Database | βͺ |
NEXT_PUBLIC_APP_URL |
Your app URL | βͺ |
- Supabase: https://supabase.com/docs
- Gmail API: https://developers.google.com/gmail/api
- PostHog: https://posthog.com/docs
- Next.js: https://nextjs.org/docs
- Vercel: https://vercel.com/docs
- Check the Troubleshooting section above
- Review error messages carefully
- Verify all values are correct
- Try test scripts to isolate the issue
- GitHub Issues: [Your repo]/issues
- Email: support@yourapp.com
- Documentation: [Your docs URL]
Last Updated: January 2026
Version: 1.0.0