Skip to content

White-label transactional emails

When a user on your customer's app resets their password, the email that lands in their inbox is part of your customer's brand, or it should be. Out of the box, those messages go through Appwrite's shared sender and carry Appwrite's default templates, which quietly breaks the white-label illusion the moment a user reads the "from" line. The Project API lets you route a project's mail through the customer's own SMTP server and rewrite every template, so verification, recovery, and invitation emails come from the customer's domain and read in their voice.

This guide:

  • Points a project at the customer's SMTP server.
  • Confirms that mail is reaching the inbox.
  • Rebrands a template in the customer's voice.
Required scopes

The SMTP calls need the project.write scope, and the template calls need the templates.write scope.

Configure custom SMTP

Set up the client, then hand the project the customer's SMTP credentials and the sender identity their recipients should see in the inbox. The password is write-only, so it is stored securely and never comes back in a response.

import { Client, Project, ProjectSMTPSecure } from 'node-appwrite';

const client = new Client()
    .setEndpoint('https://<REGION>.cloud.appwrite.io/v1')
    .setProject('<PROJECT_ID>')
    .setKey('<YOUR_API_KEY>');

const project = new Project(client);

const result = await project.updateSMTP({
    host: 'smtp.example.com',
    port: 587,
    username: '<USERNAME>', // optional
    password: '<PASSWORD>', // optional
    senderEmail: 'email@example.com',
    senderName: '<SENDER_NAME>', // optional
    replyToEmail: 'email@example.com', // optional
    replyToName: '<REPLY_TO_NAME>', // optional
    secure: ProjectSMTPSecure.Tls, // optional
    enabled: true
});
Enabling SMTP validates the connection

The moment you enable custom SMTP, Appwrite tries to connect to the host you gave it, so the server has to be reachable with those exact credentials or the call fails. Custom SMTP is also the prerequisite for everything below: you cannot customize email templates until it is enabled.

Send a test email

Do not wait for a real password reset to find out the configuration is wrong. Send yourself a test message, which goes out through the SMTP settings you just saved, and a message arriving in the inbox confirms the whole path end to end.

const result = await project.createSMTPTest({
    emails: ['email@example.com']
});

Customize a template

With mail flowing through the customer's server, rebrand the messages themselves. Set the subject, body, and sender for a template in a given locale, and repeat per locale to localize. This example rewrites the verification email.

const result = await project.updateEmailTemplate({
    templateId: ProjectEmailTemplateId.Verification,
    locale: ProjectEmailTemplateLocale.En, // optional
    subject: '<SUBJECT>', // optional
    message: '<MESSAGE>', // optional
    senderName: '<SENDER_NAME>', // optional
    senderEmail: 'email@example.com', // optional
    replyToEmail: 'email@example.com', // optional
    replyToName: '<REPLY_TO_NAME>' // optional
});

The template ID is one of verification, magicSession, recovery, invitation, mfaChallenge, sessionAlert, or otpSession.

Read templates back

Read a template back to check the copy that is live, which is worth doing whenever you let a customer edit their own templates through your dashboard.

const result = await project.getEmailTemplate({
    templateId: ProjectEmailTemplateId.Verification,
    locale: ProjectEmailTemplateLocale.En // optional
});

Or list the project's templates to see everything you have overridden in one call.

const result = await project.listEmailTemplates({
    queries: [], // optional
    total: false // optional
});

Next steps

With SMTP and templates driven from code, every customer's project sends mail that carries their brand rather than yours. From here: