<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Michele Mazzucchi</title>
    <description>The latest articles on DEV Community by Michele Mazzucchi (@mmic).</description>
    <link>https://dev.to/mmic</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1354908%2Ff1dce191-908a-45ad-b77b-9eb9ed3fb4b8.png</url>
      <title>DEV Community: Michele Mazzucchi</title>
      <link>https://dev.to/mmic</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mmic"/>
    <language>en</language>
    <item>
      <title>The right way to email your users</title>
      <dc:creator>Michele Mazzucchi</dc:creator>
      <pubDate>Tue, 02 Jul 2024 14:53:50 +0000</pubDate>
      <link>https://dev.to/mmic/the-right-way-to-email-your-users-2pnn</link>
      <guid>https://dev.to/mmic/the-right-way-to-email-your-users-2pnn</guid>
      <description>&lt;p&gt;When you build a web application you need to email your users for password resets, orders placed and the like.&lt;/p&gt;

&lt;p&gt;A trivial task, it seems. Here's a one-liner in django:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;django.core.mail&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;send_mail&lt;/span&gt;

&lt;span class="nf"&gt;send_mail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Password changed!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hey &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;first_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;!&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s"&gt;Your password changed...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;support@yourservice.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
          &lt;span class="n"&gt;fail_silently&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h1&gt;
  
  
  Reality kicks in
&lt;/h1&gt;

&lt;p&gt;A one-liner. Problem solved, now moving on.&lt;/p&gt;

&lt;p&gt;But soon after, issues arise:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You want to include some special characters like '🙂' or 'schön'. The one-liner no longer works.&lt;/li&gt;
&lt;li&gt;You need some conditional text for paying users. Which requires some &lt;strong&gt;templating&lt;/strong&gt; logic.&lt;/li&gt;
&lt;li&gt;Some users fail to receive your notifications because their &lt;strong&gt;spam filter&lt;/strong&gt; distrusts your bare-bones emails. Figure out what they need and build it.&lt;/li&gt;
&lt;li&gt;You want &lt;strong&gt;branded&lt;/strong&gt; emails with your logo and stationery. You need to build complex MIME/multipart assemblies.&lt;/li&gt;
&lt;li&gt;You need to &lt;strong&gt;prevent delivery&lt;/strong&gt; to actual users during development from your dev environment. You need environment-specific logic.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can surely address each hurdle. Your one-liner grows into 10, 100, 1000 lines of code.&lt;/p&gt;

&lt;p&gt;It may grow organically, adapted at every notification-sending point. Or structured - and get you to build your own DIY notification framework.&lt;/p&gt;
&lt;h1&gt;
  
  
  Notification system
&lt;/h1&gt;

&lt;p&gt;You &lt;em&gt;may&lt;/em&gt; build your DIY notification system, experiencing and overcoming a problem after the other.&lt;/p&gt;

&lt;p&gt;But why go through the pain if the work is already done?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://tattler.dev"&gt;Tattler&lt;/a&gt; solves all the problems above and more. It's a lightweight service you deploy within minutes, and your notification one-liners stay a one-liner:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;tattler.client.tattler_py&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;send_notification&lt;/span&gt;

&lt;span class="c1"&gt;# trigger notification in Python code
&lt;/span&gt;&lt;span class="nf"&gt;send_notification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;website&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;password_changed&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;You can call tattler from any language and tech stack -- with one call to its REST API:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# trigger notification via REST API in any language&lt;/span&gt;
curl &lt;span class="nt"&gt;-XPOST&lt;/span&gt; http://localhost:11503/notification/website/password_changed?user&lt;span class="o"&gt;=&lt;/span&gt;foo@bar.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Notification templates look like this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jinja"&gt;&lt;code&gt;Hey &lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;user_firstname&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;! 👋🏻

A quick heads up that your password was changed.

If it was you, all good. If it wasn't, reply to this email without delay.

&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nv"&gt;user_account_type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'premium'&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
P.S.: Thank you for supporting us with your premium account! 🤩
&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endif&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h1&gt;
  
  
  Deploying tattler
&lt;/h1&gt;

