<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Arthur Gladfield</title>
    <description>The latest articles on Forem by Arthur Gladfield (@agladfield).</description>
    <link>https://forem.com/agladfield</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3193722%2F42616680-6e86-4084-854a-3d6c701835fa.png</url>
      <title>Forem: Arthur Gladfield</title>
      <link>https://forem.com/agladfield</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/agladfield"/>
    <language>en</language>
    <item>
      <title>Postc.art: Send Beautiful Postcards by Email</title>
      <dc:creator>Arthur Gladfield</dc:creator>
      <pubDate>Mon, 09 Jun 2025 06:58:27 +0000</pubDate>
      <link>https://forem.com/agladfield/postcart-send-beautiful-postcards-by-email-17j0</link>
      <guid>https://forem.com/agladfield/postcart-send-beautiful-postcards-by-email-17j0</guid>
      <description>&lt;p&gt;This is a submission for the &lt;a href="https://dev.to/challenges/postmark"&gt;Postmark Challenge: Inbox Innovators&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;Postc.art enables you to send beautiful postcards straight from email. No need to remember any passwords or sign into a website. Just fill out an email with the standardized template and send!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F16ufzrh41egv6ee9kpin.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F16ufzrh41egv6ee9kpin.gif" alt="Demo Loop" width="760" height="587"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your recipient have terrible internet connection, an outdated computer, or doesn't trust HTML email content? No problem! Each postcard email also comes with a text/ASCII version of the email to ensure your recipient at least has a vague idea of what your postcard looks like and what you wanted to tell them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl9bs9b6huyuy84rws5ns.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl9bs9b6huyuy84rws5ns.gif" alt="ASCII Loop" width="322" height="592"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(ASCII Version of your postcards!)&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;To try Postc.art out you have two choices:&lt;/p&gt;

&lt;h3&gt;
  
  
  Live Service
&lt;/h3&gt;

&lt;p&gt;You can try out sending a postcard right now straight from your email!&lt;/p&gt;

&lt;p&gt;Create a new email, address it to &lt;code&gt;send@postc.art&lt;/code&gt;, give it a subject, and then implement this template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;To: Name &amp;lt;email@domain.com&amp;gt;
From: Name &amp;lt;email@domain.com&amp;gt;
Artwork: { City | Islands | Lake | Mountains }
Style: { Illustrated | Painting | Photo | Vintage Photo }
Border: { None | Stripes | Lines | Cubes | Artwork }
Font: { Marker | Polite | Retro | Typewriter }
Textured: { Yes | No }
Country: { The ISO2, ISO3, or most common spelling of any recognized countries }
{ Message Content }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Use the Template
&lt;/h4&gt;

&lt;p&gt;&lt;a href="//mailto:send@postc.art?subject=New%20Postcard&amp;amp;body=To:%20Name%20%3Cemail@domain.com%3E%0AFrom:%20Name%20%3Cemail@domain.com%3E%0AArtwork:%0AStyle:%0ABorder:%0AFont:%0ATextured:%0ACountry:%0A%0A"&gt;Click here to draft an email with this template!&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The only required fields is &lt;code&gt;To:&lt;/code&gt;. The message starts when you are done with the fields. If you do not specify &lt;code&gt;From:&lt;/code&gt; the sevice will automatically use your email and name from your email.&lt;/li&gt;
&lt;li&gt;The values within the &lt;code&gt;{}&lt;/code&gt; curly braces are the presently available options.&lt;/li&gt;
&lt;li&gt;If you choose to send emails via the live service please note that you have a balance of &lt;strong&gt;3&lt;/strong&gt; postcards before you are cut off as to avoid racking up my AI bill. Additionally, if you send a postcard and it bounces or is marked as spam you will no longer be able to send postcards.&lt;/li&gt;
&lt;li&gt;If you are still unsure look at some of the example emails provided in the first gif.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If you clone the repository and run the code yourself you can use attachments as artwork so long as they are one of the following formats:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;image/png
image/webp
image/jpeg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, for safety, security, and legal reasons I did not enable attachment artwork in the live service.&lt;/p&gt;

&lt;h3&gt;
  
  
  Running the code on your own
&lt;/h3&gt;

&lt;p&gt;To run the code you MUST HAVE:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A recent-ish version of Go installed
&amp;gt; &lt;a href="https://go.dev/doc/install" rel="noopener noreferrer"&gt;Install Go Guide&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;LibVIPS (an image processing library) installed
&amp;gt; &lt;a href="https://www.libvips.org/install.html" rel="noopener noreferrer"&gt;Install LibVIPS&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To run the project, clone or download the repository, cd into the src directory (&lt;code&gt;cd src&lt;/code&gt;), run &lt;code&gt;go mod download&lt;/code&gt; then you have two choices.&lt;/p&gt;

&lt;p&gt;Ensure you are in the &lt;code&gt;src&lt;/code&gt; directory before continuing otherwise Go will not be able to detect the project.&lt;/p&gt;

