Send email from Workers using MailChannels, for free

If you’re looking for a way to send email from Workers, that now exists, with no login, no domain verification, and no payment. MailChannels now provides an API endpoint that lets you send email to anyone, for free.

Our KB article describes how to get started here: Sending Email from Cloudflare Workers using MailChannels Send API

Snippet of Workers code follows to show how it works:

addEventListener("fetch", event => {
    event.respondWith(handleRequest(event.request))
})
 
async function handleRequest(request) {
    let content = "";
    for( var i of request.headers.entries() ) {
        content += i[0] + ": " + i[1] + "\n";
    }
    let send_request = new Request("https://api.mailchannels.net/tx/v1/send", {
        "method": "POST",
        "headers": {
            "content-type": "application/json",
        },
        "body": JSON.stringify({
            "personalizations": [
                { "to": [ {"email": "[email protected]",
                        "name": "Test Recipient"}]}
            ],
            "from": {
                "email": "[email protected]",
                "name": "Test Sender",
            },
            "subject": "Test Subject",
            "content": [{
                "type": "text/plain",
                "value": "Test message content\n\n" + content,
            }],
        }),
    });
 
    let respContent = "";
    // only send the mail on "POST", to avoid spiders, etc.
    if( request.method == "POST" ) {
        const resp = await fetch(send_request);
        const respText = await resp.text();
 
        respContent = resp.status + " " + resp.statusText + "\n\n" + respText;
 
    }
 
    let htmlContent = "<html><head></head><body><pre>" +
        "</pre><p>Click to send message: <form method="post"><input type="submit" value="Send"/></form></p>" +
        "<pre>" + respContent + "</pre>" +
        "</body></html>";
    return new Response(htmlContent, {
        headers: { "content-type": "text/html" },
    })
}
9 Likes

I modified the script a bit to use a simple form instead of hard coding it. Can you tell what I am doing wrong … I am keep getting 400 error…although it worked once but after that, I am repeatedly getting 400.
and “Content value must be at least one character in length.

addEventListener("fetch", event => {
    event.respondWith(handleRequest(event.request))
})

async function handleRequest(request) {
    let content = "just drop if it fails...okay ?";

        let respContent = "";
        let fls = ""; 
    for( var i of request.headers.entries() ) {
        content += i[0] + ": " + i[1] + "\n";
    }

    let send_request = new Request("https://api.mailchannels.net/tx/v1/send", {
        "method": "POST",
        "headers": {
            "content-type": "application/json",
        },
        "body": JSON.stringify({
            "personalizations": [
                { "to": [ {"email": fls.email,
                        "name": fls.name}]}
            ],
            "from": {
                "email": "[email protected]",
                "name": "Amara Silva",
            },
            "subject": fls.subject,
            "content": [{
                "type": "text/plain",
                "value": fls.message,
            }],
        }),
    });

        // only send the mail on "POST", to avoid spiders, etc.
    if( request.method == "POST" ) {
    
    const formData = await request.formData();
    const body = {};
        for (const entry of formData.entries()) {
                      body[entry[0]] = entry[1];
              }
        fls = JSON.parse(JSON.stringify(body));

        const resp = await fetch(send_request);
        const respText = await resp.text();
 
        respContent = resp.status + " " + resp.statusText + "\n\n" + respText + fls.name; 
    }
 
    let htmlContent = "<html><head></head><body><pre>" +
        '</pre><p>Click to send message: <form method="post">Name: <input type="text" name="name"/><br>Email: <input type="text" name="email"/><br>Sub: <input type="text" name="subject"/><br>Msg: <input type="text" name="message"/><br><input type="submit" value="Send"/></form></p>' +
        "<pre>" + respContent + "</pre>" +
        "</body></html>";
    return new Response(htmlContent, {
        headers: { "content-type": "text/html" },
    })
}
1 Like

I think the problem is that when let send_request = new Request(...) is called, fls is still "". It gets reassigned to contain the form contents later, but at that point, the request has already been constructed.

1 Like

Thanks…I’ve fixed it now. Just called the new Request(...) later after the body has assigned all the form variables. Thanks.

2 Likes

Great to hear that you got it working, Suleman. Don’t hesitate to file a support ticket by emailing [email protected] if you have any issues that aren’t getting resolved here in the forums.

Thanks it’s okay. I was just testing it. I liked the fact that it cannot leveraged for spam and bulk mailing…that’s what I wanted to test it for.

i made something about this see comment instead · GitHub

What’s the limitation of this service?
On the free workers plan, we can make 100,000 requests per day. Does this mean we can send 100,000 emails using workers every day on the free plan?

What types of content are we allowed to send using workers?

  • Transactional emails?
  • Marketing / promotional emails?
  • Can we build a marketing platform on top of workers and have users of our app send marketing / promotional / newsletters using this?

We don’t place any restrictions or arbitrary limits on what you can send. We do perform extensive spam and phishing filtering and have a state of the art system to ensure that bad email practices will be limited or blocked.

In the near future, we will offer paid accounts that provide access to metrics, logs, and other features that will help customers build larger services using the platform. If this is you, please get in touch with us and we will get something going for you.

The best way to get in touch for commercial questions is via [email protected]. For technical matters, please send to [email protected].

1 Like

Thanks! I guess if the CEO says it’s ok, then…it’s better than a note from your mom. :+1:

Agreed!