<?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: Vicente Junior</title>
    <description>The latest articles on DEV Community by Vicente Junior (@vicente_junior_dev).</description>
    <link>https://dev.to/vicente_junior_dev</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%2F3838257%2Fff6de2e1-5a5f-4aab-bdb3-2099aa03009b.jpg</url>
      <title>DEV Community: Vicente Junior</title>
      <link>https://dev.to/vicente_junior_dev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vicente_junior_dev"/>
    <language>en</language>
    <item>
      <title>Notion Life Review OS — Log your day to Notion from WhatsApp using AI</title>
      <dc:creator>Vicente Junior</dc:creator>
      <pubDate>Sun, 29 Mar 2026 12:26:25 +0000</pubDate>
      <link>https://dev.to/vicente_junior_dev/notion-life-review-os-log-your-day-to-notion-from-whatsapp-using-ai-3g3m</link>
      <guid>https://dev.to/vicente_junior_dev/notion-life-review-os-log-your-day-to-notion-from-whatsapp-using-ai-3g3m</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/notion-2026-03-04"&gt;Notion MCP Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Notion Life Review OS&lt;/strong&gt; is a WhatsApp assistant that captures your day and organizes everything in your own Notion workspace — from a single message.&lt;/p&gt;

&lt;p&gt;You send something like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Worked on the API integration today. Need to present to the client next Thursday. Also figured out why our Redis connection was dropping."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It extracts a task, a project, a learning, and your mood. Asks you to confirm. Saves everything to the right Notion database. No forms. No clicking. No friction.&lt;/p&gt;

&lt;p&gt;The core idea is simple: your day lives in WhatsApp already. You're already typing there. So why open another tool?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It also works the other way.&lt;/strong&gt; Ask it anything:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What tasks are due this week?"&lt;br&gt;
"What did I learn this week?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And you can manage your Notion schema directly from WhatsApp — even via voice:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Add a column called Who, select type, to the Tasks table"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The new field is available on the very next message.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One thing I really liked about how this came together:&lt;/strong&gt; the project and task structure is completely generic. You can use it for work — a project called "API Backend" with tasks like "Deploy to production". But it works just as well for a grocery list — project "Supermarket", tasks "milk, eggs, bread". Or a personal to-do list. The system doesn't care. It just captures what you tell it and puts it in the right place.&lt;/p&gt;

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

&lt;p&gt;&lt;iframe src="https://player.mux.com/Tkqz82m9uzGSX01TCH2awdaxibvQtAjBQ6p5DvuwpHGg" width="710" height="399"&gt;
&lt;/iframe&gt;

&lt;/p&gt;

&lt;h2&gt;
  
  
  Show me the code
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/vicente-r-junior/notion-life-review-os" rel="noopener noreferrer"&gt;github.com/vicente-r-junior/notion-life-review-os&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Full setup instructions in the README.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Used Notion MCP
&lt;/h2&gt;

&lt;p&gt;Notion MCP is the backbone of the entire system. Every single interaction with Notion goes through it — no direct API calls anywhere.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reading schema at startup&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When the app boots, it calls &lt;code&gt;API-retrieve-a-database&lt;/code&gt; and &lt;code&gt;API-retrieve-a-data-source&lt;/code&gt; for each of the 5 databases. The schemas get cached in Redis and injected directly into the GPT-4o system prompt — so the agent knows what fields exist, what types they are, and which ones are required, without any extra calls per message.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Writing data&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When the user confirms, the app calls &lt;code&gt;API-post-page&lt;/code&gt; for each item — daily log, tasks, projects, learnings. This part is pure deterministic Python, not an LLM. The write step is too important to leave non-deterministic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Querying data&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For questions like "what tasks are due this week?", the agent uses &lt;code&gt;API-query-data-source&lt;/code&gt; with structured filters built from natural language. It resolves dates, applies status filters, and formats the answer for WhatsApp.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Updating schema dynamically&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When the user asks to add a column — even via voice — the app calls &lt;code&gt;API-update-a-data-source&lt;/code&gt;. The Redis cache refreshes immediately and the system prompt is rebuilt. The new field is available on the next message.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bulk updates&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For things like "set all tasks Who to Vicente", the app queries first, shows a confirmation with the affected records, then calls &lt;code&gt;API-patch-page&lt;/code&gt; for each one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WhatsApp → Evolution API → FastAPI webhook
                               ↓
                     Intent classifier (GPT-4o-mini)
                               ↓
           ┌───────────────────┼──────────────────┐
           ↓                   ↓                  ↓
 Conversational agent     Query agent      Add column flow
      (GPT-4o)             (GPT-4o)        (GPT-4o-mini)
           ↓                   ↓                  ↓
      SAVE_PAYLOAD        Notion MCP         Notion MCP
           ↓               (query)          (update schema)
    User confirms
           ↓
     Notion Writer
     (pure Python)
           ↓
       Notion MCP
      (write pages)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;One conversational agent instead of a pipeline.&lt;/strong&gt;&lt;br&gt;
