An OCaml library that simplifies connecting to Sendgrid's REST API

This library wraps several different email services and simplifies the
process of sending mail. Let's look at an example:

module Email = Tidy_email.Email
module Mailgun = Tidy_email_mailgun

let send = Mailgun.backend {
    api_key = Sys.getenv "MAILGUN_API_KEY";
    base_url = Sys.getenv "MAILGUN_BASE_URL";

let () =
  let email =
      ~subject:"A small message"
      ~body:(Email.Text "Hi there!") in
  let u = Lwt_main.run (send email) in
  match u with
  | Ok _ -> print_string "Send succeeded."; exit 0
  | Error _ -> print_string "Send failed."; exit 1

The library defines two main entities an email type and an email
backend. Email backends are defined so that switching email providers
requires minimal changes.

Currently, tidy_email supports three different email services:

Each service is factored out into its own library; for example, to
integrate with SendGrid, use tidy_email_sendgrid.

About SendGrid and Mailgun

First things first! Here are links to use for setting up a new mail

  • To sign up for Mailgun, click here.

  • To sign up for SendGrid, click here.

The sections below contain a more extensive comparison of these two

Both SendGrid and Mailgun have extensive REST APIs (in fact, they also
support SMTP but recommend their APIs). These services can receive
email as well as sending it. Currently, this library only covers
sending email. Advanced features like managing email templates are
not covered here. They could be added in the future; pull requests and
suggestions are welcome.

Configuring mail service

This section contains tips for configuring your mail plan.

Mailgun is the only service I've encountered that provides a "sandbox
domain". A sandbox domain allows you to send email to a limited number
of configured email addresses, without having to register a domain.

If you don't plan to use a sandbox domain, you will first need to
register a domain (Route53 is a reasonable choice, most domains cost
~12 USD per year). In this case, most email providers will need you to
create some combination of DNS records (typically TXT, CNAME, and MX)
in order for email to work. Both SendGrid and Mailgun provide
straightforward instructions about which records to create.

If you're planning on sending marketing email or other automated
email, consider setting up a separate subdomain to protect the
reputation of your main domain. For example, if you own foo.com you
might use mail.foo.com for automated email. Since the reputations
for foo.com and mail.foo.com are distinct, a mistake in your
application won't impact the delivery of emails from foo.com
addresses. You can read more about this


This service is easy to set up and doesn't require a credit card until
you begin using custom domains. The "sandbox domain" feature is quite
helpful for running tests, though it's common for sandbox domain
emails to go directly to your spam folder. This is the provider I
recommend if you don't have your own domain or SMTP server already.

You will need two pieces of information from the Mailgun console:

  1. An API key. You may need to create this in the console first. For
    sandbox domains, this should exist already.

  2. A "base url". In the US, this looks like
    https://api.mailgun.net/v3/<Your Domain>. In the EU, it may look
    slightly different; check the documentation.

Note: If you're working with a sandbox domain, you will also need to
configure which addresses the domain is allowed to send to. Expect
messages from a sandbox domain to go to your spam folder (the relevant
DNS records can't get set correctly because the domain is temporary).


SendGrid requires a company email and website when setting up an
account. Using a generic email account (e.g. Protonmail, Gmail, etc.)
for this purpose is likely to result in your account getting frozen
for several days while you negotiate with their security/audit team.

If you already have a domain and company email, registering with a
SendGrid account is fast. SendGrid's REST API is somewhat more
complicated than the one provided by Mailgun. Both services have a
free tier and aren't terribly expensive for small volumes of mail.

To use SendGrid with tidy_email, you will need:

  1. An API key. You'll need to create this in the console and set the
    permissions accordingly.

  2. The URL for the SendGrid API. As of this writing, this is
    https://api.sendgrid.com/v3/mail/send, but check the console to

SMTP Server

This backend is the most generic. In order to utilize an SMTP server,
you will need to obtain the hostname, username, and password for the
server. Many email services support SMTP in addition to a REST API
(take, for example Amazon
so this is a useful option if you want to use tidy_email with a
service that isn't supported by name yet.


Each backend module (Mailgun, SendGrid, and SMTP) contains a folder of
examples. To run an example, copy the relevant send.sh.template file
to send.sh and populate the missing environment
variables. Running ./send.sh should print:

Starting email send.
Send succeeded.

and then a single message should appear in the destination inbox.

How this package is tested

There are two levels of testing in this project:

  1. Unit tests defined using alcotest.

  2. End-to-end tests that are run manually by maintainers.

To run the unit tests, navigate to the root of the repository and run
dune test. Running ./coverage.sh will generate a summary of test
coverage via bisect_ppx. The coverage report will appear under

The specifications for the end to end tests are captured in the
/examples folder of each library. It's unfortunate that these tests
can't be captured in a public CI system, but testing requires
Mailgun/SendGrid credentials that can't easily be shared.

Other Design Suggestions

Sending an email is a fairly expensive operation; A REST API call to
Mailgun or SendGrid may take ~500 ms or more. You may wish to use
tidy_email together with some sort of queuing system and/or a
background worker. This blog
suggestions about how to do this.


The best place to record feature requests and bugs is in the
issues section of
this repo.

I am also available on Discord and Discuss:

Tagging comments with @jsthomas is a good way to get my attention.


Pull requests and other contributions (examples, bug reports, better
documentation) are welcome.


The implementation of tidy_email_sendgrid is based on work in

= version
>= "1.7.0"
>= "2.0.0"
>= "2.5.5"
>= "4.11.0"
>= "2.8" & >= "2.0"
>= "1.0.4" & with-test
>= "1.4.0" & with-test
>= "2.5.0" & dev
Reverse Dependencies