<?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: adamday75</title>
    <description>The latest articles on DEV Community by adamday75 (@adamday75).</description>
    <link>https://dev.to/adamday75</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%2F3847746%2F94d62c71-4597-41ad-ae31-7a9a1a47de76.png</url>
      <title>DEV Community: adamday75</title>
      <link>https://dev.to/adamday75</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/adamday75"/>
    <language>en</language>
    <item>
      <title>When Personal AI Starts Feeling Real: Memory, Follow-Through, and Useful Work</title>
      <dc:creator>adamday75</dc:creator>
      <pubDate>Sat, 25 Apr 2026 19:15:18 +0000</pubDate>
      <link>https://dev.to/adamday75/when-personal-ai-starts-feeling-real-memory-follow-through-and-useful-work-1d5j</link>
      <guid>https://dev.to/adamday75/when-personal-ai-starts-feeling-real-memory-follow-through-and-useful-work-1d5j</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/openclaw-2026-04-16"&gt;OpenClaw Writing Challenge&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Most AI assistants feel impressive for five minutes.&lt;/p&gt;

&lt;p&gt;Then real life shows up.&lt;/p&gt;

&lt;p&gt;A useful assistant does not just need a good model. It needs memory, follow-through, and a workflow that can survive ordinary messy days.&lt;/p&gt;

&lt;p&gt;That is the shift OpenClaw created for me.&lt;/p&gt;

&lt;p&gt;I did not end up caring most about fancy prompting. I cared about whether the system could:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;remember something important from last week&lt;/li&gt;
&lt;li&gt;compress a noisy day into a readable summary&lt;/li&gt;
&lt;li&gt;turn short-term notes into long-term memory&lt;/li&gt;
&lt;li&gt;remind me to review something before it silently drifted&lt;/li&gt;
&lt;li&gt;support real work without becoming another source of noise&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That shifted my thinking about personal AI.&lt;/p&gt;

&lt;h2&gt;
  
  
  The real unlock was not “smarter answers”
&lt;/h2&gt;

&lt;p&gt;The real unlock was building a system around continuity.&lt;/p&gt;

&lt;p&gt;Once memory was working end to end, OpenClaw stopped feeling like a stateless chatbot and started feeling more like an actual assistant.&lt;/p&gt;

&lt;p&gt;That did not happen because the model suddenly got brilliant.&lt;br&gt;
It happened because the workflow got structure.&lt;/p&gt;

&lt;p&gt;In practice, that meant:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;verifying memory instead of assuming it worked&lt;/li&gt;
&lt;li&gt;improving daily digests so they were actually readable&lt;/li&gt;
&lt;li&gt;curating weekly memory so important wins and lessons stayed durable&lt;/li&gt;
&lt;li&gt;using reminders where blind automation would have been premature&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That sounds almost boring compared to big AGI claims.&lt;/p&gt;

&lt;p&gt;It is also what made the system genuinely useful.&lt;/p&gt;

&lt;h2&gt;
  
  
  Memory is not enough without curation
&lt;/h2&gt;

&lt;p&gt;One of the easiest ways to make AI worse is to feed it too much raw context and call that memory.&lt;/p&gt;

&lt;p&gt;Raw notes are not judgment.&lt;br&gt;
Raw logs are not continuity.&lt;br&gt;
A giant pile of transcripts is not wisdom.&lt;/p&gt;

&lt;p&gt;I found that useful personal AI needs at least two layers:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Compression
&lt;/h3&gt;

&lt;p&gt;You need a way to turn the day into signal.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Curation
&lt;/h3&gt;

&lt;p&gt;You need a way to decide what deserves to survive.&lt;/p&gt;

&lt;p&gt;That is why the combination of daily digest + weekly curated memory ended up mattering so much.&lt;/p&gt;

&lt;p&gt;The digest handled volume.&lt;br&gt;
The weekly curation handled meaning.&lt;/p&gt;

&lt;p&gt;Without that second step, memory just becomes clutter with better branding.&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%2Fo812agj3cj6ydtq3f4pe.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%2Fo812agj3cj6ydtq3f4pe.png" alt="ai-optimizer-extension" width="347" height="311"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 2: The surrounding OpenClaw environment where real memory and workflow experiments were being used, not just discussed in theory.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Automation gets better when you stop pretending it is magic
&lt;/h2&gt;

&lt;p&gt;One of my biggest takeaways from building with OpenClaw is that good automation is not blind automation.&lt;/p&gt;

&lt;p&gt;The most valuable pattern was not “remove the human.”&lt;br&gt;
It was “be honest about what still needs review.”&lt;/p&gt;

&lt;p&gt;That showed up everywhere:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;reminder cron before full automation&lt;/li&gt;
&lt;li&gt;disk verification instead of trusting a green status message&lt;/li&gt;
&lt;li&gt;content drafting followed by a humanizing pass&lt;/li&gt;
&lt;li&gt;explicit review loops before calling something reliable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That mindset also shaped how I thought about AI agents more broadly.&lt;/p&gt;

&lt;p&gt;Recent model progress makes this tension obvious: capability is climbing fast, but trust still lags behind.&lt;/p&gt;

&lt;p&gt;That means the opportunity is not just smarter models.&lt;br&gt;
It is better guardrails, better memory, and better workflow design.&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%2Fhd31sh5v5ls1mgcq4g16.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%2Fhd31sh5v5ls1mgcq4g16.png" alt="model-evaluations" width="610" height="97"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 3: Model evaluation notes helped keep the system grounded in real behavior instead of hype.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Cost discipline becomes part of the architecture
&lt;/h2&gt;

&lt;p&gt;While building these OpenClaw workflows, I was also building AI Optimizer.&lt;/p&gt;

&lt;p&gt;It was not the main story, but it became an important supporting lesson: once AI becomes part of real work, cost and waste stop being abstract.&lt;/p&gt;

&lt;p&gt;If your assistant is doing useful work every day, then bad routing, wasted calls, duplicate outputs, and noisy workflows all become expensive.&lt;/p&gt;

&lt;p&gt;So the architecture started to converge around a simple idea:&lt;/p&gt;

&lt;p&gt;Useful AI is not one clever prompt.&lt;br&gt;
It is a system.&lt;/p&gt;

&lt;p&gt;A system needs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;memory&lt;/li&gt;
&lt;li&gt;curation&lt;/li&gt;
&lt;li&gt;review loops&lt;/li&gt;
&lt;li&gt;cost discipline&lt;/li&gt;
&lt;li&gt;and enough honesty to admit when it is not ready to run alone&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What OpenClaw gets right
&lt;/h2&gt;

&lt;p&gt;What OpenClaw gets right is that it is hackable enough to become personal.&lt;/p&gt;

&lt;p&gt;It lets you move past “chat with a model” and toward “build an assistant that fits actual life and work.”&lt;/p&gt;

&lt;p&gt;That is a much more interesting problem.&lt;/p&gt;

&lt;p&gt;And, in my experience, it is also where the real value starts.&lt;/p&gt;

&lt;p&gt;Not when the assistant sounds smartest.&lt;br&gt;
When it becomes dependable.&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%2Fq1vxisrr3019bqopq3ut.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%2Fq1vxisrr3019bqopq3ut.png" alt="workflow-cron" width="340" height="180"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 4: Workflow structure and scheduled follow-through are what make the assistant useful after the first impressive demo.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Reliability matters more than green checkmarks
&lt;/h2&gt;

&lt;p&gt;A final lesson from this build: success states can lie.&lt;/p&gt;

&lt;p&gt;I spent time debugging workflows that looked successful on paper but were not actually delivering the result I needed. That reinforced a simple rule:&lt;/p&gt;

&lt;p&gt;Do not trust a clean status line more than observable output.&lt;/p&gt;

&lt;p&gt;If memory is supposed to work, verify recall.&lt;br&gt;
If a workflow is supposed to deliver, confirm delivery.&lt;br&gt;
If a summary is supposed to help, make sure it is actually readable.&lt;/p&gt;

&lt;p&gt;That kind of verification sounds mundane.&lt;/p&gt;

