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

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 function, passing in data as keyword parameters. This can only be called from a Server Module. is available on the Individual Plan and above

You can send emails on the free plan by setting a custom SMTP server in the Email service

See The API Docs for the full list of positional parameters.
  from_name="MyApp Support",
  subject="Welcome to MyApp",

Receiving Email

To receive emails to your app, register a server function to handle incoming messages using the decorator. Any emails sent to (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.
def handle_incoming_emails(msg):
  msg.reply(text="Thank you for your message.")

  msg_row = app_tables.received_messages.add_row(
  for a in msg.attachments:

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


The msg.addressees object contains all the parsed addressees of the message. This saves you from having to parse addresses like "John.Doe <>" 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.
  • 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 <>.

In the following example, we use the parsed name of the sender to greet them by name in the reply.
  def handle_incoming_emails(msg):
    msg.reply(text=f"Hi {}, thank you for your message.")

To reject an incoming email, raise an exception. This will return an SMTP failure response (code 554) to the sender of the message.
  def handle_incoming_emails(msg):
    raise"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.


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.