I started with separate extractor, matcher, and confirmation agents. It was complex and fragile. A single GPT-4o call with Redis conversation history turned out to be simpler, faster, and much easier to debug. The agent holds the full context of the conversation and knows when it has enough information to produce a &lt;code&gt;SAVE_PAYLOAD&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The write step is never an LLM.&lt;/strong&gt;&lt;br&gt;
The &lt;code&gt;notion_writer&lt;/code&gt; is pure Python calling Notion MCP directly. Every property format handled explicitly. Giving an LLM direct write access to your Notion is asking for trouble.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Schema-aware prompts.&lt;/strong&gt;&lt;br&gt;
The agent knows your exact Notion schema at all times. Custom fields like &lt;em&gt;Who&lt;/em&gt;, &lt;em&gt;Priority&lt;/em&gt;, or &lt;em&gt;Energy&lt;/em&gt; are injected into the system prompt dynamically. If a field is marked required, the agent asks for it before saving — no partial records.&lt;/p&gt;
&lt;h2&gt;
  
  
  Stack
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Technology&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Backend&lt;/td&gt;
&lt;td&gt;Python 3.12 + FastAPI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI&lt;/td&gt;
&lt;td&gt;OpenAI GPT-4o + Whisper&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Notion interface&lt;/td&gt;
&lt;td&gt;Notion MCP (&lt;code&gt;mcp/notion&lt;/code&gt; Docker image)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WhatsApp bridge&lt;/td&gt;
&lt;td&gt;Evolution API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Session + cache&lt;/td&gt;
&lt;td&gt;Redis 7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Infrastructure&lt;/td&gt;
&lt;td&gt;Docker Compose on Hostinger VPS&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Clone and configure&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/vicente-r-junior/notion-life-review-os.git
&lt;span class="nb"&gt;cd &lt;/span&gt;notion-life-review-os
&lt;span class="nb"&gt;cp&lt;/span&gt; .env.example .env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Create 5 Notion databases&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Inside a parent page called &lt;strong&gt;Life Review OS&lt;/strong&gt;, create:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Daily Logs&lt;/li&gt;
&lt;li&gt;Tasks&lt;/li&gt;
&lt;li&gt;Projects&lt;/li&gt;
&lt;li&gt;Learnings&lt;/li&gt;
&lt;li&gt;Weekly Reports&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Copy each database ID into &lt;code&gt;.env&lt;/code&gt;. Connect your Notion integration to the parent page — it propagates to all children automatically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Configure &lt;code&gt;.env&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OPENAI_API_KEY=sk-...
NOTION_API_KEY=secret_...
NOTION_DB_DAILY_LOGS=...
NOTION_DB_TASKS=...
NOTION_DB_PROJECTS=...
NOTION_DB_LEARNINGS=...
NOTION_DB_WEEKLY_REPORTS=...
MCP_AUTH_TOKEN=any-random-string
EVOLUTION_API_URL=http://your-evolution-api:8080
EVOLUTION_API_KEY=...
EVOLUTION_INSTANCE=your-instance-name
WHATSAPP_NUMBER=5511999999999
REDIS_URL=redis://app-redis:6379
TIMEZONE=America/Sao_Paulo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. Start&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Point your Evolution API webhook to &lt;code&gt;http://your-server:8000/webhook&lt;/code&gt; and you're live.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;MCP response parsing trips you up the first time.&lt;/strong&gt; Every Notion MCP response is SSE-wrapped JSON inside a &lt;code&gt;content&lt;/code&gt; array. Once you have the unwrapping pattern it's trivial — but it's not obvious when you first hit it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One agent beats a pipeline.&lt;/strong&gt; I built the multi-agent version first. Extractor, matcher, confirmation, writer — each doing one thing. It looked clean on paper and was a nightmare in practice. Replacing it with a single conversational GPT-4o call and Redis history was the best decision I made on this project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The write step should never be an LLM.&lt;/strong&gt; Flexible conversation on the way in, deterministic code on the way out. That's the pattern that worked.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prompt design is the real work.&lt;/strong&gt; Getting the agent to always include &lt;code&gt;SAVE_PAYLOAD&lt;/code&gt; when there's actionable content, never say "done" without confirming, correctly handle corrections mid-conversation — that's where most of the iteration went. The code was the easy part.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Redis for everything.&lt;/strong&gt; Session state, schema cache, idempotency keys, conversation history — all in Redis with TTLs. No separate database needed. Cleanup is automatic.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>notionchallenge</category>
      <category>mcp</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
