Live Chat

We'll need to share your messages (and your email address if you're logged in) with our live chat provider, Drift. Here's their privacy policy.

If you don't want to do this, you can email us instead at contact@anvil.works.

Sending and Receiving Email in your Apps

Enabling the Email Service

To enable the Email Service, click the ‘plus’ next to services in the App Browser:

Sending Email

To send an email, use the anvil.email.send function, passing in data as keyword parameters. This can only be called from a Server Module.

See The API Docs for the full list of positional parameters.

anvil.email.send(
  to="customer@example.com",
  from_address="support",
  from_name="MyApp Support",
  subject="Welcome to MyApp",
  html="<h1>Welcome!</h1>"
)

Receiving Email

To receive emails to your app, register a server function to handle incoming messages using the @anvil.email.handle_message decorator. Any emails sent to anything@YOUR_APP_ID.anvil.app (or anything@YOUR_CUSTOM_DOMAIN) will be passed to the decorated function.

This example stores incoming email messages and attachments in Data Tables and sends a reply to each message.

@anvil.email.handle_message
def handle_incoming_emails(msg):
  
  msg.reply(text="Thank you for your message.")

  msg_row = app_tables.received_messages.add_row(
              from_addr=msg.envelope.from_address, 
              to=msg.envelope.recipient,
              text=msg.text, 
              html=msg.html
            )
  for a in msg.attachments:
    app_tables.attachments.add_row(
      message=msg_row, 
      attachment=a
    )

The msg argument is of type anvil.email.Message. See the API Docs for full details of the Message object.

Addressees

The msg.addressees object contains all the parsed addressees of the message. This saves you from having to parse addresses like "John.Doe <john.doe@domain.com>" manually. The addressees object has three attributes:

  • from_address - A parsed address from the FROM header.
  • to_addresses - A list of parsed addresses from the TO headers.
  • cc_addresses - A list of parsed addresses from the CC headers.

Parsed addresses have three attributes:

  • address - The plain email address of this addressee, e.g. john.doe@domain.com.
  • name - The display name of this addressee, e.g. John Doe.
  • raw_value - A single string containing both address and display name, e.g. John Doe <john.doe@domain.com>.

In the following example, we use the parsed name of the sender to greet them by name in the reply.

  @anvil.email.handle_message
  def handle_incoming_emails(msg):
    
    msg.reply(text="Hi %s, thank you for your message." %
      msg.addressees.from_address.name
    )

To reject an incoming email, raise an anvil.email.DeliveryFailure exception. This will return an SMTP failure response (code 554) to the sender of the message.

  @anvil.email.handle_message
  def handle_incoming_emails(msg):
    
    raise anvil.email.DeliveryFailure("Delivery failed for some reason")

Test Mode

When test mode is enabled, all outgoing email will be sent to the owner of the app instead of the specified recipients. This allows you to test changes to your app without accidentally emailing your real users.

The Email Service view, showing a checked checkbox saying 'test mode' and a notification informing you that sent emails will go to your address.

Custom Mail Server

You can configure the Email Service to use your own SMTP server if you wish. Simply tick “Use Custom Mail Server”, then enter your SMTP connection settings. The password will be encrypted and stored using the same mechanism as used by the Secrets Service. After entering the settings, click “Test settings” to check that they work.

Email sent through a custom SMTP server does not count towards your monthly quota.

The Email Service view, showing a checked checkbox saying 'custom mail server' and a form where you can enter your SMTP server settings.

Quotas

Outgoing email is subject to a monthly quota, depending on your account plan.

If you exceed your monthly quota, all outgoing emails will be re-routed to the app owner, just like in Test Mode. Your monthly quota resets on the 1st of each month.

Email sent through a custom SMTP server does not count towards your monthly quota.