Home
Mailgun Blog
Dev Life category
Learn how to build a password reset email workflow using Mailgun API
Building transactional email workflows for password resets with Mailgun’s API
Struggling with sending secure password reset emails? This hands-on guide walks you through building a reliable, automated email workflow using Mailgun’s API and FastAPI.…
PUBLISHED ON
Transactional emails are automated emails triggered by user actions on a website, app, or service. Unlike marketing emails, which promote products or services, transactional emails provide essential, often real-time information.
Transactional email workflows take this further by automating a sequence of emails based on user actions to ensure timely communication and a smooth user experience. Companies rely on services like Mailgun for transactional emails because they're prompt, relevant, and triggered by immediate user actions. For example, the workflow allows a user to quickly reset their password and regain access to their account.
In this tutorial, you'll learn how to build a transactional email workflow for password resets with Mailgun's API.
Table of contents
Setting up a Mailgun account
If you don't already have a Mailgun account, the first thing you need to do is create one. Make sure to check the email associated with your Mailgun account and verify your account:

Next, input your phone number and the provided authorization code to activate your account:

Once your Mailgun account is set up, it's time to obtain an API key to start using the service.
Obtaining an API key
For your application to interact with Mailgun's services, it needs to be authenticated via an API key. To get your API key, navigate to the Mailgun dashboard and select Get started on the left to view a Get started guide:

Click the Get started button under Create an API Key. Then, provide a description that properly describes the API's key purpose. For example, if the API key is for user authentication processes, you can name it something like user_authentication and click Create Key:

Make sure to copy and keep this key somewhere safe, as this is the only time the key will be displayed:

Building the application interface using FastAPI
To build the application interface that powers the email workflow, the first thing you need to do is install the necessary libraries. Before you do, create a project directory by running mkdir my_fastapi_app
and launch a virtual environment in the directory.
Then, install the necessary libraries with the following command:
pip install fastapi jinja2 requests uvicorn python-multipart
Here, you install:
fastapi
, which serves as the main backend frameworkjinja2
, which serves your frontend templatesrequests
, which sends HTTP requests to the Mailgun API
While FastAPI is primarily for building APIs, adding the jinja2
library allows for full-stack development – but it requires proper directory setup. Your project structure should follow this format:
my_fastapi_app/
│── main.py # Main FastAPI application file
│── users.json # JSON file storing user data
│── templates/ # HTML templates folder
│ ├── login.html # Login page
│ ├── home.html # Home page
│ ├── passwordReset.html # Password reset page
│── static/ # Static files folder
│ ├── styles.css # Styles for the login.html and passwordReset.html files
You can find all the code for this tutorial in this GitHub repository.
With your project structure in place, you can either copy and paste the code into the respective files or clone the GitHub repository above with the following command:
git clone https://github.com/EphraimX/building-transactional-email-workflows-for-password-resets-with-mailgun-api.git
Let's take a look at the first section of the main.py file. This file is where all the magic happens, as it connects the frontend and backend operations:
In this code, the app handles authentication by serving login and password reset pages while verifying credentials in users.json
. The app initializes FastAPI, configures templates, and defines a loginData model for validation. The /login
and /passwordResetView/
routes serve their respective pages, while /home verifies credentials, redirecting valid users or displaying errors. The password reset function generates a unique link using uuid and sends it via Mailgun's API.
To test this out, open your makeshift database (users.json
) and paste the following:
You should also authorize these email addresses via the Mailgun dashboard if you're using a sandbox domain.
Start your application by running the command uvicorn main:app --reload. Then, head over to http://localhost:8000/login, where you should see this:

If you attempt to log in with any of the credentials in the users.json file, you'll be taken to the home page:

If you input the wrong login information, you'll see the following:

Setting up a connection to Mailgun
To send emails using Mailgun, you need to establish a connection between your application and Mailgun's email service.
To do so, open main.py and paste the following code, replacing <SANDBOX_URL>
, <RECIPIENT_NAME>
, and <RECIPIENT_EMAIL> with valid values:
For testing purposes, pass your API key directly when calling the function:
send_simple_message("your-api-key-here")
Run the script by executing the following:
python main.py
If successful, the function will print a response similar to this:
{
"id": "<MESSAGE_ID>",
"message": "Queued. Thank you."
}
This confirms that the email request has been successfully queued by Mailgun.
Designing and setting up the email template
To work on the password reset logic, you need to design the template for the email that's sent to the user. With Mailgun, there are two ways to design an email: HTML or Mailgun's drag-and-drop template builder. You'll use the HTML approach here.
To begin the design, go to your Mailgun dashboard and navigate to Send > Sending > Templates:

Next, click Create message template, select the option to code it in HTML, and click the Blank template:

Fill in the template form with the name, description, and details of the email:

Then, scroll down to the Editor tab and copy and paste the code from the email_template.html file:

When you select the Test Data tab, you'll see two dynamic variables: username
and reset_link
.
Select the Preview tab to see the template:

