How to Add Email Subscribers to MailGun List Using PHP Form

Based on your description, this is a classic Next.js environment variable scoping issue. The problem occurs because you’re likely trying to access MAILGUN_API_KEY in a client-side context, but Next.js environment variables have specific rules about where they can be accessed.

Understanding Next.js Environment Variable Rules

Next.js has two types of environment variables:

  1. Server-side only (default): Variables in .env.local without the NEXT_PUBLIC_ prefix
  2. Client-side accessible: Variables prefixed with NEXT_PUBLIC_

Since MAILGUN_API_KEY contains sensitive credentials, it should never be prefixed with NEXT_PUBLIC_ as this would expose it to the browser.

The Solution: Move Email Logic to API Routes

The best practice is to create a Next.js API route to handle email sending on the server-side where your environment variables are accessible:

Step 1: Create an API Route

Create /pages/api/send-email.ts (or /app/api/send-email/route.ts if using App Router):

typescript
// pages/api/send-email.ts
import { NextApiRequest, NextApiResponse } from ‘next’;
import EmailClient from “mailgun.js”;
import FormData from “form-data”;

const emailClient = new EmailClient(FormData);

const mailService = emailClient.client({
username: “api”,
key: process.env.MAILGUN_API_KEY!, // This works on server-side
url: “https://api.eu.mailgun.net/”,
});

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method !== ‘POST’) {
return res.status(405).json({ message: ‘Method not allowed’ });
}

const { recipient, title, content, htmlContent } = req.body;

try {
const emailData = {
from: “[email protected]”,
to: [recipient],
subject: title,
text: content,
html: htmlContent,
};

const result = await mailService.messages.create("mydomain.com", emailData);

res.status(200).json({ success: true, messageId: result.id });

} catch (error) {
console.error(‘Email sending failed:’, error);
res.status(500).json({ success: false, error: ‘Failed to send email’ });
}
}

Step 2: Update Your Client-Side Code

Now call your API route instead of directly using the email service:

typescript
// In your component or client-side code
const sendEmail = async (emailData: {
recipient: string;
title: string;
content?: string;
htmlContent?: string;
}) => {
const response = await fetch(‘/api/send-email’, {
method: ‘POST’,
headers: {
‘Content-Type’: ‘application/json’,
},
body: JSON.stringify(emailData),
});

if (!response.ok) {
throw new Error(‘Failed to send email’);
}

return response.json();
};

// Usage
await sendEmail({
recipient: userEmail,
title: “Account Created”,
content: “Thanks for joining us!”,
});

Additional Troubleshooting Steps

1. Verify Your Environment File

Ensure your .env.local file is in the root of your project and formatted correctly:

bash
MAILGUN_API_KEY=your-actual-api-key-here

No spaces around the equals sign, no quotes unless they’re part of the actual key.

2. Restart Your Development Server

After adding new environment variables, always restart your Next.js development server:

bash
npm run dev

or

yarn dev

3. Check Environment Variable Loading

Add a temporary server-side console log to verify the variable is loaded:

typescript
// In your API route
console.log(‘Mailgun API Key loaded:’, process.env.MAILGUN_API_KEY ? ‘YES’ : ‘NO’);

Why This Approach Works

  • Security: Your API key stays on the server and never gets exposed to the client
  • Reliability: Server-side environment variables are always accessible in API routes
  • Scalability: You can add authentication, rate limiting, and other server-side logic
  • Best Practice: This follows Next.js recommended patterns for handling sensitive operations

Important Note: If you’re deploying to production, make sure to add your environment variables to your hosting platform’s environment configuration (Vercel, Netlify, etc.).

This solution will resolve your 401 error and set you up with a robust, secure email sending system! :rocket:

The 401 Unauthorized error you’re encountering is a common authentication issue with Mailgun’s API. The problem is that you’re using your email and password for authentication, but Mailgun’s API requires an API key instead.

The Solution: Use Your API Key

Here’s what you need to do:

  1. Log into your Mailgun dashboard
  2. Navigate to Settings → API Keys
  3. Copy your Private API Key (it starts with key-)
  4. Replace your email/password with the API key

Correct Authentication Format

Your curl command should look like this:

bash
curl -i -X GET -u api:YOUR_PRIVATE_API_KEY ‘https://api.mailgun.net/v3/mail.example.org/events

Key points to remember:

  • The username is literally api (not your email)
  • The password is your Private API Key from the dashboard
  • Make sure you’re using the correct domain name in the URL

Alternative Authentication Methods

You can also authenticate using headers instead of basic auth:

bash
curl -i -X GET -H “Authorization: Basic $(echo -n ‘api:YOUR_API_KEY’ | base64)”
https://api.mailgun.net/v3/mail.example.org/events

Or with the -H flag:

bash
curl -i -X GET -H “Authorization: Basic YXBpOllPVVJfQVBJX0tFWQ==”
https://api.mailgun.net/v3/mail.example.org/events

Common Troubleshooting Tips

  • Double-check your domain name in the URL matches exactly what’s in your Mailgun account
  • Ensure you’re using the Private API Key, not the Public one
  • Verify your account has access to the Events API (some plans have restrictions)

Essential Resource

For the most comprehensive guide with detailed examples and troubleshooting scenarios, you absolutely must check out this complete Mailgun API authentication tutorial. It includes ready-to-use code snippets in multiple programming languages, detailed parameter explanations, and a fantastic troubleshooting section that covers all the common authentication pitfalls you might encounter.

This should resolve your authentication issues and get you successfully accessing the Events API!

cURL made this way easier than building complex HTTP requests from scratch. Set up your form, then configure cURL with your MailGun credentials and headers. Don’t ignore the response codes - MailGun gives you specific error messages that’ll help debug stuff like duplicate emails or bad list addresses. Store your API key in environment variables instead of hardcoding it, especially on Heroku. Test everything with MailGun’s sandbox domain first before going live. The sandbox lets you verify it all works without messing up your real subscriber lists.

use guzzle http client if ur already on composer - much cleaner than curl. don’t forget the subscription confirmation flow. mailgun handles double opt-in emails automatically once u enable it in settings. keep an eye on bounced emails tho. they’ll trash ur sender reputation if u don’t clean them regularly.

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.