&lt;p&gt;If you would just like to see the Postcard generation in action, you can run (AI features are disabled by default):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;go run ./cmd/postcard-demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Otherwise, if you prefer to run the entire server (webhook processing, image uploading, and email sending) from start to finish run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;go run ./cmd/postcart
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To get the full server to run properly you need to pass at least these environment variables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;POSTMARK_SERVER_TOKEN&lt;/code&gt;: The token for the Postmark server you want to use&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;POSTMARK_AUTH_USER&lt;/code&gt;: The webhook basic authentication username to protect your server&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;POSTMARK_AUTH_PASS&lt;/code&gt;: The webhook basic authentication password to protect your server&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;POSTMARK_INBOUND_EMAIL&lt;/code&gt;: The inbound email for which to expect postcards to be addressed to&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;POSTMARK_EMAIL_DOMAIN&lt;/code&gt;: The domain from which to send out other emails on&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Optional Env Values Include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;GCP_CRED_PATH&lt;/code&gt;: The path to your Google Cloud/Vertex AI credentials needed for using Google's Imagen4 image generation AI. See the guide on how to get it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://docs.mindmac.app/how-to.../add-api-key/create-google-cloud-vertex-ai-api-key" rel="noopener noreferrer"&gt;Creating Service Account for Vertex AI Guide&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;IMPORTANT: In addition to giving the service account &lt;code&gt;Vertex AI Service Agent&lt;/code&gt; it must also have a Storage administrative role of some kind that it may to write to buckets with your project if you wish to use the longer storage.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;GCP_PROJECT&lt;/code&gt;: The project you created your service account above with. Make sure it has Vertex AI enabled.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;GCP_BUCKET&lt;/code&gt;: A Google Cloud bucket path for persisting image uploads. If not included &lt;code&gt;tmpfiles.org&lt;/code&gt; will be used instead which only stores the images for 60 minutes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;PORT&lt;/code&gt;: Overrides the http server port (defaults to 8080)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Env Toggles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;INSTALL_FONTS&lt;/code&gt;: If set to &lt;code&gt;true&lt;/code&gt;, will install fonts into the current user (you's) Font directory (untested on Windows but should work). Otherwise, your computer will rollback to seriff fonts if these are not found.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;USE_AI&lt;/code&gt;: If set to &lt;code&gt;true&lt;/code&gt;, will use Google's Imagen AI to generate artwork for postcards. Must be paired with &lt;code&gt;VERTEX_AI_KEY&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ALLOW_ATTACHMENTS&lt;/code&gt;: If set to &lt;code&gt;true&lt;/code&gt;, will enable the processing of attachment images as postcard artwork.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While GoLang does offer environment variable libraries I did not feel those necessary for this project, as such you can find all of the environment variables and logic inside &lt;code&gt;src/pkg/shared/env/env.go&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here is a template you can copy and paste to put your environment variables in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export POSTMARK_SERVER_TOKEN={SERVER_TOKEN}
export POSTMARK_AUTH_USER={SERVER_BASIC_AUTH_USERNAME}
export POSTMARK_AUTH_PASS={SERVER_BASIC_AUTH_PASSWORD}
export POSTMARK_INBOUND_EMAIL={INBOUND_EMAIL}
export POSTMARK_EMAIL_DOMAIN={EMAIL_DOMAIN}
export GCP_CRED_PATH=""
export GCP_PROJECT=""
export GCP_BUCKET=""
USE_AI=false
INSTALL_FONTS=false
ALLOW_ATTACHMENTS=true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will know that everything configured and started correctly if you end up with this screen:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fywpyt4izpwkd7oacxw61.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fywpyt4izpwkd7oacxw61.gif" alt="Good to go splash" width="470" height="382"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If configured correctly, your Postmark server should have a new standard template in it for sending postcards called &lt;code&gt;Deliveries Template&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code Repository
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/agladfield/postcart" rel="noopener noreferrer"&gt;Github Repository&lt;/a&gt; (Should Completely work so long as you followed the Go and LibVIPS installation processes)&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Implementation Process
&lt;/h3&gt;

&lt;p&gt;When I first received the prompt for the challenge I took a little while to think about what sort of information could be conveniently shared via email in a structured format. That is when I landed on the idea that all of the information contained on a postcard could easily be fitted to a standardized email.&lt;/p&gt;

&lt;p&gt;I decided that, since either end-user (the sender or the recipient) deal with the postcards via email only there was no neeed for a traditional front end/website. So first I came up with the design and resources of the postcards (found in the &lt;code&gt;src/pkg/cards/res&lt;/code&gt; folder). Then I set about writing the code to assemble the completed postcard images (&amp;amp; later text/ASCII). Once I was satisfied with how they look and were assembled, I moved on to integrating with Postmark.&lt;/p&gt;

