<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Business Attire</title>
    <description>The latest articles on DEV Community by Business Attire (@businessattire).</description>
    <link>https://dev.to/businessattire</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%2F3203892%2Fd565941d-f053-4d27-8d87-b5993206fcca.png</url>
      <title>DEV Community: Business Attire</title>
      <link>https://dev.to/businessattire</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/businessattire"/>
    <language>en</language>
    <item>
      <title>Fineprint: Find out what really changed in the ToS</title>
      <dc:creator>Business Attire</dc:creator>
      <pubDate>Fri, 06 Jun 2025 15:44:46 +0000</pubDate>
      <link>https://dev.to/businessattire/fineprint-find-out-what-really-changed-in-the-tos-1405</link>
      <guid>https://dev.to/businessattire/fineprint-find-out-what-really-changed-in-the-tos-1405</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;I built &lt;a href="https://fineprint.help" rel="noopener noreferrer"&gt;Fineprint&lt;/a&gt;, a service for figuring out how a Terms of Service, Privacy Policy, User Agreement, or other company legal document has changed.&lt;/p&gt;

&lt;p&gt;The idea is that companies are always sending "Upcoming changes to our legal agreements" emails, but often don't tell you &lt;em&gt;what&lt;/em&gt; has changed.&lt;/p&gt;

&lt;p&gt;That's where Fineprint comes in!&lt;/p&gt;

&lt;p&gt;Just forward one of those "Our Terms of Service has changed" emails to &lt;code&gt;app@fineprint.help&lt;/code&gt;. It'll load the policy, find a previous version (from the Internet Archive), calculate a diff, and summarize the changes for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;em&gt;Why&lt;/em&gt; I Built Fineprint
&lt;/h2&gt;

&lt;p&gt;[note: you can skip on down to the Demo section below if you just want to see it in action]&lt;/p&gt;

&lt;p&gt;When I saw the contest idea, I asked myself "what is unique to email?". What properties and what communications make the most sense over email? After all, there's a dozen or so different form factors for communication these days: social media, SMS, phone calls, websites, apps, WhatsApp/Signal/Discord/etc, snail mail, carrier pigeon, smoke signals, etc.&lt;/p&gt;

&lt;p&gt;I threw down a few random thoughts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Forwarding stuff is a quintessentially "email" thing to do&lt;/li&gt;
&lt;li&gt;Email is a common mechanism for receiving notifications&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And from there, the question was "what notifications are specific to email?". There are many notifications that we receive through email that are redundant -- updates from LinkedIn, GitHub, Discord, social media, etc -- but those all have some other source of truth we can query directly. What notifications would we not receive anywhere else?&lt;/p&gt;

&lt;p&gt;And the answer, at least for me, was policy updates.&lt;/p&gt;

&lt;p&gt;Policy updates are perfect because it's frequently a legal requirement for companies to notify you when they change, and the main medium for doing that is email.&lt;/p&gt;

&lt;p&gt;While a select few companies will go the extra mile to say &lt;em&gt;what&lt;/em&gt; they've changed, the vast majority just say "here's the new policy, go read it or something".&lt;/p&gt;

&lt;p&gt;And of course, &lt;a href="https://tosdr.org/" rel="noopener noreferrer"&gt;basically nobody reads the Terms of Service&lt;/a&gt;, and similarly few people are going to go to the effort to diff two versions of a 10+ page document. But these things are also important! Is the company now collecting information I would consider invasive? Are they sharing my information with new partners that I think are shady? Are they &lt;em&gt;selling&lt;/em&gt; access to the information I've given them?&lt;/p&gt;

&lt;p&gt;It turns out computers can make quick work of this task. And that's what Fineprint is for -- lowering the activation energy to answering these questions so you can make informed decisions about which companies you interact with and entrust your information to.&lt;/p&gt;

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

&lt;p&gt;The best demo is just to try it! Search your inbox for subjects like "Changes to [...]" or "[...] Agreement Update" or "Update to [...]", and forward one of those emails to &lt;code&gt;app@fineprint.help&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The response you'll get will look something like:&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%2F6she25ls67j52lxoc3uy.png" 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%2F6she25ls67j52lxoc3uy.png" alt="Screenshot of example summary"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this case, we couldn't find an older version of the policy because &lt;a href="https://bsky.app/profile/archive.org/post/3lqwzgf7qhc2p" rel="noopener noreferrer"&gt;the Internet Archive is down this morning&lt;/a&gt; 🙃&lt;/p&gt;

