Get your dream built 10x faster

Replit and Mailgun Integration: 2026 Guide

We build custom applications 5x faster and cheaper 🚀

Book a Free Consultation
4.9
Clutch rating 🌟
600+
Happy partners
17+
Countries served
190+
Team members
Matt Graham, CEO of Rapid Developers

Book a call with an Expert

Stuck on an error? Book a 30-minute call with an engineer and get a direct fix + next steps. No pressure, no commitment.

Book a free consultation

How to Integrate Replit with Mailgun

To integrate Replit with Mailgun, you send emails from your Repl by calling Mailgun’s REST API and you keep your Mailgun API key and domain inside Replit Secrets. There is no native or automatic integration — you explicitly make HTTPS requests from your backend code. On Replit, this usually means running a small server (Node.js, Python, etc.) that calls Mailgun’s API using your secret key. For webhooks (like tracking bounces, deliveries, opens), you expose a route on your Repl’s server and give Mailgun the public URL that Replit provides after running your app. That’s the entire real-world pattern: outbound API calls + inbound webhooks, all secured through Replit Secrets.

 

Core Steps to Integrate Replit with Mailgun

 

You set up Mailgun (domain + API key), store the key in Replit Secrets, write code that calls Mailgun’s REST API, run a server bound to 0.0.0.0, and if you need webhooks, expose a route and register that URL in Mailgun’s dashboard. This is practical, explicit, and matches how Replit actually runs full‑stack projects.

  • Create a Mailgun domain and get your API key and sending domain (e.g., mg.yourdomain.com).
  • In your Repl, open the Secrets panel and add secrets like MAILGUN_API_KEY and MAILGUN\_DOMAIN.
  • Write code that makes HTTPS POST requests to Mailgun’s API endpoint https://api.mailgun.net/v3/YOUR\_DOMAIN/messages.
  • Start your Repl’s server using Node.js or Python, binding to 0.0.0.0.
  • For webhooks (delivery events, bounces): create a route like /mailgun/webhook and register your public Replit URL in Mailgun’s dashboard.

 

Minimal, Real Node.js Example (Mailgun REST API)

 

// server.js
// Fully real, working example of sending an email with Mailgun from a Replit server

import express from "express";
import fetch from "node-fetch";

const app = express();
app.use(express.json()); // Needed for JSON webhooks if you use them

// Load secrets from Replit environment variables
const MAILGUN_API_KEY = process.env.MAILGUN_API_KEY;
const MAILGUN_DOMAIN = process.env.MAILGUN_DOMAIN; // e.g. mg.example.com

app.get("/", (req, res) => {
  res.send("Mailgun Integration Running");
});

app.get("/send-test", async (req, res) => {
  const url = `https://api.mailgun.net/v3/${MAILGUN_DOMAIN}/messages`;

  const form = new URLSearchParams();
  form.append("from", "Your App <mailgun@" + MAILGUN_DOMAIN + ">");
  form.append("to", "[email protected]");       // Replace with any email
  form.append("subject", "Hello from Replit + Mailgun!");
  form.append("text", "This message was sent using Mailgun's REST API.");

  // Mailgun uses HTTP Basic Auth with "api" as the username
  const authString = "Basic " + Buffer.from("api:" + MAILGUN_API_KEY).toString("base64");

  const resp = await fetch(url, {
    method: "POST",
    headers: { Authorization: authString },
    body: form
  });

  const data = await resp.json();
  res.json(data);
});

// OPTIONAL: Webhook endpoint for Mailgun delivery events
app.post("/mailgun/webhook", (req, res) => {
  // Mailgun sends JSON describing the event
  console.log("Webhook received:", req.body);

  // Always respond 200 OK or Mailgun will retry
  res.status(200).end();
});

// Replit requirement: bind to 0.0.0.0
app.listen(3000, "0.0.0.0", () => {
  console.log("Server running on port 3000");
});

 

Adding Secrets in Replit

 

In your Repl:

  • Open Tools → Secrets.
  • Create MAILGUN_API_KEY with your real API key.
  • Create MAILGUN\_DOMAIN with your sending domain.

Secrets automatically turn into environment variables inside your running Repl. Never hardcode the API key in your code.

 

How Webhooks Work on Replit

 

If you want Mailgun to notify your app about events (delivered, bounced, unsubscribed), you expose a server route and point Mailgun to it.

  • Run your Repl. Replit will generate a public URL like https://yourrepl.yourusername.repl.co.
  • Your webhook URL becomes https://yourrepl.yourusername.repl.co/mailgun/webhook.
  • In Mailgun Dashboard → Sending → Webhooks, paste that URL.
  • Mailgun will POST JSON to that route. Your server must return 200 OK.