&lt;p&gt;As there is no officially maintained Postmark API Library for GoLang I had to make my own. So included in my code is one I made my own that only includes the features and types used in this program. For instance, I did not implement the default send email endpoint as I only send emails/postcards using templates. Something important to note for anyone unfamiliar with Go evaluating the Postmark code I provided should know is that in Go the names/keys of a struct's properties do not necessarily match up 1 for 1 like they would in say Typescript. Instead, Go uses what are called JSON tags to identify under what string key it should look for a value when turning raw bytes into a structured type. While conveniently Postmark's JSON keys basically universally match up with what Go expects in a struct declaration (PascalCase), there were a few small semantics where the JSON tag differs from the field key in my code such for example: HTMLBody string &lt;code&gt;json:"HtmlBody"&lt;/code&gt;. Go's styling guidelines/LSPs strongly emphasize keeping acronyms capitalized so I followed that convention throughout the program. Having gotten that out of the way, I implemented types for the webhook events I want to receive and process (inbound emails, delivered, bounce, and spam complaint notifications). I then implemented the request and response types and http calls for the API routes I intended to use (templates, inbound rules, send with template).&lt;/p&gt;

&lt;p&gt;Now that I had interfacing with Postmark established, I built the webhook server and inbound email parser. Once I got the parser working and placing the information on the postcards I setup uploading the images to third party services. This way I can provide URLs to display them in emails as I found that they would get cut off if I tried to embed them. To make them work in a cohesive fashion I use Postmark's template feature which allows me to send less information with my email requests and have the postcard images consistently fit edge to edge. All-in-all I am happy with how it turned out.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tech Stack
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Go Lang:&lt;/strong&gt; For robust error handling, performance, large standard library (http server &amp;amp; clients included), and static compilability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LibVIPS:&lt;/strong&gt; A high performance image processing library written in C.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Postmark:&lt;/strong&gt; for email processing, status notifications, templating, and sending.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google's Imagen4:&lt;/strong&gt; for generating beautiful artwork images for the Postcards.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google's Cloud Storage:&lt;/strong&gt; for permanent image storage and embedding.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;tmpfiles.org:&lt;/strong&gt; for quick and easy image upload for embedding the images into sent emails.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Postmark experience
&lt;/h3&gt;

&lt;p&gt;This is my first full implementation of Postmark, I have only previously used it briefly in testing where I observed solid deliverability. Having gotten to test and implement a greater breadth of their services while building this I am (rather unsurprisingly) increasingly pleased with Postmark's ease of use and reliability of their documentation. The only time I ran into any issues with Postmark while building my project was a result of my incorrect implementation and not anything on their end. I am grateful to have chosen Postmark as an email provider because otherwise I would not have heard about this competition and would not have had this idea!&lt;/p&gt;

&lt;h3&gt;
  
  
  Ways I would improve upon my submission
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Adding an actual database:&lt;/em&gt;&lt;/strong&gt; I had originally developed this with Turso/LibSQL but as development progressed the network latency became unbearable and so I had to scrap using it. Moving forward I would probably pair it with something like PostgreSQL.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Splitting up the Postcard Images:&lt;/em&gt;&lt;/strong&gt; It is quick and easy joining the postcard images completely but it would look better if they were rotated and partially overlapping one another for a more realistic, authentic look.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Increasing Option Variety:&lt;/em&gt;&lt;/strong&gt; Expanding configuration offerings. While I am satisfied with the four style, artwork, and font options, I would definitely add many more moving forward.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Adding randomization:&lt;/em&gt;&lt;/strong&gt; Adding random imperfections to the cards to make them feel less cookie cutter.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Instrument it:&lt;/em&gt;&lt;/strong&gt; Right now there's no observability metrics provided by the application. The only way to see how things are going is via the Postmark dashboard. I would implement Prometheus metrics to get a better understanding of performance and usage patterns.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;URL Permanance:&lt;/em&gt;&lt;/strong&gt; The maximum time a signed URL may expire with Google Cloud Storage is 7 days from issuing. This is good for most recipients but in case someone wanted to check on an old postcard they received the embedded URL should last longer than a week.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Smarter Parser:&lt;/em&gt;&lt;/strong&gt; While my current email parser is reasonably fast and reliable, it is rather rigid and I think if I were to do it over I would completely redo the message/text body parser.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Closing note
&lt;/h3&gt;

&lt;p&gt;This is my first ever public release of a programming project I have written. Paid or otherwise. I would appreciate criticisms and other feedbacks as I continue to try to improve. Thanks for your time!&lt;/p&gt;

&lt;p&gt;Additionally, I love this idea so that much that I will be turning it into an actual business. So stay tuned for here or at the website (&lt;a href="https://postc.art" rel="noopener noreferrer"&gt;https://postc.art&lt;/a&gt;) to see it in action.&lt;/p&gt;

&lt;h3&gt;
  
  
  Edit 6/10:
&lt;/h3&gt;

&lt;p&gt;For some reason the example gifs that should be loaded from my site were not loading and so I have fixed this by uploading them here to dev.to.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>postmarkchallenge</category>
      <category>webdev</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