&lt;p&gt;Install Tattler into an own folder &lt;code&gt;~/tattler_quickstart&lt;/code&gt;, with this structure:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/tattler_quickstart/
├── conf/         # configuration files
├── templates/    # templates for notifications
└── venv/         # python virtual environment holding the code
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Here's a terminal session performing the steps in this guide:&lt;/p&gt;


&lt;div class="ltag_asciinema"&gt;
  
&lt;/div&gt;



&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;p&gt;Create this directory structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# create the directory structure above&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/tattler_quickstart
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/tattler_quickstart
&lt;span class="nb"&gt;mkdir &lt;/span&gt;conf templates

&lt;span class="c"&gt;# create and load a virtualenv to install into&lt;/span&gt;
python3 &lt;span class="nt"&gt;-m&lt;/span&gt; venv ~/tattler_quickstart/venv
&lt;span class="nb"&gt;.&lt;/span&gt; ~/tattler_quickstart/venv/bin/activate

&lt;span class="c"&gt;# install tattler into it&lt;/span&gt;
pip &lt;span class="nb"&gt;install &lt;/span&gt;tattler
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Configuration
&lt;/h2&gt;

&lt;p&gt;Tattler's configuration is organized in a bunch of files (envdir), whose filename is the configuration key and content its value. We'll configure the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/tattler_quickstart/
└── conf/
    ├── TATTLER_MASTER_MODE    # actually deliver notifications
    ├── TATTLER_SMTP_ADDRESS   # IP:port of the SMTP server
    └── TATTLER_SMTP_AUTH      # username:password SMTP credentials
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's do it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/tattler_quickstart/conf

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'production'&lt;/span&gt;   &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; TATTLER_MASTER_MODE

&lt;span class="c"&gt;# replace with your SMTP server&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'127.0.0.1:25'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; TATTLER_SMTP_ADDRESS

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'username:password'&lt;/span&gt;  &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; TATTLER_SMTP_AUTH
&lt;span class="nb"&gt;chmod &lt;/span&gt;400 TATTLER_SMTP_AUTH
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Running tattler
&lt;/h1&gt;

&lt;p&gt;At this point tattler is ready to run, so let's!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# run this from the terminal where you loaded your virtual environment&lt;/span&gt;

envdir ~/tattler_quickstart/conf tattler_server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And tattler will confirm:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INFO:tattler.server.pluginloader:Loading plugin PassThroughAddressbookPlugin (&amp;lt;class 'passthrough_addressbook_tattler_plugin.PassThroughAddressbookPlugin'&amp;gt;) from module passthrough_addressbook_tattler_plugin
INFO:tattler.server.tattlersrv_http:Using templates from /../tattler_quickstart/templates
INFO:tattler.server.tattlersrv_http:==&amp;gt; Meet tattler @ https://tattler.dev . If you like tattler, consider posting about it! ;-)
WARNING:tattler.server.tattlersrv_http:Tattler enterprise now serving at 127.0.0.1:11503
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Done! ✅&lt;/p&gt;

&lt;h2&gt;
  
  
  Sending a test notification
&lt;/h2&gt;

&lt;p&gt;Ask tattler to send a test notification with the &lt;code&gt;tattler_notify&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#              [ recipient ]  [ scope ]  [ event ]  [ do send! ]&lt;/span&gt;

tattler_notify my@email.com   demoscope  demoevent  &lt;span class="nt"&gt;-m&lt;/span&gt; production
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This sends a demo notification embedded in tattler, so you don't need to write your own content. &lt;code&gt;-m production&lt;/code&gt; tells tattler to deliver to the actual recipient &lt;code&gt;my@email.com&lt;/code&gt;. Without it, tattler will safely operate in development mode and divert any delivery to a development address (configured in &lt;code&gt;TATTLER_DEBUG_RECIPIENT_EMAIL&lt;/code&gt;) so real users aren’t accidentally sent emails during development.&lt;/p&gt;

