Custom Emails
Some server actions send email messages to users. For example, when a user creates a new account, the server sends a "Welcome" email message. On Medplum's hosted environment, the email will include a link to "https://app.medplum.com/setpassword/...".
The two main email messages are:
- Welcome email - when a new user account is created
- Password reset email - when a user requests a password reset
Medplum fully supports creating a white-label experience where users do not see any Medplum branding. That includes overriding all email behavior.
This document describes the steps to send custom email messages.
In short, here are the key steps:
- Setup reCAPTCHA (required for password reset emails, optional for welcome emails)
- Create a "Reset Password" page in your application (required for password reset emails, optional for welcome emails)
- Create a "Set Password" page in your application
- Create a Medplum Bot that processes new
PasswordChangeRequest
resources - Create a FHIR Subscription that connects
PasswordChangeRequest
resources to the bot
Setup reCAPTCHA
Optional reCAPTCHA is only required for Password Reset emails. reCAPTCHA is optional for Welcome emails.
Medplum requires reCAPTCHA for all unauthenticated API requests. "Reset Password" is necessarily unauthenticated, so you will need to setup reCAPTCHA first.
Visit the reCAPTCHA website to get started. You will need to create a new reCAPTCHA v3 key. Make note of the "Site Key" and "Secret Key".
Once you have your "Site Key" and "Secret Key", you will need to configure your Medplum project. Go to Projects and click "Edit...". Enter the "Site Key" and "Secret Key" in the "reCAPTCHA" section.
Reset Password Page
Optional Reset Password Page is only required for Password Reset emails. Reset Password Page is optional for Welcome emails.
In your custom application, you will need a "Reset Password" page. This will be the page where users go to initiate the reset password flow. For a full example of a "Reset Password" page, check out the source to ResetPasswordPage.tsx
for the Medplum App.
This page is conceptually simple, as the only input is an email. However, you must also include the projectId
and recaptchaToken
in the request body.
Key functions of the page:
- Initialize reCAPTCHA
- Prompts the user for
email
- Sends the
email
,projectId
, andrecaptchaToken
to the/auth/resetpassword
API endpoint
See the ResetPasswordPage.tsx
for a full example.
Set Password Page
In your custom application, you will need a "Set Password" page. This will be the page where users go to confirm their email address. For a full example of a "Set Password" page, check out the source to SetPasswordPage.tsx
for the Medplum App.
Key functions of the page:
- Receives
id
andsecret
from URL parameters - Prompts the user for
password
andconfirmPassword
- Verifies that
password
andconfirmPassword
match - Sends the
id
,secret
, andpassword
to the/auth/setpassword
API endpoint
Password Change Request Bot
Create a Medplum Bot to handle new PasswordChangeRequest
resources. The bot will send a custom email message to the user.
If you are new to Medplum Bots, you may want to read the Bots documentation first.
This Bot will use ProjectMembership
and PasswordChangeRequest
resources, so the Bot must be a "Project Admin" to access these resources.
Here is a full example of a Bot that sends a custom email message:
import { BotEvent, getDisplayString, getReferenceString, MedplumClient, ProfileResource } from '@medplum/core';
import { PasswordChangeRequest, ProjectMembership, Reference, User } from '@medplum/fhirtypes';
export async function handler(medplum: MedplumClient, event: BotEvent<PasswordChangeRequest>): Promise<any> {
// This Bot executes on every new PasswordChangeRequest resource.
// PasswordChangeRequest resources are created when a new user registers or is invited to a project.
// PasswordChangeRequest resources are only available to project administrators.
// Therefore, this Bot must be configured as a project admin.
const pcr = event.input;
// Get the user from the PasswordChangeRequest.
const user = await medplum.readReference(pcr.user as Reference<User>);
// Get the project membership for the user.
// ProjectMembership is an administrative resource that connects a User and a Project.
const membership = (await medplum.searchOne('ProjectMembership', {
user: getReferenceString(user),
})) as ProjectMembership;
// From the ProjectMembership, we can retrieve the user's profile.
// Here, "profile" means FHIR profile: the FHIR resource that represents the user's identity.
// In general, the profile will be a FHIR Patient or a FHIR Practitioner.
const profile = await medplum.readReference(membership.profile as Reference<ProfileResource>);
// Get the email from the user.
const email = user.email as string;
// Generate the setPasswordUrl based on the id and secret.
// You will need to use these values in your application to confirm the user account.
const setPasswordUrl = `https://example.com/setpassword/${pcr.id}/${pcr.secret}`;
// Now we can send the email to the user.
// This is a simple plain text email to the user.
// Medplum supports sending HTML emails as well via the nodemailer API.
// Learn more: https://nodemailer.com/extras/mailcomposer/
if (pcr.type === 'invite') {
// This PasswordChangeRequest was created as part of a new user invite flow.
// Send a Welcome email to the user.
await medplum.sendEmail({
to: email,
subject: 'Welcome to Example Health!',
text: [
`Hello ${getDisplayString(profile)}`,
'',
'Please click on the following link to create your account:',
'',
setPasswordUrl,
'',
'Thank you,',
'Example Health',
'',
].join('\n'),
});
} else {
// This PasswordChangeRequest was created as part of a password reset flow.
// Send a Password Reset email to the user.
await medplum.sendEmail({
to: email,
subject: 'Example Health Password Reset',
text: [
`Hello ${getDisplayString(profile)}`,
'',
'Someone requested to reset your Example Health password.',
'',
'To reset your password, please click on the following link:',
'',
setPasswordUrl,
'',
'If you received this in error, you can safely ignore it.',
'',
'Thank you,',
'Example Health',
'',
].join('\n'),
});
}
return true;
}
Create, save, and deploy your bot. Make note of the Bot ID.
FHIR Subscription
Create a FHIR Subscription that connects PasswordChangeRequest
resources to the bot. The subscription will trigger the bot when a new ProjectMembership
resource is created.
Go to Subscriptions and click "New...".
Enter the following values:
- Status =
active
- Reason =
New Password Change Request
- Criteria =
PasswordChangeRequest
- Channel Type =
rest-hook
- Channel Endpoint =
Bot/YOUR_BOT_ID
Testing
To test your custom welcome email, create a new user account. The user will receive a custom email message.