<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
    <channel>
      <title>EuroMail Blog</title>
      <link>https://euromail.dev/blog</link>
      <description>Guides, tutorials, and insights on GDPR-compliant email delivery for European developers.</description>
      <generator>Zola</generator>
      <language>en</language>
      <atom:link href="https://euromail.dev/blog/rss.xml" rel="self" type="application/rss+xml"/>
      <lastBuildDate>Sat, 04 Apr 2026 00:00:00 +0000</lastBuildDate>
      <item>
          <title>Send Your First Email in 5 Minutes</title>
          <pubDate>Sat, 04 Apr 2026 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://euromail.dev/blog/send-first-email-five-minutes/</link>
          <guid>https://euromail.dev/blog/send-first-email-five-minutes/</guid>
          <description xml:base="https://euromail.dev/blog/send-first-email-five-minutes/">&lt;p&gt;This guide takes you from zero to a delivered email. No configuration files. No infrastructure. Just an API key and a few lines of code.&lt;&#x2F;p&gt;
&lt;p&gt;By the end, you&#x27;ll have sent a real email through euromail&#x27;s SMTP engine with DKIM signing, SPF alignment, and bounce tracking, all running from Finnish infrastructure.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-you-ll-need&quot;&gt;What you&#x27;ll need&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;An email address for registration&lt;&#x2F;li&gt;
&lt;li&gt;A domain you control (for DNS verification)&lt;&#x2F;li&gt;
&lt;li&gt;A terminal with &lt;code&gt;curl&lt;&#x2F;code&gt;, or a project using TypeScript or Rust&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;step-1-create-your-account&quot;&gt;Step 1: Create your account&lt;&#x2F;h2&gt;
&lt;p&gt;Go to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;dashboard.euromail.dev&#x2F;register&quot;&gt;dashboard.euromail.dev&#x2F;register&lt;&#x2F;a&gt; and sign up with your email, a password, and your company or project name.&lt;&#x2F;p&gt;
&lt;p&gt;You&#x27;ll receive a verification email. Click the link to activate your account.&lt;&#x2F;p&gt;
&lt;p&gt;The free plan gives you &lt;strong&gt;1,000 emails per month&lt;&#x2F;strong&gt;, one domain, five templates, and two webhooks. No credit card required.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;step-2-get-your-api-key&quot;&gt;Step 2: Get your API key&lt;&#x2F;h2&gt;
&lt;p&gt;After verifying your email, the dashboard shows your onboarding page with a freshly generated API key. It looks like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #24292E; background-color: #FFFFFF;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;em_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ012345&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Copy it now. The full key is only shown once. You can always create new keys from the dashboard under API Keys.&lt;&#x2F;p&gt;
&lt;p&gt;You&#x27;ll pass this key in the &lt;code&gt;X-EuroMail-Api-Key&lt;&#x2F;code&gt; header on every request.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;step-3-register-your-sending-domain&quot;&gt;Step 3: Register your sending domain&lt;&#x2F;h2&gt;
&lt;p&gt;Before sending email, you need to tell euromail which domain you&#x27;re sending from. If your emails come from &lt;code&gt;hello@yourdomain.com&lt;&#x2F;code&gt;, register &lt;code&gt;yourdomain.com&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;via-the-dashboard&quot;&gt;Via the dashboard&lt;&#x2F;h3&gt;
&lt;p&gt;The onboarding wizard has a domain field. Type your domain name and click &quot;Add domain.&quot;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;via-the-api&quot;&gt;Via the API&lt;&#x2F;h3&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #24292E; background-color: #FFFFFF;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;curl&lt;&#x2F;span&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt; -X&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; POST https:&#x2F;&#x2F;api.euromail.dev&#x2F;v1&#x2F;domains&lt;&#x2F;span&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt;  -H&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; &amp;quot;X-EuroMail-Api-Key: em_live_your_key_here&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt;  -H&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; &amp;quot;Content-Type: application&#x2F;json&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt;  -d&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; &amp;#39;{&amp;quot;domain&amp;quot;: &amp;quot;yourdomain.com&amp;quot;}&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The response includes DNS records you&#x27;ll need to configure:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #24292E; background-color: #FFFFFF;&quot;&gt;&lt;code data-lang=&quot;json&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt;  &amp;quot;data&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt;    &amp;quot;id&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; &amp;quot;d4e5f6...&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt;    &amp;quot;domain&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; &amp;quot;yourdomain.com&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt;    &amp;quot;dns_records&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt;      &amp;quot;dkim&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt;        &amp;quot;type&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; &amp;quot;TXT&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt;        &amp;quot;host&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; &amp;quot;euromail._domainkey.em.yourdomain.com&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt;        &amp;quot;value&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; &amp;quot;v=DKIM1; k=rsa; p=MIIBIjANBg...&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt;      &amp;quot;spf&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt;        &amp;quot;type&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; &amp;quot;TXT&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt;        &amp;quot;host&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; &amp;quot;em.yourdomain.com&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt;        &amp;quot;value&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; &amp;quot;v=spf1 include:spf.euromail.dev ~all&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt;      &amp;quot;return_path&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt;        &amp;quot;type&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; &amp;quot;MX&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt;        &amp;quot;host&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; &amp;quot;em.yourdomain.com&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt;        &amp;quot;value&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; &amp;quot;bounce.euromail.dev&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt;        &amp;quot;priority&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt; 10&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Notice that all records live under the &lt;code&gt;em.&lt;&#x2F;code&gt; subdomain. This isolates your transactional email reputation from your root domain, so a delivery issue with marketing email won&#x27;t affect your password reset deliverability (or vice versa).&lt;&#x2F;p&gt;
&lt;h2 id=&quot;step-4-send-your-first-email-sandbox-mode&quot;&gt;Step 4: Send your first email (sandbox mode)&lt;&#x2F;h2&gt;
&lt;p&gt;Here&#x27;s the trick: you don&#x27;t have to wait for DNS verification to test the API. Euromail has &lt;strong&gt;sandbox mode&lt;&#x2F;strong&gt;. When your domain isn&#x27;t verified yet, you can send emails to your own account email address. The email goes through the full pipeline (queue, DKIM signing, SMTP delivery) but is restricted to your address only.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;using-curl&quot;&gt;Using cURL&lt;&#x2F;h3&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #24292E; background-color: #FFFFFF;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;curl&lt;&#x2F;span&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt; -X&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; POST https:&#x2F;&#x2F;api.euromail.dev&#x2F;v1&#x2F;emails&lt;&#x2F;span&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt;  -H&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; &amp;quot;X-EuroMail-Api-Key: em_live_your_key_here&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt;  -H&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; &amp;quot;Content-Type: application&#x2F;json&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt;  -d&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; &amp;#39;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #032F62;&quot;&gt;    &amp;quot;from&amp;quot;: &amp;quot;hello@yourdomain.com&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #032F62;&quot;&gt;    &amp;quot;to&amp;quot;: &amp;quot;your-account@email.com&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #032F62;&quot;&gt;    &amp;quot;subject&amp;quot;: &amp;quot;My first euromail&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #032F62;&quot;&gt;    &amp;quot;html_body&amp;quot;: &amp;quot;&amp;lt;h1&amp;gt;It works!&amp;lt;&#x2F;h1&amp;gt;&amp;lt;p&amp;gt;Sent from Finland.&amp;lt;&#x2F;p&amp;gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #032F62;&quot;&gt;  }&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If everything is correct, you&#x27;ll get a &lt;code&gt;202 Accepted&lt;&#x2F;code&gt; response:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #24292E; background-color: #FFFFFF;&quot;&gt;&lt;code data-lang=&quot;json&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt;  &amp;quot;data&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt;    &amp;quot;id&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; &amp;quot;a1b2c3d4-...&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt;    &amp;quot;message_id&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; &amp;quot;&amp;lt;a1b2c3d4@euromail.dev&amp;gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt;    &amp;quot;status&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; &amp;quot;queued&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt;    &amp;quot;to&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; &amp;quot;your-account@email.com&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt;    &amp;quot;sandbox&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt; true&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt;    &amp;quot;created_at&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; &amp;quot;2026-04-04T12:00:00Z&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;&quot;sandbox&quot;: true&lt;&#x2F;code&gt; flag tells you the domain isn&#x27;t verified yet. The email still delivers to your address. Check your inbox.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;using-typescript&quot;&gt;Using TypeScript&lt;&#x2F;h3&gt;
&lt;p&gt;Install the SDK:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #24292E; background-color: #FFFFFF;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;npm&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; install @euromail&#x2F;sdk&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Send an email:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #24292E; background-color: #FFFFFF;&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; { EuroMail }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt; from&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; &amp;quot;@euromail&#x2F;sdk&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt; euromail&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt; = new&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt; EuroMail&lt;&#x2F;span&gt;&lt;span&gt;({&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  apiKey:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; &amp;quot;em_live_your_key_here&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;});&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt; result&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt; = await&lt;&#x2F;span&gt;&lt;span&gt; euromail.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;sendEmail&lt;&#x2F;span&gt;&lt;span&gt;({&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  from:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; &amp;quot;hello@yourdomain.com&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  to:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; &amp;quot;your-account@email.com&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  subject:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; &amp;quot;My first euromail&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  html_body:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; &amp;quot;&amp;lt;h1&amp;gt;It works!&amp;lt;&#x2F;h1&amp;gt;&amp;lt;p&amp;gt;Sent from Finland.&amp;lt;&#x2F;p&amp;gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;});&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;console.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt;`Email queued: ${&lt;&#x2F;span&gt;&lt;span&gt;result&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt;}`&lt;&#x2F;span&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;console.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt;`Sandbox: ${&lt;&#x2F;span&gt;&lt;span&gt;result&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;sandbox&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt;}`&lt;&#x2F;span&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;using-rust&quot;&gt;Using Rust&lt;&#x2F;h3&gt;
&lt;p&gt;Add the dependency:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #24292E; background-color: #FFFFFF;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;cargo&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; add euromail&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Send an email:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #24292E; background-color: #FFFFFF;&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;use&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt; euromail&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;EuroMail&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt; SendEmailParams&lt;&#x2F;span&gt;&lt;span&gt;};&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#[tokio&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;main]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;async fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt; main&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt; Result&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;(),&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt; Box&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;dyn&lt;&#x2F;span&gt;&lt;span&gt; std&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span&gt;error&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;Error&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&amp;gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span&gt; client&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt; EuroMail&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;new&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt;&amp;quot;em_live_your_key_here&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span&gt; response&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; client&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;send_email&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;SendEmailParams&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        from&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; &amp;quot;hello@yourdomain.com&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;into&lt;&#x2F;span&gt;&lt;span&gt;(),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        to&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; &amp;quot;your-account@email.com&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;into&lt;&#x2F;span&gt;&lt;span&gt;(),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        subject&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt; Some&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt;&amp;quot;My first euromail&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;into&lt;&#x2F;span&gt;&lt;span&gt;()),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        html_body&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt; Some&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt;&amp;quot;&amp;lt;h1&amp;gt;It works!&amp;lt;&#x2F;h1&amp;gt;&amp;lt;p&amp;gt;Sent from Finland.&amp;lt;&#x2F;p&amp;gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;into&lt;&#x2F;span&gt;&lt;span&gt;()),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;        ..&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;Default&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;default&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    })&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;.await?&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;    println!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt;&amp;quot;Email queued: {}&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, response&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;id);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;    Ok&lt;&#x2F;span&gt;&lt;span&gt;(())&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;step-5-verify-your-domain&quot;&gt;Step 5: Verify your domain&lt;&#x2F;h2&gt;
&lt;p&gt;Sandbox mode is useful for testing, but to send to real recipients you need to configure DNS. Add the three records from Step 3 to your DNS provider:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Record&lt;&#x2F;th&gt;&lt;th&gt;Type&lt;&#x2F;th&gt;&lt;th&gt;Host&lt;&#x2F;th&gt;&lt;th&gt;Value&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;DKIM&lt;&#x2F;td&gt;&lt;td&gt;TXT&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;euromail._domainkey.em.yourdomain.com&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;v=DKIM1; k=rsa; p=...&lt;&#x2F;code&gt; (from dashboard)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;SPF&lt;&#x2F;td&gt;&lt;td&gt;TXT&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;em.yourdomain.com&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;v=spf1 include:spf.euromail.dev ~all&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Return path&lt;&#x2F;td&gt;&lt;td&gt;MX&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;em.yourdomain.com&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;bounce.euromail.dev&lt;&#x2F;code&gt; (priority 10)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;After adding the records, verify them:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #24292E; background-color: #FFFFFF;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;curl&lt;&#x2F;span&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt; -X&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; POST https:&#x2F;&#x2F;api.euromail.dev&#x2F;v1&#x2F;domains&#x2F;YOUR_DOMAIN_ID&#x2F;verify&lt;&#x2F;span&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt;  -H&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; &amp;quot;X-EuroMail-Api-Key: em_live_your_key_here&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;DNS propagation typically takes 5 to 30 minutes. If verification fails, wait a few minutes and try again. The dashboard shows real-time status for each record.&lt;&#x2F;p&gt;
&lt;p&gt;Once verified, the &lt;code&gt;sandbox&lt;&#x2F;code&gt; flag disappears and you can send to any recipient.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-happens-when-you-send&quot;&gt;What happens when you send&lt;&#x2F;h2&gt;
&lt;p&gt;When your email hits &lt;code&gt;POST &#x2F;v1&#x2F;emails&lt;&#x2F;code&gt;, here&#x27;s the pipeline it goes through:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Validation&lt;&#x2F;strong&gt; ... the API checks the sender domain, recipient addresses, body size, and your plan quota&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Queue&lt;&#x2F;strong&gt; ... the email is added to a Redis stream and returns &lt;code&gt;202 Accepted&lt;&#x2F;code&gt; immediately&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;DKIM signing&lt;&#x2F;strong&gt; ... the worker signs the message with your domain&#x27;s RSA-2048 key&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;SMTP delivery&lt;&#x2F;strong&gt; ... direct connection to the recipient&#x27;s MX server, with STARTTLS and optional DANE verification&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Bounce handling&lt;&#x2F;strong&gt; ... if the recipient&#x27;s server rejects the email, the bounce is parsed and classified (hard&#x2F;soft)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Webhook notification&lt;&#x2F;strong&gt; ... if you&#x27;ve configured webhooks, you&#x27;ll get a callback for each delivery event&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;The entire pipeline runs in Finland. No data leaves the EU.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;next-steps&quot;&gt;Next steps&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;add-a-webhook&quot;&gt;Add a webhook&lt;&#x2F;h3&gt;
&lt;p&gt;Get notified when emails are delivered, bounced, or marked as spam:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #24292E; background-color: #FFFFFF;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;curl&lt;&#x2F;span&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt; -X&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; POST https:&#x2F;&#x2F;api.euromail.dev&#x2F;v1&#x2F;webhooks&lt;&#x2F;span&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt;  -H&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; &amp;quot;X-EuroMail-Api-Key: em_live_your_key_here&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt;  -H&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; &amp;quot;Content-Type: application&#x2F;json&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt;  -d&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; &amp;#39;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #032F62;&quot;&gt;    &amp;quot;url&amp;quot;: &amp;quot;https:&#x2F;&#x2F;yourdomain.com&#x2F;webhooks&#x2F;euromail&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #032F62;&quot;&gt;    &amp;quot;events&amp;quot;: [&amp;quot;delivered&amp;quot;, &amp;quot;bounced&amp;quot;, &amp;quot;complained&amp;quot;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #032F62;&quot;&gt;  }&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The response includes a &lt;code&gt;secret&lt;&#x2F;code&gt; for HMAC-SHA256 signature verification. Store it securely.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;use-templates&quot;&gt;Use templates&lt;&#x2F;h3&gt;
&lt;p&gt;Instead of sending raw HTML every time, create a reusable template:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #24292E; background-color: #FFFFFF;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;curl&lt;&#x2F;span&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt; -X&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; POST https:&#x2F;&#x2F;api.euromail.dev&#x2F;v1&#x2F;templates&lt;&#x2F;span&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt;  -H&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; &amp;quot;X-EuroMail-Api-Key: em_live_your_key_here&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt;  -H&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; &amp;quot;Content-Type: application&#x2F;json&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt;  -d&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; &amp;#39;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #032F62;&quot;&gt;    &amp;quot;alias&amp;quot;: &amp;quot;welcome&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #032F62;&quot;&gt;    &amp;quot;name&amp;quot;: &amp;quot;Welcome Email&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #032F62;&quot;&gt;    &amp;quot;subject&amp;quot;: &amp;quot;Welcome, {{ name }}!&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #032F62;&quot;&gt;    &amp;quot;html_body&amp;quot;: &amp;quot;&amp;lt;h1&amp;gt;Hello {{ name }}&amp;lt;&#x2F;h1&amp;gt;&amp;lt;p&amp;gt;Your account is ready.&amp;lt;&#x2F;p&amp;gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #032F62;&quot;&gt;  }&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then send with the template and variables:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #24292E; background-color: #FFFFFF;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;curl&lt;&#x2F;span&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt; -X&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; POST https:&#x2F;&#x2F;api.euromail.dev&#x2F;v1&#x2F;emails&lt;&#x2F;span&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt;  -H&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; &amp;quot;X-EuroMail-Api-Key: em_live_your_key_here&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt;  -H&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; &amp;quot;Content-Type: application&#x2F;json&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt;  -d&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; &amp;#39;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #032F62;&quot;&gt;    &amp;quot;from&amp;quot;: &amp;quot;hello@yourdomain.com&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #032F62;&quot;&gt;    &amp;quot;to&amp;quot;: &amp;quot;user@example.com&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #032F62;&quot;&gt;    &amp;quot;template_alias&amp;quot;: &amp;quot;welcome&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #032F62;&quot;&gt;    &amp;quot;template_data&amp;quot;: {&amp;quot;name&amp;quot;: &amp;quot;Alice&amp;quot;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #032F62;&quot;&gt;  }&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Templates use &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.rs&#x2F;minijinja&quot;&gt;MiniJinja&lt;&#x2F;a&gt; syntax: &lt;code&gt;{{ variable }}&lt;&#x2F;code&gt; for interpolation, &lt;code&gt;{% if condition %}&lt;&#x2F;code&gt; for conditionals, &lt;code&gt;{% for item in list %}&lt;&#x2F;code&gt; for loops.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;check-delivery-status&quot;&gt;Check delivery status&lt;&#x2F;h3&gt;
&lt;p&gt;Poll the status of a sent email:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #24292E; background-color: #FFFFFF;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;curl&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; https:&#x2F;&#x2F;api.euromail.dev&#x2F;v1&#x2F;emails&#x2F;YOUR_EMAIL_ID&lt;&#x2F;span&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt;  -H&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; &amp;quot;X-EuroMail-Api-Key: em_live_your_key_here&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;status&lt;&#x2F;code&gt; field progresses through: &lt;code&gt;queued&lt;&#x2F;code&gt; -&amp;gt; &lt;code&gt;processing&lt;&#x2F;code&gt; -&amp;gt; &lt;code&gt;sent&lt;&#x2F;code&gt; -&amp;gt; &lt;code&gt;delivered&lt;&#x2F;code&gt; (or &lt;code&gt;bounced&lt;&#x2F;code&gt; &#x2F; &lt;code&gt;failed&lt;&#x2F;code&gt;).&lt;&#x2F;p&gt;
&lt;h3 id=&quot;explore-the-api&quot;&gt;Explore the API&lt;&#x2F;h3&gt;
&lt;p&gt;The full API reference with an interactive Swagger UI is at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;api.euromail.dev&#x2F;docs&quot;&gt;api.euromail.dev&#x2F;docs&lt;&#x2F;a&gt;. Every endpoint is documented with request&#x2F;response examples.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;common-issues&quot;&gt;Common issues&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;strong&gt;&quot;Domain not verified&quot; error when sending to other people&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Your DNS records aren&#x27;t fully propagated yet. Both DKIM and return path must be verified. Check status in the dashboard or call &lt;code&gt;POST &#x2F;v1&#x2F;domains&#x2F;{id}&#x2F;verify&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;&quot;Authentication required&quot; (401)&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Make sure you&#x27;re passing the API key in the &lt;code&gt;X-EuroMail-Api-Key&lt;&#x2F;code&gt; header (not &lt;code&gt;Authorization&lt;&#x2F;code&gt; without the right prefix). The key must start with &lt;code&gt;em_live_&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;&quot;Monthly quota exceeded&quot; (429)&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The free plan allows 1,000 emails per month. Upgrade to Starter ($25&#x2F;mo) for 25,000 emails at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;dashboard.euromail.dev&quot;&gt;dashboard.euromail.dev&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Email lands in spam&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This usually means DNS isn&#x27;t fully configured. Verify that SPF, DKIM, and return path all show &quot;Verified&quot; in the dashboard. Also check your sending domain&#x27;s reputation with &lt;a href=&quot;&#x2F;blog&#x2F;google-postmaster-tools-monitor-your-email-reputation&#x2F;&quot;&gt;Google Postmaster Tools&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Why We Chose Rust for Transactional Email</title>
          <pubDate>Fri, 03 Apr 2026 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://euromail.dev/blog/why-rust-for-transactional-email/</link>
          <guid>https://euromail.dev/blog/why-rust-for-transactional-email/</guid>
          <description xml:base="https://euromail.dev/blog/why-rust-for-transactional-email/">&lt;p&gt;Most transactional email platforms are built on Node.js, Python, or Go. We built euromail on Rust. Not because it&#x27;s trendy, but because the specific problems in email infrastructure map almost perfectly to Rust&#x27;s strengths: long-lived concurrent connections, cryptographic signing on every message, and zero tolerance for resource leaks under sustained load.&lt;&#x2F;p&gt;