When the Repl sleeps or restarts, the public URL stays stable, so webhooks remain valid. Just make sure the server is running.

 

Production Considerations

 

  • Keep secrets in Replit Secrets only, never in code.
  • Use Workflows if you need automated sending jobs (e.g., nightly reports).
  • For high‑volume email, run the sending logic on a stable backend outside Replit, but for typical app usage Replit is fine.
  • Always test with the sandbox domain or test addresses before sending to real users.

This setup—explicit API call, explicit webhook, secrets stored safely—is the correct and reliable way to integrate Replit with Mailgun.

Use Cases for Integrating Mailgun and Replit

1

Transactional Emails from a Replit Web App

 

A Replit-hosted web app can send transactional emails (sign‑up confirmations, password resets, receipts) using Mailgun’s REST API. The backend runs inside a Repl, binds to 0.0.0.0, and uses Replit Secrets to store the Mailgun API key and domain. Whenever a user triggers an action, the server makes an outbound HTTPS request to Mailgun to deliver the email. This works reliably because the email sending is offloaded to Mailgun, so even if the Repl restarts, the mail still goes out.

  • Mailgun API key stored in Secrets: MAILGUN_API_KEY
  • No inbound traffic required — just outbound HTTPS
  • Great fit for lightweight SaaS prototypes built inside Replit
// Example using Node.js + Express inside Replit
import express from "express";
import fetch from "node-fetch";

const app = express();
app.use(express.json());

app.post("/signup", async (req, res) => {
  await fetch(`https://api.mailgun.net/v3/${process.env.MAILGUN_DOMAIN}/messages`, {
    method: "POST",
    headers: {
      Authorization: "Basic " + Buffer.from("api:" + process.env.MAILGUN_API_KEY).toString("base64"),
      "Content-Type": "application/x-www-form-urlencoded"
    },
    body: new URLSearchParams({
      from: "My App <noreply@" + process.env.MAILGUN_DOMAIN + ">",
      to: req.body.email,
      subject: "Welcome!",
      text: "Thanks for signing up!"
    })
  });
  res.json({ ok: true });
});

app.listen(3000, "0.0.0.0");

2

Transactional Emails from a Replit Web App

 

You can expose a Webhook endpoint from a Replit server to receive delivery, open, spam, and bounce notifications from Mailgun. Replit assigns a public URL for the running Repl, and you register that URL in Mailgun’s dashboard. Your endpoint must verify Mailgun’s signature (HMAC) so you trust the events. This lets your app keep an accurate email status log without polling Mailgun.

  • Real-time updates for bounces and complaints
  • Useful for dashboards or customer support tools inside Replit
  • Webhook verification requires checking timestamp, token, and signature
// Mailgun webhook endpoint
import crypto from "crypto";
app.post("/mailgun/webhook", express.urlencoded({ extended: false }), (req, res) => {
  const { timestamp, token, signature } = req.body;
  const expected = crypto
    .createHmac("sha256", process.env.MAILGUN_API_KEY)
    .update(timestamp + token)
    .digest("hex");

  if (expected !== signature) return res.status(401).end();

  // Process the event safely
  console.log("Mailgun Event:", req.body.event);
  res.status(200).end();
});

3

Automated Bulk Email Jobs via Replit Workflows

 

Replit Workflows allow you to run scheduled or on-demand scripts without keeping a server online. You can create a workflow that loads your contact list (from a file, database, or external API) and sends bulk but controlled email batches through Mailgun. Each run is stateless and reproducible, ideal for newsletters, status updates, or periodic system notifications.

  • Runs even when the Repl is not open
  • Separates bulk jobs from your interactive web server
  • Easy to throttle by batching requests
# Example workflow step (run with Replit Workflows)
node send-batch.js
// send-batch.js
import fetch from "node-fetch";

const recipients = ["[email protected]", "[email protected]"]; // demo list

for (const to of recipients) {
  await fetch(`https://api.mailgun.net/v3/${process.env.MAILGUN_DOMAIN}/messages`, {
    method: "POST",
    headers: {
      Authorization: "Basic " + Buffer.from("api:" + process.env.MAILGUN_API_KEY).toString("base64"),
      "Content-Type": "application/x-www-form-urlencoded"
    },
    body: new URLSearchParams({
      from: "Batch Sender <noreply@" + process.env.MAILGUN_DOMAIN + ">",
      to,
      subject: "Scheduled Update",
      text: "Hello from Replit Workflows!"
    })
  });
}