&lt;p&gt;Proceed to your mailbox to find the result. If nothing is in, check the logs of &lt;code&gt;tattler_server&lt;/code&gt;. They should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[...]
INFO:tattler.server.sendable.vector_email:SMTP delivery to 127.0.0.1:25 completed successfully.
INFO:tattler.server.tattlersrv_http:Notification sent. [{'id': 'email:b386e396-7ad4-4d50-bc2c-1406bf6a8814', 'vector': 'email', 'resultCode': 0, 'result': 'success', 'detail': 'OK'}]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If they don't, you'll see an error description. Most likely your SMTP server is misconfigured, so check again Configuration and restart &lt;code&gt;tattler_server&lt;/code&gt; when fixed.&lt;/p&gt;

&lt;p&gt;Next, you'll want to write your own content to notify users.&lt;/p&gt;

&lt;h1&gt;
  
  
  Templates
&lt;/h1&gt;

&lt;p&gt;Write your own template to define what content to send upon an event like "password_changed".&lt;/p&gt;

&lt;p&gt;Templates are folders organized in this directory structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/tattler_quickstart/
└── templates/                  # base directory holding all notification templates
    └── website/                # an arbitrary 'scope' (ignore for now)
        ├── password_changed/   # template to send when user changes password
        ├── order_placed/       # template to send when user places an order
        └── ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What does the content of the event template itself look like?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;password_changed/         # arbitrary event name for the template
└── email/
    ├── body.html         # template to expand for the HTML body
    ├── body.txt          # template to expand for the plain text body
    └── subject.txt       # template to expand for the subject
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's proceed to create the directories for one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/tattler_quickstart

&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; templates/password_changed/email
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Plain text template
&lt;/h2&gt;

&lt;p&gt;Now, edit file &lt;code&gt;email/body.txt&lt;/code&gt; with the content of the plain text email. This is seen by users of webmails or email applications that lack support for HTML emails.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jinja"&gt;&lt;code&gt;&lt;span class="c"&gt;{# file email/body.txt (this is a comment) #}&lt;/span&gt;
Hey &lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;user_firstname&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;! 👋🏻

A quick heads up that your password was changed.

If it was you, all good. If it wasn't, reply to this email without delay.

&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nv"&gt;user_account_type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'premium'&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
P.S.: Thank you for supporting us with your premium account! 🤩
&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endif&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Subject
&lt;/h2&gt;

&lt;p&gt;Next, edit file &lt;code&gt;email/subject.txt&lt;/code&gt; with the subject of the email:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jinja"&gt;&lt;code&gt;Warning: password changed for account &lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;user_email&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;! ⚠️
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that the subject supports both non-ASCII characters and templating too! 😎&lt;/p&gt;

&lt;h2&gt;
  
  
  HTML template
&lt;/h2&gt;

&lt;p&gt;Then, edit file &lt;code&gt;email/body.html&lt;/code&gt; with HTML content. Here's where you implement your company's stationery -- colors, logo, fonts, layout and all.&lt;/p&gt;

&lt;p&gt;Make sure to stick to HTML constructs supported by email clients! That's MUCH less than your browser does. See &lt;a href="https://caniemail.com"&gt;caniemail.com&lt;/a&gt; for help.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jinja"&gt;&lt;code&gt;&lt;span class="c"&gt;{# file email/body.html (this is a comment) #}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&amp;lt;title&amp;gt;&lt;/span&gt;Your password was changed!&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Dear &lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;user_firstname&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;! 👋🏻&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;A quick heads up that &lt;span class="nt"&gt;&amp;lt;strong&amp;gt;&lt;/span&gt;your password was changed&lt;span class="nt"&gt;&amp;lt;/strong&amp;gt;&lt;/span&gt;!&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;If it was you, all good. If it wasn't, reply to this email without delay.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;

    &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nv"&gt;user_account_type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'premium'&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"color: darkgray"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;P.S.: Thank you for supporting us with your premium account! 🤩&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
    &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endif&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Send your template
&lt;/h2&gt;

&lt;p&gt;Now that you've got your own template, tell tattler to deliver it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#              [ recipient ]  [ scope ]  [ event ]       [ do send! ]&lt;/span&gt;

tattler_notify my@email.com   website   password_changed  &lt;span class="nt"&gt;-m&lt;/span&gt; production
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check your inbox and that's it!&lt;/p&gt;