&lt;p&gt;It is also the difference between an AI demo and a system you can actually lean on.&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%2Fn2xtzqj7w3wub9r0o8bw.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%2Fn2xtzqj7w3wub9r0o8bw.png" alt="delivery-debug" width="602" height="241"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 5: Reliability work matters — a workflow reporting “ok” is not the same as a workflow actually delivering value.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  ClawCon Michigan
&lt;/h2&gt;

&lt;p&gt;I did not attend ClawCon Michigan.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>openclawchallenge</category>
      <category>ai</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Building a Personal AI Workflow That Actually Follows Through</title>
      <dc:creator>adamday75</dc:creator>
      <pubDate>Fri, 24 Apr 2026 02:40:04 +0000</pubDate>
      <link>https://dev.to/adamday75/building-a-personal-ai-workflow-that-actually-follows-through-63n</link>
      <guid>https://dev.to/adamday75/building-a-personal-ai-workflow-that-actually-follows-through-63n</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/openclaw-2026-04-16"&gt;OpenClaw Challenge&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;I built a practical personal AI workflow around OpenClaw that does more than answer messages.&lt;/p&gt;

&lt;p&gt;The goal was simple: make it useful in real life, not just impressive in a demo.&lt;/p&gt;

&lt;p&gt;That meant building a system that could:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;remember important context over time&lt;/li&gt;
&lt;li&gt;summarize noisy days into something readable&lt;/li&gt;
&lt;li&gt;help curate durable lessons into long-term memory&lt;/li&gt;
&lt;li&gt;monitor AI news and turn strong topics into post-ready drafts&lt;/li&gt;
&lt;li&gt;keep the whole workflow sustainable while I was also building AI Optimizer to reduce model waste and cost&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The core problem I wanted to solve was this: most AI assistants feel smart in the moment, but they fall apart over time. They forget what matters, lose continuity, and create more noise than follow-through.&lt;/p&gt;

&lt;p&gt;I wanted something better. OpenClaw gave me a way to actually build it.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Used OpenClaw
&lt;/h2&gt;

&lt;p&gt;OpenClaw became the operating layer for the whole workflow.&lt;/p&gt;

&lt;p&gt;Here is what I set up and refined:&lt;/p&gt;

&lt;h3&gt;
  
  
  1) Memory that could be tested instead of assumed
&lt;/h3&gt;

&lt;p&gt;I verified OpenClaw memory end to end instead of trusting a health check blindly. That included confirming embeddings were working, proving recall with a fresh test memory, and checking the local LanceDB files on disk.&lt;/p&gt;

&lt;p&gt;That changed the whole project, because once memory was real, the assistant could start acting more like a system and less like a stateless chatbot.&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%2F3bwwgzgr24x6c5hkvsc0.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%2F3bwwgzgr24x6c5hkvsc0.png" alt="memory-verification" width="800" height="81"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 1: Verifying OpenClaw memory end-to-end — embeddings working, LanceDB files present on disk.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2) Daily digest + weekly memory curation
&lt;/h3&gt;

&lt;p&gt;I installed and improved a daily digest workflow so each day could be compressed into something actually readable.&lt;/p&gt;

&lt;p&gt;From there, I used a second layer: weekly curation into &lt;code&gt;MEMORY.md&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That gave me a simple but powerful structure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;raw daily notes&lt;/li&gt;
&lt;li&gt;daily digest as a compression layer&lt;/li&gt;
&lt;li&gt;weekly curated memory for durable wins, lessons, decisions, and insights&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This ended up being one of the biggest practical upgrades in the whole setup.&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%2Fkz91nly1sbclkiiour32.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%2Fkz91nly1sbclkiiour32.png" alt="daily-digest-memory" width="581" height="83"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 2: Left: Daily digest output compressing a raw day into structured wins, lessons, and decisions. Right: Weekly MEMORY.md curation showing durable insights.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3) AI news scouting and content workflow testing
&lt;/h3&gt;

&lt;p&gt;I used OpenClaw to investigate AI news workflows and then used that output to test a LinkedIn drafting pipeline.&lt;/p&gt;

&lt;p&gt;I evaluated &lt;code&gt;linkedin-drafter&lt;/code&gt; and &lt;code&gt;humanizer&lt;/code&gt;, cleaned up output leakage, and built a drafter -&amp;gt; humanizer pass so the content came out cleaner and more believable.&lt;/p&gt;

&lt;p&gt;That let me turn real topics into post-ready drafts, including one of my favorite test runs based on the Stanford HAI AI Index and the jump in agent performance on OSWorld.&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%2Fc31y79bg8lyh52a3rye0.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%2Fc31y79bg8lyh52a3rye0.png" alt="news-scout-telegram" width="491" height="663"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 3: Top: AI News Scout Telegram report delivering fresh AI topics.&lt;/em&gt;&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%2Fl0fxf7o2b5g5flnig9dl.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%2Fl0fxf7o2b5g5flnig9dl.png" alt="news-scout-draft" width="645" height="405"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 3b: Bottom: Humanized LinkedIn draft ready for posting.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  4) Reminders and follow-through
&lt;/h3&gt;

&lt;p&gt;I added a daily reminder cron for digest review instead of automating blindly too early.&lt;/p&gt;

&lt;p&gt;That mattered because the lesson from this whole build was not "automate everything immediately."&lt;br&gt;
It was "automate what you trust, and add review where quality still matters."&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%2Fxjl2al2zekrhdbsefiu8.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%2Fxjl2al2zekrhdbsefiu8.png" alt="Digest-reminder-cron" width="597" height="337"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 4: Cron job list showing the 9:30 PM ET daily digest reminder — automation with a review loop.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  5) AI Optimizer as a supporting layer
&lt;/h3&gt;

&lt;p&gt;While building these workflows, I was also building AI Optimizer to reduce waste and cost in real model usage.&lt;/p&gt;

&lt;p&gt;It was not the main point of this OpenClaw build, but it became part of the system thinking: once AI stops being a toy and becomes part of daily work, cost discipline matters too.&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%2F1qthy48pzn9dmfw8cw62.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%2F1qthy48pzn9dmfw8cw62.png" alt="AI-optimizer-proxy-health" width="478" height="243"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Figure 5: AI Optimizer proxy &lt;code&gt;/health&lt;/code&gt; and &lt;code&gt;/stats&lt;/code&gt; endpoints showing cache hits and version v2.1.3.&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;The screenshots above show the workflow in action. Here's the full flow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Raw memory file&lt;/strong&gt; — Daily notes captured automatically&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Daily digest output&lt;/strong&gt; — Compressed, structured summary delivered to Telegram&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Curated MEMORY.md&lt;/strong&gt; — Weekly rollup of durable wins, lessons, decisions, insights&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI News Scout → polished draft&lt;/strong&gt; — Real topics turned into humanized content&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reminder/automation layer&lt;/strong&gt; — Cron jobs keeping the system alive without blind automation&lt;/li&gt;
&lt;/ol&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%2Fs8l89zkv98ozsxq9ytnx.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%2Fs8l89zkv98ozsxq9ytnx.png" alt="Full-workflow-collage" width="582" height="572"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;The biggest lesson was that personal AI becomes useful when it stops acting like a single prompt and starts acting like a system.&lt;/p&gt;

&lt;p&gt;A few things mattered much more than I expected:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;memory quality matters more than flashy responses&lt;/li&gt;
&lt;li&gt;digesting and curation matter more than dumping raw context into the model&lt;/li&gt;
&lt;li&gt;reminders and review loops matter more than blind automation&lt;/li&gt;
&lt;li&gt;cost and waste matter once the workflow becomes real&lt;/li&gt;
&lt;li&gt;the best builds are usually the ones that stay honest about failure modes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I also learned that "status: ok" is not enough for automation. I had to verify persistence on disk, not just trust logs or cron success states.&lt;/p&gt;

