Notifio is a desktop app that watches rental search pages and emails you when a new listing appears. The app is local, so almost everything the product says to a user arrives as email, and every template lives in one folder on the server:
server/src/lib/emails/ activation.ts 52 lines alert.ts 240 lines free-access.ts 107 lines I have measured these templates before, from two different angles: Gmail clips a message at 102KB, and our alert email runs out of room between 136 and 208 listings was about size, and three of the five text colours in our alert emails fail WCAG AA was about contrast. Four kinds of email, one endpoint was about the dispatch route behind them.
This one is about links, which I had never counted.
The census
$ grep -c '<a href' src/lib/emails/*.ts src/lib/emails/activation.ts:0 src/lib/emails/alert.ts:2 src/lib/emails/free-access.ts:1 Three anchor tags in 399 lines of email template. Here is what each of the six message kinds actually contains:
| Sent by | Anchors | Points at | |
|---|---|---|---|
| Activation code | Stripe webhook | 0 | nothing |
| New listings | /api/notify | 1 | the listing, on the site it was found on |
| Auto-reply sent | /api/notify | 1 | the listing, on the site it was found on |
| Session expired | /api/notify | 0 | nothing |
| Site refusing checks | /api/notify | 0 | nothing |
| Free licence granted | a CLI script | 1 | notifio.app/download |
So of the three links in the product's entire email surface, two take the reader to somebody else's website, and the third is in an email sent by scripts/grant-free-access.mjs, which is how I hand out free copies. The only email with a clickable route back to notifio.app is the one that goes to people who did not pay.
The footer that is not a link
It is not that the domain is missing. It appears seven times:
<p style="margin:0;font-size:12px;color:#404040;">Notifio — notifio.app</p> That line is in the activation email, in all four of the alert emails, and in the free-licence email, and in the plain-text version it is one line of prose. In none of those seven places is it wrapped in an anchor. It is a domain name printed as body text, at 12px, in #404040 on #171717, which is the contrast failure from the earlier post and also, it turns out, the only reference to our website in a message a customer actually receives.
The plain-text parts are worse, and I had not looked at them at all. Every alert type has a real text builder, which is good:
html: buildAuthFailureEmail(payload.authFailureSite), text: buildAuthFailureText(payload.authFailureSite), Four text builders, and the count of notifio.app across all of them is zero. The only URLs in a plain-text Notifio email are listing URLs on other people's sites.
The email that should have a link and does not
The activation email is the one that matters here, because it is the one a buyer gets straight after paying. The Stripe webhook builds it, and its instructions are four numbered steps:
<li>Open Notifio on your computer</li> <li>Enter your email: <strong>${email}</strong></li> <li>Paste the activation token above</li> <li>Click <strong>Activate</strong></li> Every one of those steps happens inside the app. Step one assumes the app is already installed, which is true of someone who bought it from the download page and false of someone who bought it on their phone, or who is setting up a second machine, or who is reinstalling after a disk wipe. The email even anticipates the reinstall case in its own last line: "you'll need this token if you reinstall the app". It does not tell them where the app is.
And if any of that goes wrong, notifio.app/help is the page that answers it. That page is one click from every one of the site's 38 pages and zero clicks from every email we send.
Why it reads like this, which is an explanation and not an excuse
Look at the list of six again and there is a pattern. A new listing happens on a rental site. An activation happens in the app. A session expiring happens in the app. A site refusing checks is resolved in the app. Not one of these events happens on our website, so no template author ever had a reason to link to it.
That is the local-first architecture leaking into the copy, and in most respects it is the correct instinct: a transactional email that tries to drag you to a marketing site is a bad email, and I do not want a "View in browser" link or a footer full of social icons in a message whose job is to carry a listing URL.
But "do not pull the reader to the website" and "give the reader no way to reach help" are different rules, and I had been shipping the second one while believing the first.
What I am changing, and what I am not
The fix is one shared footer, used by all three template files, with a linked notifio.app/help, present in both the HTML and the text part. That is seven edits and it also puts a URL in the plain-text bodies for the first time.
What I am not doing is adding it to the new-listing alert. That email competes for inbox attention with a timer running, every one of these messages already carries a "Getting this in Promotions? Move it to Primary" nudge for exactly that reason, and a listing alert whose footer grows a help link looks more like marketing to a spam classifier than the same link does in an activation receipt. The two emails that should carry it are the activation code and the two "something needs you" notices, where the reader is already stuck and is the most likely person on earth to want our help page.
If you want to see the page all of this is about, it is notifio.app/help. The app the emails keep telling people to open is at notifio.app/download, and the one-off price those activation emails follow is on notifio.app/pricing.