Finally, click the Create button to create it.
Implementing the email sending functionality
Once your email template is ready, it's time to build out the email sending functionality. Open the main.py
file and paste the following code:
In this code, the reset_password
function (@app.post("/resetPassword/")
) processes password reset requests by checking if the submitted email exists in the users.json
database. If found, it generates a unique reset link using generate_password_reset_link(email_address)
and sends the reset email via send_password_reset_email(username, email_address, reset_link)
. The function then returns a success message if the email was sent or displays an error message if not. If no matching email is found, an appropriate error response is provided.
Note: Replace the API_KEY
variable with the key to your Mailgun account.
To test the implementation, run uvicorn main:app
--reload and open http://localhost:8000/login. Click the Forgot Password? link:

Enter an email present in your users.json
file and click Reset Password:

If successful, you should be directed back to the login page, where you'll be met with instructions to check your email. You should see the following email in the recipient's email inbox:

Integrating with application events
Integrating with application events using webhooks allows your system to automatically respond to real-time updates from external services.
A webhook is essentially an HTTP callback. Whenever a specific event occurs in an external application (such as a payment confirmation, user signup, or password reset request), the service sends a request to your predefined webhook URL. This enables seamless automation, reducing the need for manual intervention or periodic polling. By configuring your application to listen for these webhook events, you can trigger relevant actions, such as updating a database, sending notifications, or processing transactions, as soon as the event happens.
To integrate webhooks with Mailgun, you need to create an endpoint. To do that, open the main.py file and paste the following code at the end:

Click Add webhook in the top-right corner. In the drop-down list of event types, select Delivered Messages, and in the URL field, input the route to your webhook URL. In this case, it's https://<YOUR_DOMAIN>/webhooks/password-reset. When you're finished, click Create webhook:

Test the webhook. Your result should look like this:

On the server side, you should get the following message:
Password reset email clicked: {'signature': {'token': '41929b08bb287b8a0b157f7834844b6bc483b7b090692a2b15', 'timestamp': '1739785355', 'signature': '117f70dcf985c72df30c07b02898dd458d1cbbad29d0457ccee8c4497f89c5ac'}, 'event-data': {'id': 'CPgfbmQMTCKtHW6uIWtuVe', 'timestamp': 1521472262.908181, 'log-level': 'info', 'event': 'delivered', 'delivery-status': {'tls': True, 'mx-host': 'smtp-in.example.com', 'code': 250, 'description': '', 'session-seconds': 0.4331989288330078, 'utf8': True, 'attempt-no': 1, 'message': 'OK', 'certificate-verified': True}, 'flags': {'is-routed': False, 'is-authenticated': True, 'is-system-test': False, 'is-test-mode': False}, 'envelope': {'transport': 'smtp', 'sender': 'bob@sandbox9199067bcb654265aae9ce9e308b6150.mailgun.org', 'sending-ip': '209.61.154.250', 'targets': 'alice@example.com'}, 'message': {'headers': {'to': 'Alice <alice@example.com>', 'message-id': '20130503182626.18666.16540@sandbox9199067bcb654265aae9ce9e308b6150.mailgun.org', 'from': 'Bob <bob@sandbox9199067bcb654265aae9ce9e308b6150.mailgun.org>', 'subject': 'Test delivered webhook'}, 'attachments': [], 'size': 111}, 'recipient': 'alice@example.com', 'recipient-domain': 'example.com', 'storage': {'url': 'https://se.api.mailgun.net/v3/domains/sandbox9199067bcb654265aae9ce9e308b6150.mailgun.org/messages/message_key', 'key': 'message_key'}, 'campaigns': [], 'tags': ['my_tag_1', 'my_tag_2'], 'user-variables': {'my_var_1': 'Mailgun Variable #1', 'my-var-2': 'awesome'}}}
This may change depending on the email being sent.
Monitoring and managing emails
Peter Drucker, a famous management consultant, once said:
If you can't measure it, you can't improve it.
In line with this, Mailgun provides tools to check the performance of sent emails. For example, the Send > Sending > Analytics page helps measure what percentage of emails are being delivered, opened, and clicked:

On the Send > Reporting > Metrics page, you can get detailed information about how your emails are performing:

You can also choose the metrics you wish to see over a given period:

Mailgun also provides you with logs in case you want to dig deeper into delivered events and investigate failures when they occur:

Lastly, Mailgun provides a bounce classification to see how many of your emails don't go through. It also lets you know how many of those bounces are critical:

Wrapping up
In this tutorial, you learned how to build a complete transactional email workflow for password resets using Mailgun's API. After setting up a Mailgun account and obtaining an API key, you built an application interface using FastAPI. You then designed and configured an email template, implemented email sending functionality, and integrated webhooks for real-time event tracking.
Transactional email operations are critical in providing a seamless and secure user experience, especially for password resets. Mailgun can help you automate this process, improving security and productivity while eliminating manual intervention. Implementing this approach in your projects will not only increase customer satisfaction but also help maintain a dependable authentication system.