&lt;p&gt;That mindset carried through the whole build: prove it, then trust it.&lt;br&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%2F6vccl2f7jri6v425l2um.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%2F6vccl2f7jri6v425l2um.png" alt="ai-optimizer-extension" width="347" height="302"&gt;&lt;/a&gt;&lt;br&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%2Ftj1aydggnmawdpwro00m.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%2Ftj1aydggnmawdpwro00m.png" alt="ai-optimizer-ui" width="462" height="938"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  ClawCon Michigan
&lt;/h2&gt;

&lt;p&gt;I did not attend ClawCon Michigan.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>openclawchallenge</category>
    </item>
    <item>
      <title>Built a Caching Proxy for OpenAI — Saved 40% on API Bills</title>
      <dc:creator>adamday75</dc:creator>
      <pubDate>Sat, 28 Mar 2026 15:10:17 +0000</pubDate>
      <link>https://dev.to/adamday75/built-a-caching-proxy-for-openai-saved-40-on-api-bills-3b6h</link>
      <guid>https://dev.to/adamday75/built-a-caching-proxy-for-openai-saved-40-on-api-bills-3b6h</guid>
      <description>&lt;p&gt;A maintenance manager's first SaaS. Technical deep-dive + lessons learned.&lt;/p&gt;

&lt;p&gt;Hey dev.to! 👋&lt;/p&gt;

&lt;p&gt;I'm not a career developer. I supervise industrial mechanics and run a maintenance department. But we needed AI for our CMMS (Computerized Maintenance Management System), and the OpenAI API costs were getting crazy.&lt;/p&gt;

&lt;p&gt;So I built a caching proxy. Here's how it works, what I learned, and the actual code.&lt;/p&gt;

&lt;p&gt;───&lt;/p&gt;

&lt;p&gt;The Problem&lt;/p&gt;

&lt;p&gt;We're using AI for:&lt;/p&gt;

&lt;p&gt;• Auto-generating work orders&lt;br&gt;
• Predictive maintenance alerts&lt;br&gt;
• Vendor communications&lt;br&gt;
• Training docs&lt;/p&gt;

&lt;p&gt;Issue: Same prompts, repeated constantly, paying every time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User: "Generate work order for HVAC maintenance"
→ Pay $0.002

User: "Generate work order for HVAC maintenance" (same prompt)
→ Pay $0.002 again

User: "Generate work order for HVAC maintenance" (same prompt, 3rd time)
→ Pay $0.002 AGAIN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This adds up FAST at scale.&lt;/p&gt;

&lt;p&gt;───&lt;/p&gt;

&lt;p&gt;The Solution: Caching Proxy&lt;/p&gt;

&lt;p&gt;Intercept OpenAI requests, hash the prompt, cache the response.&lt;/p&gt;

&lt;p&gt;Architecture:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Your App → AI Optimizer Proxy → OpenAI API
                ↓
           SQLite Cache
                ↓
        (hash → response)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Flow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;App sends request to proxy&lt;/li&gt;
&lt;li&gt;Proxy hashes: sha256(prompt + model + params)&lt;/li&gt;
&lt;li&gt;Check cache:
• Hit: Return cached response (FREE)
• Miss: Forward to OpenAI, cache response, return&lt;/li&gt;
&lt;li&gt;Dashboard tracks hits/misses/savings&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;───&lt;/p&gt;

&lt;p&gt;The Code (Simplified)&lt;/p&gt;

&lt;p&gt;Cache lookup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getCacheKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getDb&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SELECT * FROM cache WHERE hash = ?&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;setCacheKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ttl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;86400&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getDb&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;INSERT OR REPLACE INTO cache (hash, response, expires_at) VALUES (?, ?, ?)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;ttl&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Request Handler:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/v1/chat/completions&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;hashPrompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cached&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getCacheKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cached&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;cached&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;expires_at&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Cache HIT&lt;/span&gt;
    &lt;span class="nx"&gt;analytics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;recordHit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cached&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Cache MISS - call OpenAI&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.openai.com/v1/chat/completions&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Authorization&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;setCacheKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;analytics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;recordHit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;───&lt;/p&gt;

&lt;p&gt;The Results&lt;/p&gt;

&lt;p&gt;My workload (1 week):&lt;/p&gt;