Book Your Free 30‑Minute Migration Call

Speak one‑on‑one with a senior engineer about your no‑code app, migration goals, and budget. In just half an hour you’ll leave with clear, actionable next steps—no strings attached.

Book a Free Consultation

Troubleshooting Mailgun and Replit Integration

1

1. Why are Mailgun API requests failing on a Replit project even though the API key is correct?

Mailgun calls fail on Replit mainly because the request never reaches Mailgun: Replit blocks outbound traffic on port 25/465/587, so using SMTP fails even with a valid key. Mailgun must be called through their HTTPS REST API, and failures often come from wrong domain, missing REPLIT secrets, or sending from an unverified domain.

 

Why It Happens

 

Mailgun SMTP ports are restricted, so the connection is dropped. The REST API works, but only if the MAILGUN_API_KEY and MAILGUN\_DOMAIN are stored in Replit Secrets and you call the correct regional endpoint. Also, Mailgun rejects emails if your “from” domain is not verified.

  • Use HTTPS endpoint, not SMTP.
  • Set keys in Secrets, not hardcoded.
  • Verify domain in Mailgun dashboard.

 

// Working REST call from Replit
import fetch from "node-fetch"

fetch("https://api.mailgun.net/v3/YOUR_DOMAIN/messages", {
  method: "POST",
  headers: { Authorization: "Basic " + Buffer.from(`api:${process.env.MAILGUN_API_KEY}`).toString("base64") },
  body: new URLSearchParams({ from:"[email protected]", to:"[email protected]", subject:"Hi", text:"OK" })
})

2

2. How to properly store Mailgun API keys in Replit Secrets so they are recognized at runtime?

The right way is to put your Mailgun keys into Replit Secrets and read them with process.env at runtime. You never hard‑code keys in your files, and you never commit them. Secrets automatically load into the environment each time the Repl or Deployment starts, so your Mailgun client can access them safely.

 

Store and load Mailgun keys

 

Open the Secrets panel in Replit, create keys like MAILGUN_API_KEY and MAILGUN\_DOMAIN, and Replit will expose them as environment variables while the app runs. Your code must read them dynamically.

  • Name keys clearly so you know exactly which variable your code expects.
  • Restart the Repl after adding or changing secrets so process.env receives the updates.

 

// Example: using Mailgun with Replit Secrets
import formData from "form-data"
import Mailgun from "mailgun.js"

const mg = new Mailgun(formData).client({
  username: "api",
  key: process.env.MAILGUN_API_KEY // pulled from Replit Secrets
})

mg.messages.create(process.env.MAILGUN_DOMAIN, {
  from: "[email protected]",
  to: "[email protected]",
  subject: "Hello",
  text: "Works!"
})

3

3. Why does a Replit web server return a timeout or 403 error when sending emails through Mailgun?

A Replit server times out or gets a 403 with Mailgun because the request never leaves Replit correctly or Mailgun blocks it. Most often the Repl tries to call Mailgun through the wrong endpoint, uses an invalid API key, or stalls because outbound SMTP isn’t allowed in the Replit environment. Mailgun expects HTTPS REST calls with proper authentication, and if anything is off, Replit shows a timeout or Mailgun returns 403.

 

Why It Happens

 

Your Repl must call Mailgun’s REST API over HTTPS. Replit does not allow raw outbound SMTP, so using SMTP configs leads to silent stalls and timeouts. If you use the wrong Mailgun domain, or the API key stored in Replit Secrets is incorrect, Mailgun rejects the request with 403 Forbidden. Timeouts also appear if the code waits on a connection that never succeeds.

  • Use Mailgun’s HTTPS endpoint like https://api.mailgun.net/v3/YOUR\_DOMAIN/messages
  • Store MAILGUN_API_KEY and MAILGUN\_DOMAIN in Replit Secrets

 

import FormData from "form-data"
import fetch from "node-fetch"

const fd = new FormData()
fd.append("from", "Test <[email protected]>")
fd.append("to", "[email protected]")
fd.append("subject", "Hi")
fd.append("text", "Message")

fetch(`https://api.mailgun.net/v3/${process.env.MAILGUN_DOMAIN}/messages`, {
  method: "POST",
  headers: { Authorization: "Basic " + Buffer.from("api:" + process.env.MAILGUN_API_KEY).toString("base64") },
  body: fd
})
Book a Free Consultation