&lt;h1&gt;
  
  
  Integration
&lt;/h1&gt;

&lt;p&gt;Now that tattler runs and you provisioned your template, how to have it sent from your code?&lt;/p&gt;

&lt;p&gt;Whenever your application wants to notify an event, like "password changed", you can fire it either:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;From python via tattler's native client API.&lt;/li&gt;
&lt;li&gt;From any other language via tattler's REST API.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's look at some concrete examples.&lt;/p&gt;

&lt;h2&gt;
  
  
  Python
&lt;/h2&gt;

&lt;p&gt;Tattler is written in python, so python developers can use its native python API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;tattler.client.tattler_py&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;send_notification&lt;/span&gt;

&lt;span class="nf"&gt;send_notification&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;website&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;password_changed&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  REST API
&lt;/h2&gt;

&lt;p&gt;This works with any language and tech stack. Simply make a POST request to &lt;code&gt;127.0.0.1:80&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://localhost:11503/notification/website/password_changed?user=foo@bar.com

[           API base URL          ] [scope] [event name]    [ recipient ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Host &lt;code&gt;localhost&lt;/code&gt; and TCP port &lt;code&gt;11503&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Base URL of the API = &lt;code&gt;http://localhost:11503/notification/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;POST request (not GET)!&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;website&lt;/code&gt; is the scope name (see Templates)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;password_changed&lt;/code&gt; is the template name (see Templates)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;user=foo@bar.com&lt;/code&gt; tells tattler whom to send to&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s how to leverage this from a number of programming languages.&lt;/p&gt;

&lt;h2&gt;
  
  
  Go
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"bytes"&lt;/span&gt;
    &lt;span class="s"&gt;"net/http"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"POST"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"http://localhost:11503/notification/website/password_changed?user=my@email.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewBufferString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  C
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System.Net.Http&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Program&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HttpClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;PostAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"http://localhost:11503/notification/website/password_changed?user=my@email.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Java
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.net.URI&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.net.http.HttpClient&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.net.http.HttpRequest&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;App&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;HttpClient&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;HttpClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newHttpClient&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="nc"&gt;HttpRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;HttpRequest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newBuilder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;uri&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="no"&gt;URI&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"http://localhost:11503/notification/website/password_changed?user=my@email.com"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;header&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Content-Type"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"application/json"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;POST&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpRequest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;BodyPublishers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;noBody&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

    &lt;span class="nc"&gt;HttpResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;send&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;HttpResponse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;BodyHandlers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofString&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Swift
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Foundation&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;string&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"http://localhost:11503/notification/website/password_changed?user=my@email.com"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;URLRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;httpMethod&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"POST"&lt;/span&gt;
&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;forHTTPHeaderField&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Content-Type"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;httpBody&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;URLSession&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;shared&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dataTask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;httpResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="k"&gt;as?&lt;/span&gt; &lt;span class="kt"&gt;HTTPURLResponse&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Response Code: &lt;/span&gt;&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="n"&gt;httpResponse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;statusCode&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resume&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;That's it. You installed and tested tattler within minutes, and integrated it into your code within a few more minutes.&lt;/p&gt;

&lt;p&gt;As your needs grow, tattler smoothly accommodates them without bloating your codebase.&lt;/p&gt;

&lt;p&gt;Advanced topics include passing variables to templates, sending SMS, having tattler plug-ins automatically retrieve user addresses or template variables. Find it all in &lt;a href="https://docs.tattler.dev"&gt;tattler's thorough documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Tattler is &lt;a href="https://github.com/tattler-community/tattler-community"&gt;proudly open-source&lt;/a&gt;, with a vastly liberal license (BSD) allowing commercial use. And if your company's policies require support and maintenance for every dependency, tattler offers them in an &lt;a href="https://tattler.dev/#enterprise"&gt;enterprise subscription&lt;/a&gt;, plus more features like S/MIME, multilingualism and delivery with Telegram and WhatsApp.&lt;/p&gt;

</description>
      <category>notifications</category>
      <category>email</category>
      <category>sms</category>
      <category>python</category>
    </item>
  </channel>
</rss>