&lt;p&gt;• Total requests: 1,847&lt;br&gt;
• Cache hits: 1,385&lt;br&gt;
• Cache misses: 462&lt;br&gt;
• Cache hit rate: 75%&lt;br&gt;
• Cost savings: ~40%&lt;/p&gt;

&lt;p&gt;Real money saved: If you're spending $100/month, expect $40-60 savings.&lt;/p&gt;

&lt;p&gt;───&lt;br&gt;
Technical Challenges&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Device Fingerprinting&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For license validation, I needed to identify devices without login:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;machineId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node-machine-id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;deviceId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;machineId&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fingerprint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sha256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;deviceId&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Stripe Webhooks&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;First payment webhook failed because I didn't handle customer.subscription.created vs customer.subscription.updated differently. Now I route by event.type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/stripe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;eventType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;switch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;eventType&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;customer.subscription.created&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="nf"&gt;createLicense&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;customer.subscription.deleted&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="nf"&gt;revokeLicense&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// ... 4 more event types&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Email Delivery&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Gmail OAuth was a pain. Had to:&lt;/p&gt;

&lt;p&gt;• Create Google Cloud project&lt;br&gt;
• Enable Gmail API&lt;br&gt;
• Get OAuth credentials&lt;br&gt;
• Handle refresh tokens&lt;br&gt;
• Deploy secrets to Fly.io&lt;/p&gt;

&lt;p&gt;First emails failed with unauthorized_client. Turned out my refresh token was from a different OAuth client. Started fresh, worked immediately.&lt;/p&gt;

&lt;p&gt;───&lt;/p&gt;

&lt;p&gt;The Stack&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;| Component   | Tech              |
| ----------- | ----------------- |
| Backend     | Node.js + Express |
| Desktop App | Electron          |
| Database    | SQLite            |
| Hosting     | Fly.io            |
| Payments    | Stripe            |
| Email       | Gmail API         |
| Builds      | electron-builder  |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Total build time: ~1 month (nights/weekends)&lt;/p&gt;

&lt;p&gt;───&lt;/p&gt;

&lt;p&gt;Lessons Learned&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Schema matters: Added device_limit column after deploying. Had to recreate licenses. Check your schema BEFORE launch.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fresh Stripe signup &amp;gt; DB hacking: When my license broke, creating a new test subscription was faster than debugging the DB.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ship before perfect: My first build had no stats dashboard. Shipped anyway. Added it later.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Non-devs can build SaaS: I learned enough to ship. You can too.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;───&lt;/p&gt;

&lt;p&gt;Try It...&lt;/p&gt;

&lt;p&gt;Free 14-day trial: &lt;a href="https://ai-optimizer-landing.vercel.app" rel="noopener noreferrer"&gt;https://ai-optimizer-landing.vercel.app&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;GitHub (open source): &lt;a href="https://github.com/adamday75/ai-optimizer-app" rel="noopener noreferrer"&gt;https://github.com/adamday75/ai-optimizer-app&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Drop-in replacement. Change one env var. See your savings.&lt;/p&gt;

&lt;p&gt;───&lt;/p&gt;

&lt;p&gt;Questions?&lt;/p&gt;

&lt;p&gt;I'm happy to answer anything about:&lt;/p&gt;

&lt;p&gt;(&lt;a href="https://github.com/adamday75/ai-optimizer-app" rel="noopener noreferrer"&gt;https://github.com/adamday75/ai-optimizer-app&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;• The caching strategy&lt;br&gt;
• License system&lt;br&gt;
• Stripe integration&lt;br&gt;
• Electron builds&lt;br&gt;
• Learning Node.js as a non-dev&lt;/p&gt;

&lt;p&gt;Drop a comment! 👇&lt;/p&gt;

&lt;p&gt;───&lt;/p&gt;

&lt;p&gt;Adam Day | Maintenance Manager → Accidental SaaS Founder&lt;/p&gt;

&lt;p&gt;───&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>node</category>
      <category>openai</category>
      <category>sass</category>
    </item>
  </channel>
</rss>