Schedule a 30‑Minute No‑Code‑to‑Code Consultation

Grab a quick video call to discuss the fastest, most cost‑efficient path from no‑code to production‑ready code. Zero sales fluff—just practical advice tailored to your project.

Contact us

Common Integration Mistakes: Replit + Mailgun

Leaking API Keys in Source Code

Many developers accidentally hardcode their Mailgun API key directly in their Repl. Since Replit auto‑forks and exposes code publicly, this instantly compromises the key. Always store keys in Replit Secrets so they remain private and only accessible at runtime, not committed into the project files.

  • Use environment variables like process.env.MAILGUN_API_KEY
// Correct usage: pull the key from Replit Secrets
const apiKey = process.env.MAILGUN_API_KEY;

Using the Wrong Mailgun Endpoint

Mailgun has different API base URLs depending on region and domain type. A common mistake is pointing to api.mailgun.net when your domain is on the EU cluster, causing silent failures or 404 errors. Always match your domain’s region and use the URL shown in Mailgun’s dashboard for sending.

  • US domains: https://api.mailgun.net/v3/YOUR\_DOMAIN
// Example: ensure your domain matches this base URL
const BASE_URL = "https://api.mailgun.net/v3/" + process.env.MAILGUN_DOMAIN;

Not Binding the Webhook Server to 0.0.0.0

Mailgun webhooks need to reach your Repl from the public internet. If you bind your Express or Flask server to localhost instead of 0.0.0.0, Mailgun simply cannot connect. Replit only exposes servers listening on 0.0.0.0, so webhooks will silently fail until the binding is fixed.

  • Use port from process.env.PORT when available
// Correct binding for Replit webhook servers
app.listen(process.env.PORT || 3000, "0.0.0.0");

Not Verifying Webhook Signatures

Mailgun signs every webhook request, but many developers skip verification. On Replit, where URLs are public, this means anyone can hit your webhook endpoint and trigger fake events. Use Mailgun’s timestamp + token + signature verification to confirm the request is legitimate before processing it.

  • Reject events when signature mismatch occurs
// Minimal verification (example logic)
const crypto = require("crypto");
const expected = crypto.createHmac("sha256", process.env.MAILGUN_SIGNING_KEY)
  .update(timestamp + token)
  .digest("hex");

Still stuck?
Copy this prompt into ChatGPT and get a clear, personalized explanation.

This prompt helps an AI assistant understand your setup and guide you through the fix step by step, without assuming technical knowledge.

AI AI Prompt


Recognized by the best

Trusted by 600+ businesses globally

From startups to enterprises and everything in between, see for yourself our incredible impact.

RapidDev was an exceptional project management organization and the best development collaborators I've had the pleasure of working with.

They do complex work on extremely fast timelines and effectively manage the testing and pre-launch process to deliver the best possible product. I'm extremely impressed with their execution ability.

Arkady
CPO, Praction
Working with Matt was comparable to having another co-founder on the team, but without the commitment or cost.

He has a strategic mindset and willing to change the scope of the project in real time based on the needs of the client. A true strategic thought partner!

Donald Muir
Co-Founder, Arc
RapidDev are 10/10, excellent communicators - the best I've ever encountered in the tech dev space.

They always go the extra mile, they genuinely care, they respond quickly, they're flexible, adaptable and their enthusiasm is amazing.

Mat Westergreen-Thorne
Co-CEO, Grantify
RapidDev is an excellent developer for custom-code solutions.

We’ve had great success since launching the platform in November 2023. In a few months, we’ve gained over 1,000 new active users. We’ve also secured several dozen bookings on the platform and seen about 70% new user month-over-month growth since the launch.

Emmanuel Brown
Co-Founder, Church Real Estate Marketplace
Matt’s dedication to executing our vision and his commitment to the project deadline were impressive. 

This was such a specific project, and Matt really delivered. We worked with a really fast turnaround, and he always delivered. The site was a perfect prop for us!

Samantha Fekete
Production Manager, Media Production Company
The pSEO strategy executed by RapidDev is clearly driving meaningful results.

Working with RapidDev has delivered measurable, year-over-year growth. Comparing the same period, clicks increased by 129%, impressions grew by 196%, and average position improved by 14.6%. Most importantly, qualified contact form submissions rose 350%, excluding spam.

Appreciation as well to Matt Graham for championing the collaboration!

Michael W. Hammond
Principal Owner, OCD Tech

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We’ll discuss your project and provide a custom quote at no cost.Â