&lt;p&gt;This post walks through the real engineering reasons behind the choice, with concrete examples from our codebase.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-problem-with-email-at-scale&quot;&gt;The Problem with Email at Scale&lt;&#x2F;h2&gt;
&lt;p&gt;Transactional email sounds simple: accept an API call, build a MIME message, connect to an MX server, deliver it. But at scale, the system is doing dozens of things simultaneously:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Maintaining SMTP connections to hundreds of recipient domains&lt;&#x2F;li&gt;
&lt;li&gt;DKIM-signing every message with per-customer RSA keys&lt;&#x2F;li&gt;
&lt;li&gt;Enforcing per-domain rate limits and IP warmup schedules&lt;&#x2F;li&gt;
&lt;li&gt;Processing bounces, complaints, and DMARC reports in real time&lt;&#x2F;li&gt;
&lt;li&gt;Running circuit breakers for domains that are temporarily rejecting mail&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Each of these operations involves shared mutable state, concurrent access, and resources that must be cleaned up correctly. A leaked connection or a missed semaphore release under load doesn&#x27;t just slow things down. It cascades.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ownership-makes-resource-leaks-impossible&quot;&gt;Ownership Makes Resource Leaks Impossible&lt;&#x2F;h2&gt;
&lt;p&gt;The single biggest win from Rust is deterministic resource cleanup. In our SMTP connection pool, each domain gets a semaphore controlling how many concurrent connections we open:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #24292E; background-color: #FFFFFF;&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;pub struct&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt; ConnectionGuard&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    domain&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt; String&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    semaphores&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt; Arc&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;DashMap&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;String&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt; Arc&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;Semaphore&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&amp;gt;&amp;gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;impl&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt; Drop&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt; for&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt; ConnectionGuard&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;    fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt; drop&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;&amp;amp;mut&lt;&#x2F;span&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt; self&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;        if let&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt; Some&lt;&#x2F;span&gt;&lt;span&gt;(sem)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt; self&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;semaphores&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;domain) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            sem&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;add_permits&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;When a &lt;code&gt;ConnectionGuard&lt;&#x2F;code&gt; goes out of scope, whether the delivery succeeds, fails, panics, or the task gets cancelled, the semaphore permit is released. There is no &lt;code&gt;try-finally&lt;&#x2F;code&gt; to forget. No &lt;code&gt;defer&lt;&#x2F;code&gt; to put in the wrong place. The compiler enforces it.&lt;&#x2F;p&gt;
&lt;p&gt;In Node.js, a forgotten &lt;code&gt;finally&lt;&#x2F;code&gt; block under a rare error path means a domain&#x27;s connection slot leaks permanently. We&#x27;ve seen this pattern cause cascading delivery failures in other systems. In Rust, it cannot happen.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;async-without-the-garbage-collector-tax&quot;&gt;Async Without the Garbage Collector Tax&lt;&#x2F;h2&gt;
&lt;p&gt;Our worker process runs 18+ concurrent tasks on the Tokio async runtime: email consumers, bounce processors, webhook dispatchers, retry schedulers, reputation analyzers, and more. Each task is a lightweight future, not a thread.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #24292E; background-color: #FFFFFF;&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; email_consumer&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span&gt; w&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt; Arc&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;clone&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;worker);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;    tokio&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;spawn&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;async move&lt;&#x2F;span&gt;&lt;span&gt; { w&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;run_email_consumer&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;.await&lt;&#x2F;span&gt;&lt;span&gt; })&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;};&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The critical difference from Go or Node.js: there is no garbage collector. Rust allocates and frees memory deterministically. Under sustained load of thousands of emails per second, our memory usage stays flat. There are no GC pauses causing delivery latency spikes.&lt;&#x2F;p&gt;
&lt;p&gt;Benchmarks back this up. In comparable server workloads, Rust uses roughly 15-16 MB of memory where Node.js uses 64 MB. More importantly, Rust&#x27;s latency profile has no tail spikes from garbage collection, a real problem when you&#x27;re holding open SMTP connections that have timeout windows.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;type-safe-state-machines-prevent-logic-bugs&quot;&gt;Type-Safe State Machines Prevent Logic Bugs&lt;&#x2F;h2&gt;
&lt;p&gt;Email delivery has complex state: queued, processing, sent, delivered, bounced, failed. SMTP domains cycle through circuit breaker states: closed, open, half-open. Bounce types are hard, soft, or undetermined. Getting any of these wrong means lost mail or duplicate deliveries.&lt;&#x2F;p&gt;
&lt;p&gt;Rust&#x27;s enum system makes invalid states unrepresentable:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #24292E; background-color: #FFFFFF;&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;pub enum&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt; CircuitState&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;    Closed&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;    Open&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;    HalfOpen&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Pattern matching on these enums is exhaustive. If we add a new state, the compiler flags every function that doesn&#x27;t handle it. In Python or JavaScript, a new status string silently falls through to a default case, and you find out in production.&lt;&#x2F;p&gt;
&lt;p&gt;Our email status, bounce type, plan tier, and operation status types are all enums. The compiler catches category errors that unit tests would miss.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;cryptographic-safety-by-default&quot;&gt;Cryptographic Safety by Default&lt;&#x2F;h2&gt;
&lt;p&gt;Every outgoing email gets DKIM-signed with the customer&#x27;s RSA key, and optionally ARC-sealed. That&#x27;s RSA-2048 cryptographic operations on every single message. The signing pipeline:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #24292E; background-color: #FFFFFF;&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;pub fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt; sign_message&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    message&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;: &amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;u8&lt;&#x2F;span&gt;&lt;span&gt;],&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    domain&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;: &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    selector&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;: &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    private_key_pem&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;: &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt; Result&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;Vec&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;u8&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt; SmtpError&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The key never leaves its scope. There&#x27;s no way to accidentally log it, serialize it, or pass it to the wrong function, because the borrow checker prevents moving the reference outside its intended lifetime.&lt;&#x2F;p&gt;
&lt;p&gt;We use &lt;code&gt;rustls&lt;&#x2F;code&gt; for TLS instead of OpenSSL. That means our TLS implementation is 100% Rust with no C FFI boundary. No buffer overflows. No memory corruption from a mismanaged SSL context. The entire TLS handshake for SMTP STARTTLS is memory-safe by construction.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;lock-free-concurrency-for-rate-limiting&quot;&gt;Lock-Free Concurrency for Rate Limiting&lt;&#x2F;h2&gt;
&lt;p&gt;Email providers enforce per-domain rate limits, per-IP warmup schedules, and per-account quotas. These all require concurrent counters that thousands of tasks read and write simultaneously.&lt;&#x2F;p&gt;
&lt;p&gt;We use &lt;code&gt;DashMap&lt;&#x2F;code&gt; (a lock-free concurrent hashmap) and atomic operations instead of traditional mutexes:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #24292E; background-color: #FFFFFF;&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;pub struct&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt; DomainThrottle&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    limiters&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt; Arc&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;DashMap&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;String&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt; Arc&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;Limiter&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&amp;gt;&amp;gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    rate_per_second&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt; NonZeroU32&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;For IP warmup, hourly send counters use &lt;code&gt;AtomicU64&lt;&#x2F;code&gt; with compare-and-swap:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #24292E; background-color: #FFFFFF;&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;match&lt;&#x2F;span&gt;&lt;span&gt; counter&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;value&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;compare_exchange&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    current,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    current&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt; +&lt;&#x2F;span&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;    Ordering&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;AcqRel&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;    Ordering&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;Relaxed&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt; ...&lt;&#x2F;span&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;No locks. No contention. No poisoned mutex if a thread panics. These patterns are common in Rust&#x27;s ecosystem and almost impossible to get right in languages without an ownership model.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ssrf-protection-at-the-dns-layer&quot;&gt;SSRF Protection at the DNS Layer&lt;&#x2F;h2&gt;
&lt;p&gt;Webhook delivery is a common feature in email platforms: notify the customer&#x27;s server when an email bounces or gets complained about. But webhooks are also a vector for SSRF attacks, where an attacker configures a webhook URL that resolves to an internal IP.&lt;&#x2F;p&gt;
&lt;p&gt;Most implementations validate the IP, then connect. That leaves a TOCTOU (time-of-check to time-of-use) gap where DNS can rebind between validation and connection.&lt;&#x2F;p&gt;
&lt;p&gt;We solve this by implementing Rust&#x27;s &lt;code&gt;Resolve&lt;&#x2F;code&gt; trait for &lt;code&gt;reqwest&lt;&#x2F;code&gt;, making validation and resolution atomic:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #24292E; background-color: #FFFFFF;&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;impl&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt; Resolve&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt; for&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt; SsrfSafeResolver&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;    fn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt; resolve&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;, name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt; Name&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt; Resolving&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;        Box&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;pin&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;async move&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;            let&lt;&#x2F;span&gt;&lt;span&gt; addrs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt; tokio&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;net&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;lookup_host&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt;addr_str)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;.await?&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;            &#x2F;&#x2F; Block private&#x2F;reserved IPs before returning&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;            let&lt;&#x2F;span&gt;&lt;span&gt; blocked&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt; Vec&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;_&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; addrs&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;                .&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;iter&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;                .&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;filter&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span&gt;a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt; is_private_or_reserved&lt;&#x2F;span&gt;&lt;span&gt;(a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;ip&lt;&#x2F;span&gt;&lt;span&gt;()))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;                .&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;collect&lt;&#x2F;span&gt;&lt;span&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;            if !&lt;&#x2F;span&gt;&lt;span&gt;blocked&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;is_empty&lt;&#x2F;span&gt;&lt;span&gt;() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;                return&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt; Err&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;&#x2F;* SSRF blocked *&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;            Ok&lt;&#x2F;span&gt;&lt;span&gt;(addrs)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        })&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;DNS rebinding attacks cannot work because resolution and validation happen in the same step. The HTTP client never sees the private IP.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;structured-error-handling-across-the-stack&quot;&gt;Structured Error Handling Across the Stack&lt;&#x2F;h2&gt;
&lt;p&gt;Our error types form a layered hierarchy using &lt;code&gt;thiserror&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #24292E; background-color: #FFFFFF;&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#[derive(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;Debug&lt;&#x2F;span&gt;&lt;span&gt;, thiserror&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;Error&lt;&#x2F;span&gt;&lt;span&gt;)]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;pub enum&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt; SmtpError&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    #[error(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt;&amp;quot;DNS resolution failed for {domain}: {source}&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;    DnsError&lt;&#x2F;span&gt;&lt;span&gt; { domain&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt; String&lt;&#x2F;span&gt;&lt;span&gt;, source&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt; Box&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;dyn&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt; Error&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt; +&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt; Send&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt; +&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt; Sync&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    #[error(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt;&amp;quot;Circuit open for domain {domain}&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;    CircuitOpen&lt;&#x2F;span&gt;&lt;span&gt; { domain&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt; String&lt;&#x2F;span&gt;&lt;span&gt; },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    #[error(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt;&amp;quot;Warm-up limit reached for IP {ip} (limit: {limit})&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;    WarmupLimitReached&lt;&#x2F;span&gt;&lt;span&gt; { ip&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt; String&lt;&#x2F;span&gt;&lt;span&gt;, limit&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt; u64&lt;&#x2F;span&gt;&lt;span&gt; },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Each error variant carries structured context. When a delivery fails, we know exactly why: DNS failure, circuit breaker tripped, warmup limit hit, permanent SMTP rejection. Metrics can tag by error type. Retry logic can branch on the variant. Alert thresholds differ per category.&lt;&#x2F;p&gt;
&lt;p&gt;In Node.js or Python, this is typically a string message or a generic error code. In Rust, the type system guarantees you handle every case.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-workspace-six-crates-one-build&quot;&gt;The Workspace: Six Crates, One Build&lt;&#x2F;h2&gt;
&lt;p&gt;Our codebase is a Cargo workspace with six crates:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Crate&lt;&#x2F;th&gt;&lt;th&gt;Responsibility&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;euromail-api&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;HTTP API server (Axum)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;euromail-worker&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Background job consumer, retry logic&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;euromail-smtp&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;SMTP delivery, DKIM, ARC, circuit breakers&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;euromail-common&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Shared types, auth, database, queue&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;euromail-dashboard&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Admin dashboard (server-rendered HTML)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;euromail-tests&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Integration test suite&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;All crates share workspace-level dependency versions, preventing version drift. A single &lt;code&gt;cargo build --release&lt;&#x2F;code&gt; compiles all three production binaries (API, worker, dashboard) with shared dependency compilation. Changes to the common crate propagate type-checking across the entire system instantly.&lt;&#x2F;p&gt;
&lt;p&gt;The release profile uses thin LTO and symbol stripping:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #24292E; background-color: #FFFFFF;&quot;&gt;&lt;code data-lang=&quot;toml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;profile&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;release&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;lto =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; &amp;quot;thin&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;strip =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt; true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The API binary compiles to roughly 30 MB stripped. The worker with the full SMTP engine is about 40 MB. These run on &lt;code&gt;debian:bookworm-slim&lt;&#x2F;code&gt; Docker images, keeping the total image size small enough for fast container scheduling on Kubernetes.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;graceful-shutdown-without-lost-messages&quot;&gt;Graceful Shutdown Without Lost Messages&lt;&#x2F;h2&gt;
&lt;p&gt;Email systems cannot afford to lose messages during deploys. Our shutdown sequence uses &lt;code&gt;tokio_util::CancellationToken&lt;&#x2F;code&gt; to coordinate all 18+ worker tasks:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #24292E; background-color: #FFFFFF;&quot;&gt;&lt;code data-lang=&quot;rust&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; messages&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt; tokio&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;select!&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    result&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt; queue&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;::&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;read_messages&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;redis, stream, group,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt; &amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt;self&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;consumer_name,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt; 10&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;        =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; result,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    _&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt; self&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;shutdown&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;cancelled&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt; =&amp;gt; break&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;};&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;When SIGTERM arrives, the token cancels. Every consumer loop breaks at its next &lt;code&gt;select!&lt;&#x2F;code&gt; point. In-flight message processing runs to completion. Then telemetry flushes and the process exits cleanly.&lt;&#x2F;p&gt;
&lt;p&gt;Compare this to Node.js, where &lt;code&gt;process.on(&#x27;SIGTERM&#x27;)&lt;&#x2F;code&gt; gives you a callback but no structured way to drain async work. Or Python, where signal handling in async contexts is notoriously fragile. Rust&#x27;s &lt;code&gt;select!&lt;&#x2F;code&gt; macro makes the shutdown path as explicit and testable as the happy path.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-we-d-choose-differently&quot;&gt;What We&#x27;d Choose Differently&lt;&#x2F;h2&gt;
&lt;p&gt;Rust isn&#x27;t without trade-offs.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Compile times are real.&lt;&#x2F;strong&gt; A clean release build takes about 5 minutes with sccache. Incremental builds are fast (under 30 seconds), but CI cold builds require caching strategies to stay reasonable.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;The learning curve is steep.&lt;&#x2F;strong&gt; Lifetimes, trait bounds, and async pinning take time to internalize. We wouldn&#x27;t recommend Rust for a prototype or a CRUD API. We&#x27;d reach for it again for anything that holds open thousands of concurrent connections and signs cryptographic material on every request.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;The ecosystem is younger.&lt;&#x2F;strong&gt; Some email-specific crates are less mature than their Go or Python equivalents. We&#x27;ve contributed patches upstream and occasionally vendor-forked.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-bottom-line&quot;&gt;The Bottom Line&lt;&#x2F;h2&gt;
&lt;p&gt;We chose Rust because email infrastructure is fundamentally about managing concurrent I&#x2F;O, shared state, and resource lifetimes correctly under sustained load. These are exactly the problems Rust&#x27;s ownership model, async runtime, and type system were designed to solve.&lt;&#x2F;p&gt;
&lt;p&gt;After two years of development, the choice has paid off in ways we expected (no memory leaks, no GC pauses) and ways we didn&#x27;t (the type system catches logic bugs that would be integration test failures in other languages). Our worker process handles thousands of concurrent SMTP connections with flat memory usage and predictable latency, deployed as a 40 MB container image.&lt;&#x2F;p&gt;
&lt;p&gt;If you&#x27;re building infrastructure that&#x27;s long-lived, concurrent, and security-critical, Rust is worth the investment.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Google Postmaster Tools: Monitor Your Email Reputation</title>
          <pubDate>Mon, 23 Mar 2026 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://euromail.dev/blog/gmail-postmaster-tools/</link>
          <guid>https://euromail.dev/blog/gmail-postmaster-tools/</guid>
          <description xml:base="https://euromail.dev/blog/gmail-postmaster-tools/">&lt;p&gt;You spend time crafting your transactional emails, setting up SPF, DKIM, and DMARC, warming up your IP — then an important password reset lands in someone&#x27;s spam folder. What happened?&lt;&#x2F;p&gt;
&lt;p&gt;Without visibility into how Gmail evaluates your sending domain, you&#x27;re flying blind. Google Postmaster Tools is the only way to see your reputation from Gmail&#x27;s perspective — not what your ESP reports, but what Google actually sees.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-google-postmaster-tools-is&quot;&gt;What Google Postmaster Tools Is&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gmail.com&#x2F;postmaster&#x2F;&quot;&gt;Google Postmaster Tools&lt;&#x2F;a&gt; is a free dashboard provided by Google that shows metrics about email you send to Gmail addresses. It covers spam rates, domain and IP reputation, authentication results, encryption, and delivery errors.&lt;&#x2F;p&gt;
&lt;p&gt;Gmail accounts for over 30% of email users worldwide. If you send transactional email — password resets, invoices, shipping notifications — a significant portion of your recipients are on Gmail. Postmaster Tools is your window into whether those emails reach the inbox or disappear into spam.&lt;&#x2F;p&gt;
&lt;p&gt;The tool updated to a V2 interface in late 2025, shifting focus from vague reputation grades to binary compliance checks and spam rate tracking. This aligns with Google&#x27;s stricter sender requirements that took effect in 2024.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;setting-up-postmaster-tools&quot;&gt;Setting Up Postmaster Tools&lt;&#x2F;h2&gt;
&lt;p&gt;Setup takes about 10 minutes, though data needs 24–48 hours to populate.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;step-1-verify-your-domain&quot;&gt;Step 1: Verify Your Domain&lt;&#x2F;h3&gt;
&lt;ol&gt;
&lt;li&gt;Go to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gmail.com&#x2F;postmaster&#x2F;&quot;&gt;gmail.com&#x2F;postmaster&lt;&#x2F;a&gt; and sign in with a Google account&lt;&#x2F;li&gt;
&lt;li&gt;Click the &lt;strong&gt;+&lt;&#x2F;strong&gt; button in the bottom right corner&lt;&#x2F;li&gt;
&lt;li&gt;Enter the domain you send email from (e.g., &lt;code&gt;notifications.yourdomain.com&lt;&#x2F;code&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;Google generates a TXT record — copy it&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h3 id=&quot;step-2-add-the-dns-record&quot;&gt;Step 2: Add the DNS Record&lt;&#x2F;h3&gt;
&lt;p&gt;Add the TXT record to your domain&#x27;s DNS. The exact steps depend on your DNS provider, but the record looks like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #24292E; background-color: #FFFFFF;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;@  TXT  &amp;quot;google-site-verification=abc123...&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If you&#x27;re using EuroMail, you already have DNS records for SPF, DKIM, and DMARC. This verification record sits alongside those — it doesn&#x27;t interfere with authentication.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;step-3-verify-and-wait&quot;&gt;Step 3: Verify and Wait&lt;&#x2F;h3&gt;
&lt;p&gt;Click &lt;strong&gt;Verify&lt;&#x2F;strong&gt; in Postmaster Tools. DNS propagation typically takes a few minutes, but can take up to 48 hours.&lt;&#x2F;p&gt;
&lt;p&gt;Once verified, data starts appearing after Google processes enough messages from your domain. You need a minimum volume of roughly 100–200 daily emails to Gmail for dashboards to populate — Google suppresses metrics below this threshold to protect recipient privacy.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;adding-team-members&quot;&gt;Adding Team Members&lt;&#x2F;h3&gt;
&lt;p&gt;You can grant access to other people on your team. In Postmaster Tools, select your verified domain, click the settings icon, and add their Google account email. They&#x27;ll get read access to all dashboards.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-dashboards&quot;&gt;The Dashboards&lt;&#x2F;h2&gt;
&lt;p&gt;Postmaster Tools provides seven dashboards. Here&#x27;s what each one tells you and what to watch for.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;compliance-status&quot;&gt;Compliance Status&lt;&#x2F;h3&gt;
&lt;p&gt;This is the first dashboard you should check. It shows whether you meet Google&#x27;s sender requirements:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Requirement&lt;&#x2F;th&gt;&lt;th&gt;What it checks&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;SPF&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;Valid SPF record exists and emails pass SPF checks&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;DKIM&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;Emails are signed with a valid DKIM signature&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;From header alignment&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;Your From domain matches your SPF&#x2F;DKIM domain&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;DMARC&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;A DMARC policy is published (at minimum &lt;code&gt;p=none&lt;&#x2F;code&gt;)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;TLS encryption&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;Emails are sent over an encrypted connection&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Spam rate&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;Below 0.3% for bulk senders (5,000+ daily emails)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;Bulk senders also need to support one-click unsubscribe via a &lt;code&gt;List-Unsubscribe&lt;&#x2F;code&gt; header.&lt;&#x2F;p&gt;
&lt;p&gt;For transactional email, most of these should pass automatically if your provider handles authentication correctly. If you see failures here, fix them before looking at any other dashboard.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;spam-rate&quot;&gt;Spam Rate&lt;&#x2F;h3&gt;
&lt;p&gt;This is the single most important metric. It shows the percentage of your emails that Gmail users mark as spam, relative to the total delivered to the inbox.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Thresholds:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Below 0.10%&lt;&#x2F;strong&gt; — Good. You&#x27;re in a healthy range.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;0.10% – 0.30%&lt;&#x2F;strong&gt; — Warning. Investigate which emails are generating complaints.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Above 0.30%&lt;&#x2F;strong&gt; — Critical. Gmail may start throttling or rejecting your messages.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The dashboard also accounts for messages users rescue from spam (mark as &quot;not spam&quot;), which can offset your reported rate.&lt;&#x2F;p&gt;
&lt;p&gt;For transactional email, a high spam rate usually means one of two things: your emails look like marketing to the recipient, or your From address is shared with promotional campaigns. Keep transactional and marketing email on separate domains or subdomains.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;domain-reputation&quot;&gt;Domain Reputation&lt;&#x2F;h3&gt;
&lt;p&gt;Gmail assigns your sending domain a reputation rating based on how recipients interact with your mail:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;High&lt;&#x2F;strong&gt; — Emails are consistently delivered to the inbox&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Medium&lt;&#x2F;strong&gt; — Mostly delivered, but some may be filtered&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Low&lt;&#x2F;strong&gt; — Significant portion goes to spam&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Bad&lt;&#x2F;strong&gt; — Most emails are rejected or sent to spam&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Domain reputation is built over time. Sudden volume spikes, high spam rates, or authentication failures can drop it quickly. Recovery is gradual — consistent, authenticated, low-complaint sending is the only way back.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;ip-reputation&quot;&gt;IP Reputation&lt;&#x2F;h3&gt;
&lt;p&gt;Similar to domain reputation, but evaluated per IP address. This matters if you&#x27;re on dedicated IPs or if your provider uses shared IP pools.&lt;&#x2F;p&gt;
&lt;p&gt;The dashboard shows a stacked bar chart of traffic distribution across reputation levels. You want to see mostly green (High) and some yellow (Medium). Red (Low&#x2F;Bad) bars indicate problems with specific IPs.&lt;&#x2F;p&gt;
&lt;p&gt;If you&#x27;re on shared infrastructure, IP reputation is partly outside your control — your provider&#x27;s sending practices across all customers affect it. This is why choosing a provider with strict anti-abuse policies matters.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;authentication&quot;&gt;Authentication&lt;&#x2F;h3&gt;
&lt;p&gt;This dashboard shows pass rates for SPF, DKIM, and DMARC across all messages sent to Gmail:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;SPF pass rate&lt;&#x2F;strong&gt; — Percentage of messages where the sending IP matches your SPF record&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;DKIM pass rate&lt;&#x2F;strong&gt; — Percentage of messages with a valid DKIM signature&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;DMARC pass rate&lt;&#x2F;strong&gt; — Percentage of messages that pass DMARC alignment (SPF or DKIM aligned with the From domain)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;All three should be at or near 100%. If they&#x27;re not:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Low SPF&lt;&#x2F;strong&gt; — Check that all sending IPs are included in your SPF record. Watch for &lt;code&gt;include&lt;&#x2F;code&gt; chains that exceed the 10-lookup limit.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Low DKIM&lt;&#x2F;strong&gt; — Verify your DKIM key is published correctly and your provider is signing every message.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Low DMARC&lt;&#x2F;strong&gt; — Usually caused by SPF&#x2F;DKIM misalignment. Ensure the domain in your From header matches the domain authenticated by SPF or DKIM.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;encryption&quot;&gt;Encryption&lt;&#x2F;h3&gt;
&lt;p&gt;Shows the percentage of your emails sent using TLS encryption. In 2026, this should be 100% — any modern email provider sends over TLS by default.&lt;&#x2F;p&gt;
&lt;p&gt;If you see anything below 100%, something is misconfigured in your sending infrastructure.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;delivery-errors&quot;&gt;Delivery Errors&lt;&#x2F;h3&gt;
&lt;p&gt;Lists specific error codes and rejection reasons when Gmail refuses your messages. Common errors include:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Rate limit exceeded&lt;&#x2F;strong&gt; — You&#x27;re sending too fast. Implement gradual sending and backoff.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Suspected spam&lt;&#x2F;strong&gt; — Content or reputation triggers. Check your spam rate and domain reputation.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Bad reputation&lt;&#x2F;strong&gt; — IP or domain reputation too low. Focus on authentication and reducing complaints.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;DMARC policy&lt;&#x2F;strong&gt; — Your own DMARC policy caused the rejection (e.g., &lt;code&gt;p=reject&lt;&#x2F;code&gt; with failing authentication).&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;This dashboard is your diagnostic tool when you notice delivery drops — it tells you exactly why Gmail rejected specific messages.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;a-practical-monitoring-routine&quot;&gt;A Practical Monitoring Routine&lt;&#x2F;h2&gt;
&lt;p&gt;You don&#x27;t need to check Postmaster Tools every hour. Here&#x27;s a reasonable cadence:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Weekly:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Check spam rate trend — is it stable, rising, or falling?&lt;&#x2F;li&gt;
&lt;li&gt;Verify authentication pass rates are still at 100%&lt;&#x2F;li&gt;
&lt;li&gt;Glance at domain reputation for any changes&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;After changes:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;New sending domain or subdomain? Monitor compliance status daily for the first week&lt;&#x2F;li&gt;
&lt;li&gt;Changed DNS records? Verify authentication pass rates within 48 hours&lt;&#x2F;li&gt;
&lt;li&gt;Increased sending volume? Watch IP reputation and delivery errors&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;When something breaks:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Delivery rate drops → Check delivery errors dashboard first&lt;&#x2F;li&gt;
&lt;li&gt;Complaints increase → Check spam rate, then review recent email content&lt;&#x2F;li&gt;
&lt;li&gt;Authentication failures → Check authentication dashboard, then verify DNS records&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;postmaster-tools-and-euromail&quot;&gt;Postmaster Tools and EuroMail&lt;&#x2F;h2&gt;
&lt;p&gt;If you&#x27;re using EuroMail, your email authentication is handled automatically — SPF, DKIM, and DMARC are configured during domain setup. Your compliance status dashboard should show all green checks from day one.&lt;&#x2F;p&gt;
&lt;p&gt;Postmaster Tools complements this by giving you ongoing visibility. EuroMail handles the sending infrastructure; Postmaster Tools shows you how Gmail evaluates the result. Together, they give you full control over your transactional email deliverability.&lt;&#x2F;p&gt;
&lt;p&gt;Setting up Postmaster Tools for your EuroMail sending domain takes the same 10 minutes described above. Add the verification TXT record alongside your existing SPF and DKIM records, and you&#x27;ll have direct insight into how your email performs at Gmail&#x27;s front door.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Why Your SaaS Needs an EU-Based Email Provider</title>
          <pubDate>Tue, 03 Mar 2026 00:00:00 +0000</pubDate>
          <author>Unknown</author>
          <link>https://euromail.dev/blog/gdpr-email-delivery-europe/</link>
          <guid>https://euromail.dev/blog/gdpr-email-delivery-europe/</guid>
          <description xml:base="https://euromail.dev/blog/gdpr-email-delivery-europe/">&lt;p&gt;Every time your SaaS sends a password reset, order confirmation, or invoice, that email carries personal data: names, email addresses, sometimes purchase details or account information. If you&#x27;re using a US-based email provider, that data crosses the Atlantic — and with it, your GDPR compliance gets complicated.&lt;&#x2F;p&gt;
&lt;p&gt;For European developers building for European users, there&#x27;s a simpler path: send email from infrastructure that never leaves the EU.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-problem-with-transatlantic-email-data&quot;&gt;The Problem with Transatlantic Email Data&lt;&#x2F;h2&gt;
&lt;p&gt;The GDPR doesn&#x27;t ban sending data outside the EU, but it requires &lt;strong&gt;adequate safeguards&lt;&#x2F;strong&gt; when you do. After the Schrems II ruling invalidated the EU-US Privacy Shield, companies relying on US-based email providers have been in a legal gray area.&lt;&#x2F;p&gt;
&lt;p&gt;The current EU-US Data Privacy Framework attempts to fill that gap, but its future is uncertain. Legal challenges are already underway, and European Data Protection Authorities continue to scrutinize transatlantic data flows.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s what this means in practice for email:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Standard Contractual Clauses (SCCs)&lt;&#x2F;strong&gt; are required for transfers to the US, adding legal overhead and ongoing compliance work&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Transfer Impact Assessments (TIAs)&lt;&#x2F;strong&gt; must evaluate whether the recipient country provides adequate protection&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Your Data Processing Agreement (DPA)&lt;&#x2F;strong&gt; must explicitly address international transfers and sub-processors&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Data subject requests&lt;&#x2F;strong&gt; (access, deletion, portability) become harder when data lives in another jurisdiction&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;For most SaaS teams, email infrastructure is a background concern — until an audit or a customer due diligence questionnaire surfaces these gaps.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-eu-data-residency-actually-means&quot;&gt;What EU Data Residency Actually Means&lt;&#x2F;h2&gt;
&lt;p&gt;EU data residency for email means that every component of the email pipeline processes and stores data within the European Union:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Component&lt;&#x2F;th&gt;&lt;th&gt;What it handles&lt;&#x2F;th&gt;&lt;th&gt;Why residency matters&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;API server&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;Receives your send request with recipient data&lt;&#x2F;td&gt;&lt;td&gt;First point of data ingestion&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Queue &#x2F; worker&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;Processes email jobs, renders templates&lt;&#x2F;td&gt;&lt;td&gt;Personal data in transit&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;SMTP engine&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;Delivers to recipient MX servers&lt;&#x2F;td&gt;&lt;td&gt;Outbound connection metadata&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Bounce processor&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;Handles delivery failures and complaints&lt;&#x2F;td&gt;&lt;td&gt;Contains recipient addresses&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Logs &amp;amp; analytics&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;Tracks delivery events&lt;&#x2F;td&gt;&lt;td&gt;Retention-relevant personal data&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Database&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;Stores email records, templates, settings&lt;&#x2F;td&gt;&lt;td&gt;Long-term personal data storage&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;If any of these components runs outside the EU — even a logging sidecar or an analytics pipeline — the entire chain breaks. True data residency requires control over every layer.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;email-authentication-not-just-compliance-deliverability&quot;&gt;Email Authentication: Not Just Compliance, Deliverability&lt;&#x2F;h2&gt;
&lt;p&gt;GDPR compliance is table stakes. What actually determines whether your email reaches the inbox is &lt;strong&gt;email authentication&lt;&#x2F;strong&gt; — and getting it right is non-negotiable in 2026.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;spf-sender-policy-framework&quot;&gt;SPF (Sender Policy Framework)&lt;&#x2F;h3&gt;
&lt;p&gt;SPF tells receiving mail servers which IP addresses are authorized to send email for your domain. Without it, your emails are more likely to land in spam.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #24292E; background-color: #FFFFFF;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;v=spf1 include:spf.euromail.dev -all&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;-all&lt;&#x2F;code&gt; directive means &quot;reject anything not in this list&quot; — a strict policy that builds sender reputation.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;dkim-domainkeys-identified-mail&quot;&gt;DKIM (DomainKeys Identified Mail)&lt;&#x2F;h3&gt;
&lt;p&gt;DKIM adds a cryptographic signature to every outgoing email. The receiving server verifies this signature against a public key published in your DNS. This proves the email wasn&#x27;t tampered with in transit.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #24292E; background-color: #FFFFFF;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;euromail._domainkey.yourdomain.com  TXT  &amp;quot;v=DKIM1; k=rsa; p=MIIBIjAN...&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Your email provider should sign every email automatically — no manual key management on your side.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;dmarc-domain-based-message-authentication&quot;&gt;DMARC (Domain-based Message Authentication)&lt;&#x2F;h3&gt;
&lt;p&gt;DMARC ties SPF and DKIM together and tells receivers what to do when authentication fails. It also enables aggregate reporting so you can monitor your email authentication health.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #24292E; background-color: #FFFFFF;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;_dmarc.yourdomain.com  TXT  &amp;quot;v=DMARC1; p=reject; rua=mailto:dmarc@yourdomain.com&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;A &lt;code&gt;p=reject&lt;&#x2F;code&gt; policy is the gold standard: it tells receiving servers to drop any email that fails authentication.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;why-this-matters-for-deliverability&quot;&gt;Why This Matters for Deliverability&lt;&#x2F;h3&gt;
&lt;p&gt;Google and Yahoo now &lt;strong&gt;require&lt;&#x2F;strong&gt; SPF, DKIM, and DMARC for bulk senders. Even for transactional email, proper authentication directly impacts inbox placement. An EU-based provider with a clean IP reputation and proper authentication gives you the best foundation.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;sending-email-with-the-euromail-api&quot;&gt;Sending Email with the EuroMail API&lt;&#x2F;h2&gt;
&lt;p&gt;Here&#x27;s what sending a transactional email looks like with an EU-based provider that handles authentication automatically:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #24292E; background-color: #FFFFFF;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;curl&lt;&#x2F;span&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt; -X&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; POST https:&#x2F;&#x2F;api.euromail.dev&#x2F;v1&#x2F;emails&lt;&#x2F;span&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt;  -H&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; &amp;quot;X-EuroMail-Api-Key: em_live_...&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt;  -H&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; &amp;quot;Content-Type: application&#x2F;json&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt;  -d&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; &amp;#39;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #032F62;&quot;&gt;    &amp;quot;from&amp;quot;: &amp;quot;notifications@yourdomain.com&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #032F62;&quot;&gt;    &amp;quot;to&amp;quot;: &amp;quot;customer@example.com&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #032F62;&quot;&gt;    &amp;quot;subject&amp;quot;: &amp;quot;Your invoice #1042&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #032F62;&quot;&gt;    &amp;quot;html_body&amp;quot;: &amp;quot;&amp;lt;h1&amp;gt;Invoice Ready&amp;lt;&#x2F;h1&amp;gt;&amp;lt;p&amp;gt;Your invoice for March 2026 is attached.&amp;lt;&#x2F;p&amp;gt;&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #032F62;&quot;&gt;    &amp;quot;tags&amp;quot;: [&amp;quot;invoice&amp;quot;, &amp;quot;billing&amp;quot;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #032F62;&quot;&gt;  }&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;What happens behind the scenes:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;The API server in Finland receives your request&lt;&#x2F;li&gt;
&lt;li&gt;DKIM signature is applied using your domain&#x27;s private key&lt;&#x2F;li&gt;
&lt;li&gt;The SMTP engine delivers directly to the recipient&#x27;s mail server — no third-party relay&lt;&#x2F;li&gt;
&lt;li&gt;Delivery status, bounces, and complaints are tracked and available via webhooks&lt;&#x2F;li&gt;
&lt;li&gt;All data stays within Finland throughout the entire process&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;For TypeScript projects:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #24292E; background-color: #FFFFFF;&quot;&gt;&lt;code data-lang=&quot;typescript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; { EuroMail }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt; from&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; &amp;quot;@euromail&#x2F;sdk&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #005CC5;&quot;&gt; euromail&lt;&#x2F;span&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt; = new&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt; EuroMail&lt;&#x2F;span&gt;&lt;span&gt;({ apiKey:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; &amp;quot;em_live_...&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt; });&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #D73A49;&quot;&gt;await&lt;&#x2F;span&gt;&lt;span&gt; euromail.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6F42C1;&quot;&gt;sendEmail&lt;&#x2F;span&gt;&lt;span&gt;({&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  from:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; &amp;quot;notifications@yourdomain.com&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  to:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; &amp;quot;customer@example.com&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  subject:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; &amp;quot;Your invoice #1042&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  html_body:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; &amp;quot;&amp;lt;h1&amp;gt;Invoice Ready&amp;lt;&#x2F;h1&amp;gt;&amp;lt;p&amp;gt;Your invoice is attached.&amp;lt;&#x2F;p&amp;gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  tags: [&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt;&amp;quot;invoice&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #032F62;&quot;&gt; &amp;quot;billing&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;],&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;});&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;choosing-an-email-provider-what-to-evaluate&quot;&gt;Choosing an Email Provider: What to Evaluate&lt;&#x2F;h2&gt;
&lt;p&gt;When selecting a transactional email provider for a GDPR-conscious SaaS, consider these criteria:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Data residency&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Where are API servers, databases, and SMTP infrastructure located?&lt;&#x2F;li&gt;
&lt;li&gt;Are sub-processors also within the EU?&lt;&#x2F;li&gt;
&lt;li&gt;Is a Data Processing Agreement (DPA) included?&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Email authentication&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Does the provider handle DKIM signing automatically?&lt;&#x2F;li&gt;
&lt;li&gt;Are SPF records provided and documented?&lt;&#x2F;li&gt;
&lt;li&gt;Is DMARC guidance part of the onboarding flow?&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Delivery infrastructure&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Does the provider use its own SMTP engine or relay through a third party?&lt;&#x2F;li&gt;
&lt;li&gt;What IP warm-up options are available?&lt;&#x2F;li&gt;
&lt;li&gt;How are bounces and complaints handled?&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;GDPR tooling&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Can you export all data for a specific email address (data subject access requests)?&lt;&#x2F;li&gt;
&lt;li&gt;Is there a right-to-erasure endpoint?&lt;&#x2F;li&gt;
&lt;li&gt;Are audit logs available for compliance reviews?&lt;&#x2F;li&gt;
&lt;li&gt;Can you configure data retention periods?&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Transparency&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Where does the provider publish its infrastructure details?&lt;&#x2F;li&gt;
&lt;li&gt;Is the DPA publicly available before sign-up?&lt;&#x2F;li&gt;
&lt;li&gt;What sub-processors are listed?&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;beyond-compliance-building-trust&quot;&gt;Beyond Compliance: Building Trust&lt;&#x2F;h2&gt;
&lt;p&gt;GDPR compliance isn&#x27;t just about avoiding fines — though those can reach 4% of annual global revenue. It&#x27;s about building trust with your users. When you can tell your customers that their data never leaves the EU, that every email is cryptographically signed, and that you can fulfill any data subject request within hours, you&#x27;re not just compliant — you&#x27;re competitive.&lt;&#x2F;p&gt;
&lt;p&gt;European B2B customers increasingly require their vendors to demonstrate data residency. Having your email infrastructure in the EU simplifies vendor assessments, shortens sales cycles, and eliminates compliance blockers.&lt;&#x2F;p&gt;
</description>
      </item>
    </channel>
</rss>
