'Mailgun - 401 forbidden
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: '[email protected]',
to: '[email protected]',
subject: 'Verify User',
html: '\n' +
' <h3>Hello [email protected]!</h3>\n' +
' '
}
Solution 1:[1]
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",
});
Solution 2:[2]
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 <[email protected]>',
to: '[email protected]',
subject: 'Hello',
text: 'Testing some Mailgun awesomeness!'
};
client.messages.create(DOMAIN, messageData)
.then((res) => {
console.log(res);
})
.catch((err) => {
console.error(err);
});
Solution 3:[3]
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 [email protected] \
-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)
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:
Solution 4:[4]
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: ["[email protected]"],
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
Solution 5:[5]
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.
Solution 6:[6]
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
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Ben |
| Solution 2 | Joep |
| Solution 3 | user3765649 |
| Solution 4 | Lonare |
| Solution 5 | J. Diaz |
| Solution 6 | anaveed |
