17

I try to send a email message by using mailgun. I use node.js (nest.js) and this is my mail service. What should I change? When I try to send first email (description in mailgun official website) I got the same error message.

import { Injectable } from '@nestjs/common';
import * as Mailgun from 'mailgun-js';
import { IMailGunData } from './interfaces/mail.interface';
import { ConfigService } from '../config/config.service';

@Injectable()
export class MailService {
  private mg: Mailgun.Mailgun;

  constructor(private readonly configService: ConfigService) {
    this.mg = Mailgun({
      apiKey: this.configService.get('MAILGUN_API_KEY'),
      domain: this.configService.get('MAILGUN_API_DOMAIN'),
    });
  }

  send(data: IMailGunData): Promise<Mailgun.messages.SendResponse> {
    console.log(data);
    console.log(this.mg);
    return new Promise((res, rej) => {
      this.mg.messages().send(data, function (error, body) {
        if (error) {
          console.log(error);
          rej(error);
        }
        res(body);
      });
    });
  }
}

When I try to send message I get 401 error with forbidden description.

My mg (console.log(this.mg))

Mailgun {
  username: 'api',
  apiKey: '920d6161ca860e7b84d9de75e14exxx-xxx-xxx',
  publicApiKey: undefined,
  domain: 'lokalne-dobrodziejstwa.pl',
  auth: 'api:920d6161ca860e7b84d9de75e14exxx-xxx-xxx',
  mute: false,
  timeout: undefined,
  host: 'api.mailgun.net',
  endpoint: '/v3',
  protocol: 'https:',
  port: 443,
  retry: 1,
  testMode: undefined,
  testModeLogger: undefined,
  options: {
    host: 'api.mailgun.net',
    endpoint: '/v3',
    protocol: 'https:',
    port: 443,
    auth: 'api:920d6161ca860e7b84d9de75e14exxx-xxx-xxx',
    proxy: undefined,
    timeout: undefined,
    retry: 1,
    testMode: undefined,
    testModeLogger: undefined
  },
  mailgunTokens: {}
}

My email body

{
  from: 'rejestracja@lokalne-dobrodziejstwa.pl',
  to: 'me@gmail.com',
  subject: 'Verify User',
  html: '\n' +
    '                <h3>Hello me@gmail.com!</h3>\n' +
    '            '
}
felixthehat
  • 849
  • 2
  • 9
  • 24
Pawel
  • 427
  • 2
  • 7
  • 15

7 Answers7

78

I had this problem when my domain was in an EU zone. When you're using an EU zone, you have to specify it in the config - this isn't clearly explained by Mailgun.

So it would be something like this:

var mailgun = require("mailgun-js")({
  apiKey: API_KEY,
  domain: DOMAIN,
  host: "api.eu.mailgun.net",
});
Ben
  • 968
  • 1
  • 6
  • 9
  • It's documented here: https://documentation.mailgun.com/en/latest/api-intro.html#base-url – Danilo Mz Feb 08 '21 at 21:19
  • See how to use it with `mailgun.js` npm module here: https://github.com/mailgun/mailgun.js/issues/40#issuecomment-412087161 – jones Aug 18 '23 at 05:03
10

EU USERS: For mailgun v3 you have to specify the eu endpoint in the url option in mailgun.client() like like this :

const API_KEY = "xxxxxxxxXxxxxxxxxxxxxxxxxxxx-xxxxxxx-xxxxxx";
const DOMAIN = "mydomaim.com";

const formData = require('form-data');
const Mailgun = require('mailgun.js');

const mailgun = new Mailgun(formData);
const client = mailgun.client({
  username: 'api',
  key: API_KEY,
  url:"https://api.eu.mailgun.net"
});
// console.log(client)

const messageData = {
  from: 'Yoopster <joep@mydomain.com>',
  to: 'mybestbuddy@gmail.com',
  subject: 'Hello',
  text: 'Testing some Mailgun awesomeness!'
};

client.messages.create(DOMAIN, messageData)
  .then((res) => {
    console.log(res);
  })
  .catch((err) => {
    console.error(err);
  });
Arunas
  • 3
  • 2
Joep
  • 119
  • 1
  • 3
2

Try to send email to yourself (account email) via this command in console:

curl -s --user 'api:YOUR_API_KEY' \
    https://api.mailgun.net/v3/YOUR_DOMAIN_NAME/messages \
    -F from='Excited User <mailgun@YOUR_DOMAIN_NAME>' \
    -F to=YOU@YOUR_DOMAIN_NAME \
    -F to=bar@example.com \
    -F subject='Hello' \
    -F text='Testing some Mailgun awesomeness!'

is it working?

If its not.. I assume you have written api and domain correctly so later if you have free acount you should check authorized recipant on overview section (you cant send email everywhere you want on trial account you have to type it first)

enter image description here If you didnt find solution this is how I have done my mailService (working) so you can just try it out i used nodemailer to do this:

import { Injectable, InternalServerErrorException, OnModuleInit } from '@nestjs/common';
import { readFileSync } from 'fs';
import { compile } from 'handlebars';
import { join } from 'path';
import * as nodemailer from 'nodemailer';
import { Options } from 'nodemailer/lib/mailer';
import * as mg from 'nodemailer-mailgun-transport';

import { IReplacement } from './replacements/replacement';
import { ResetPasswordReplacement } from './replacements/reset-password.replacement';

@Injectable()
export class MailService implements OnModuleInit {
  private transporter: nodemailer.Transporter;

  onModuleInit(): void {
    this.transporter = this.getMailConfig(); 
  }

  sendResetPasswordMail(email: string, firstName: string = '', lastName: string = ''): void { // this is just example method with template but you can use sendmail directly from sendMail method
    const resetPasswordReplacement = new ResetPasswordReplacement({
      firstName,
      lastName,
      email,
    });

    this.sendMail(
      proccess.env.MailBoxAddress),
      email,
      'Change password',
      this.createTemplate('reset-password', resetPasswordReplacement),
    );
  }

  sendMail(from: string, to: string, subject: string, body: string): void {
    const mailOptions: Options = { from, to, subject, html: body };

    return this.transporter.sendMail(mailOptions, (error) => {
      if (error) {
        throw new InternalServerErrorException('Error');
      }
    });
  }

  private getMailConfig(): any {
    return nodemailer.createTransport(mg({
      auth: {
        api_key: proccess.env.MailApiKey,
        domain: proccess.env.MailDomain
      },
    }));
  }

  private createTemplate(fileName: string, replacements: IReplacement): string {
    const templateFile = readFileSync(join(__dirname, 'templates', `${fileName}.html`), { encoding: 'utf-8' });
    const template = compile(templateFile);
    return template(replacements);
  }
}

and the

const templateFile = readFileSync(join(__dirname, 'templates', `${fileName}.html`), { encoding: 'utf-8' });

definates where html file with content is located so how it looks (in this case reset-password.html):

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Password reset</title>
</head>
<body>
  <div>Welcome {{firstName}} {{lastName}}</div>
</body>
</html>

and values in {{}} wil be replaced by library automatically

in this example example ResetPasswordReplacement its noly basic object which contains 3 properties and it inherits by IReplacement which is empty interface - made it only for define values in template file

sources:

  1. https://www.npmjs.com/package/nodemailer-mailgun-transport
  2. https://documentation.mailgun.com/en/latest/quickstart-sending.html#send-with-smtp-or-api
user3765649
  • 304
  • 4
  • 11
0

from mailgun api v3 onwards you have to:

var formData = require('form-data');
            const Mailgun = require('mailgun.js');
            const mailgun = new Mailgun(formData);
            const mg      = mailgun.client({
                username: 'api', 
                key: process.env.EMAIL_MAILGUN_API_KEY
            }); 
            
            mg.messages.create(process.env.EMAIL_MAILGUN_HOST, {
                from: "sender na,e <"+process.env.EMAIL_FROM+">",
                to: ["dan@dominic.com"],
                subject: "Verify Your Email",
                text: "Testing some Mailgun awesomness!",
                html: "<h1>"+req+"</h1>"
              })
              .then(msg => {
                console.log(msg);
                res.send(msg);
              }) // logs response data
              .catch(err => { 
                console.log(err);
                res.send(err);
              }); // logs any error
Lonare
  • 3,581
  • 1
  • 41
  • 45
0

To use mailgun API, whitelist your server IP from where you are want to send emails using mailgun web console.

Settings > Security & Users > API security > IP Whitelist

anaveed
  • 11
0

If you are still looking for an answer using golang script Add the following lines

mg := mailgun.NewMailgun()

//When you have an EU-domain, you must specify the endpoint

mg.SetAPIBase("https://api.eu.mailgun.net/v3")
Allie Moosa
  • 422
  • 1
  • 4
  • 12
-1

Another possible case that happened to me:
I initially installed mailgun-js with npm and started using yarn instead, then it returned 401 Forbidden in every request. So yarn add mailgun-js resolved it.

J. Diaz
  • 371
  • 1
  • 6
  • 16
  • HTTP status 401 does not have anything to do with your NPM or Yarn client. If this truly fixed your issue then it may have been due to mailgun-js package version differences that you installed. – Cory Robinson Mar 14 '22 at 02:50