&lt;p&gt;And an example with functional diffing, now that Internet Archive is back up:&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%2Fnvc0l2di05ink0x54myi.png" 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%2Fnvc0l2di05ink0x54myi.png" alt="Screenshot of example delta"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;More details are available at &lt;a href="https://fineprint.help" rel="noopener noreferrer"&gt;fineprint.help&lt;/a&gt;. There are heavy rate limits in place as a cost control, as I'm using several APIs to generate the policy summaries.&lt;/p&gt;

&lt;p&gt;Instructions for running the application locally are included in the GitHub repo.&lt;/p&gt;

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


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/bcspragu" rel="noopener noreferrer"&gt;
        bcspragu
      &lt;/a&gt; / &lt;a href="https://github.com/bcspragu/fineprint" rel="noopener noreferrer"&gt;
        fineprint
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A tool for determining what has changed in a company's legal agreement (ToS, Privacy Policy, etc), built for Postmark's Inbox Innovators challenge
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Fineprint&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;A tool for determining what has changed in a company's legal agreement (ToS, Privacy Policy, etc), built for &lt;a href="https://postmarkapp.com/blog/announcing-the-postmark-challenge-inbox-innovators%20" rel="nofollow noopener noreferrer"&gt;Postmark's Inbox Innovators challenge&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;More usage details at &lt;a href="https://fineprint.help" rel="nofollow noopener noreferrer"&gt;Fineprint.help&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Local Testing&lt;/h2&gt;
&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Dependencies&lt;/h3&gt;
&lt;/div&gt;
&lt;p&gt;To run the service locally, you'll need all of the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A &lt;a href="https://go.dev/" rel="nofollow noopener noreferrer"&gt;Go&lt;/a&gt; compiler (likely 1.24.x)&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://nodejs.org/" rel="nofollow noopener noreferrer"&gt;Node&lt;/a&gt; environment (I'm using Node 22)
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;mjml&lt;/code&gt; needs to be installed in this env, for formatting emails&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Postmark credentials&lt;/li&gt;
&lt;li&gt;Internet Archive keys&lt;/li&gt;
&lt;li&gt;An Anthropic API key&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Running&lt;/h3&gt;

&lt;/div&gt;
&lt;p&gt;&lt;code&gt;./scripts/run.sh&lt;/code&gt; is a helper for running the service, but it assumes you have your credentials stored in &lt;code&gt;pass&lt;/code&gt; under specific names.&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;curl -v \
  -u &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;$(&lt;/span&gt;pass show postmark/webhook-username&lt;span class="pl-pds"&gt;)&lt;/span&gt;&lt;/span&gt;:&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;$(&lt;/span&gt;pass show postmark/webhook-password&lt;span class="pl-pds"&gt;)&lt;/span&gt;&lt;/span&gt; \
  --data @json-body.json \
  -H &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;'&lt;/span&gt;X-Forwarded-For: 127.0.0.1&lt;span class="pl-pds"&gt;'&lt;/span&gt;&lt;/span&gt; \
  localhost:8080/webhook&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Limitations&lt;/h2&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;If the legal document has changed a lot, the diff may be very large and overflow our LLM context, so we trim it down to size
&lt;ul&gt;
&lt;li&gt;This…&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/bcspragu/fineprint" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;For testing locally, follow the instructions in the README.md file in the root of the repo. It will require a functioning Go environment, as well as an Anthropic API key, Postmark credentials, and Internet Archive keys. You'll also need a Node environment with &lt;code&gt;mjml&lt;/code&gt; installed.&lt;/p&gt;

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

&lt;p&gt;The first ~50-60% of this project were written entirely with &lt;a href="https://docs.anthropic.com/en/docs/claude-code/overview" rel="noopener noreferrer"&gt;Claude Code&lt;/a&gt;, and then I came in and cleaned up the API calls, added the diffing support, tuned the prompts, added the MJML templates, and generally built out the rest of the project.&lt;/p&gt;

&lt;p&gt;The trickiest part was handling the different possible cases: e.g. ToS;DR results available or not, older versions of the policy available or not, etc. There are also lots of heuristics required to get the correct URLs + policies and correlate them across services.&lt;/p&gt;

&lt;p&gt;The server that receives the Postmark Inbound Email Processing webhook is written in Go. It uses Claude for classifying the email and generating summaries. The &lt;a href="https://tosdr.org/" rel="noopener noreferrer"&gt;ToS;DR&lt;/a&gt; API is used to get curated write-ups of policy information and other metadata. Postmark is used for both the inbound (i.e. forwarded policy) emails, as well as for sending the reply emails, which are sent via the API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Working with Postmark
&lt;/h2&gt;

&lt;p&gt;Working with Postmark was very straightforward: the APIs are clearly documented, use a standard HTTP/JSON interface, and are well-structured. The console itself is easy to navigate to help with testing and debugging, and adding basic auth support to the webhook was a breeze.&lt;/p&gt;

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