<?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: Hossein Rahimi</title>
    <description>The latest articles on DEV Community by Hossein Rahimi (@hoosseinrahimi).</description>
    <link>https://dev.to/hoosseinrahimi</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4236449%2Fd4ca6fb0-2827-4d45-a7e1-37ee737db3f2.jpg</url>
      <title>DEV Community: Hossein Rahimi</title>
      <link>https://dev.to/hoosseinrahimi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hoosseinrahimi"/>
    <language>en</language>
    <item>
      <title>I adopted an abandoned Notion AI proxy — and started with tests, CI and security</title>
      <dc:creator>Hossein Rahimi</dc:creator>
      <pubDate>Tue, 10 Nov 2026 06:55:23 +0000</pubDate>
      <link>https://dev.to/hoosseinrahimi/i-adopted-an-abandoned-notion-ai-proxy-and-started-with-tests-ci-and-security-oea</link>
      <guid>https://dev.to/hoosseinrahimi/i-adopted-an-abandoned-notion-ai-proxy-and-started-with-tests-ci-and-security-oea</guid>
      <description>&lt;p&gt;Notion AI is genuinely useful, but it only speaks Notion's own chat API. If you want Cursor, Postman, or any OpenAI Chat Completions client to talk to it, you need a translation layer.&lt;/p&gt;

&lt;p&gt;That layer exists. &lt;a href="https://github.com/mughu-id/Notion-AI-to-OpenAI-Compatible" rel="noopener noreferrer"&gt;NotionChat&lt;/a&gt;, originally built by mughu-id, is a FastAPI proxy that routes OpenAI-format requests to Notion AI's &lt;code&gt;runInferenceTranscript&lt;/code&gt; endpoint using your browser session cookie, and streams the NDJSON response back as OpenAI chunks. It reached 90 stars on GitHub.&lt;/p&gt;

&lt;p&gt;Then it went quiet. The last upstream push was in July 2026.&lt;/p&gt;

&lt;p&gt;I rely on tools like this, and an unmaintained proxy that holds my Notion session cookie is not something I want to just leave lying around. So instead of forking-and-forgetting or rewriting from scratch, I adopted it: a &lt;a href="https://github.com/HoosseinRahimi/Notion-AI-to-OpenAI-Compatible" rel="noopener noreferrer"&gt;maintained continuation&lt;/a&gt; with the full history and MIT license preserved.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why adopt instead of rewrite
&lt;/h2&gt;

&lt;p&gt;The hard part of this codebase — parsing Notion's transcript protocol and reassembling streamed responses — already worked and had real users. What was missing was everything &lt;em&gt;around&lt;/em&gt; it: there were no tests, no CI, no security policy, and no Responses API endpoint.&lt;/p&gt;

&lt;p&gt;Rewriting would have thrown away the working core to rebuild the easy parts. Adopting meant the opposite order of work, and honestly, a better discipline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rule one: tests before changes
&lt;/h2&gt;

&lt;p&gt;You can't responsibly change code you didn't write until you can prove what it currently does. So the first thing the continuation got was a characterization test suite — ten test files covering:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;chat completions (streaming and non-streaming)&lt;/li&gt;
&lt;li&gt;NDJSON parsing&lt;/li&gt;
&lt;li&gt;model listing and aliases&lt;/li&gt;
&lt;li&gt;concurrency and thread state&lt;/li&gt;
&lt;li&gt;the tools bridge&lt;/li&gt;
&lt;li&gt;security behavior (auth checks, bind protection)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Only with that safety net in place did I start changing behavior.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the continuation adds
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;A test suite and CI.&lt;/strong&gt; Ten test files where there were zero, a GitHub Actions workflow running them on every push, dependabot, a &lt;code&gt;SECURITY.md&lt;/code&gt;, and a changelog.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Security hardening.&lt;/strong&gt; This proxy holds a live Notion session cookie, so the details matter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API keys are verified with &lt;code&gt;secrets.compare_digest&lt;/code&gt; (timing-safe comparison)&lt;/li&gt;
&lt;li&gt;the server refuses to bind to &lt;code&gt;0.0.0.0&lt;/code&gt; with a default or empty API key&lt;/li&gt;
&lt;li&gt;state files are written atomically with POSIX &lt;code&gt;0600&lt;/code&gt; permissions&lt;/li&gt;
&lt;li&gt;upstream error responses are sanitized before they reach clients&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The OpenAI Responses API.&lt;/strong&gt; Next to &lt;code&gt;POST /v1/chat/completions&lt;/code&gt;, the continuation adds &lt;code&gt;POST /v1/responses&lt;/code&gt;, mapped to and from Chat Completions. &lt;code&gt;GET /v1/models&lt;/code&gt; is built dynamically from Notion's available models, with friendly aliases (&lt;code&gt;opus-4.8&lt;/code&gt;, &lt;code&gt;gpt-4o&lt;/code&gt;, &lt;code&gt;sonnet-4.6&lt;/code&gt;) mapped to Notion's internal IDs — and a stale-cache fallback if Notion can't be reached. Streaming is standard append-only SSE, the way OpenAI clients expect it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quickstart
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/HoosseinRahimi/Notion-AI-to-OpenAI-Compatible.git
&lt;span class="nb"&gt;cd&lt;/span&gt; Notion-AI-to-OpenAI-Compatible
python -m venv .venv
&lt;span class="nb"&gt;source&lt;/span&gt; .venv/bin/activate        &lt;span class="c"&gt;# Windows: .venv\Scripts\activate&lt;/span&gt;
pip install -r requirements.txt
pip install -e .
cp .env.example .env             &lt;span class="c"&gt;# set NOTIONCHAT_API_KEY&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Grab your Notion cookie from the browser (DevTools → Application → Cookies — you need &lt;code&gt;token_v2&lt;/code&gt;), then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;notion init --cookie &lt;span class="s2"&gt;"notion_browser_id=...; token_v2=..."&lt;/span&gt;
notion serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The server runs at &lt;code&gt;http://127.0.0.1:1994&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://127.0.0.1:1994/v1/models \
  -H &lt;span class="s2"&gt;"Authorization: Bearer &amp;lt;redacted&amp;gt;"&lt;/span&gt;

curl http://127.0.0.1:1994/v1/chat/completions \
  -H &lt;span class="s2"&gt;"Authorization: Bearer &amp;lt;redacted&amp;gt;"&lt;/span&gt; \
  -H &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; \
  -d &lt;span class="s1"&gt;'{"model": "gpt-4o", "messages": [{"role": "user", "content": "Hello"}]}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Point Cursor or any other OpenAI client at &lt;code&gt;http://127.0.0.1:1994/v1&lt;/code&gt; and it just works.&lt;/p&gt;

&lt;h2&gt;
  
  
  Honest limitations
&lt;/h2&gt;

&lt;p&gt;This is an &lt;strong&gt;unofficial&lt;/strong&gt; project, not affiliated with Notion. It authenticates with your browser cookie, so Notion's Terms of Service and your workspace plan limits apply. One operational gotcha worth knowing: Notion ties AI permission to the IP address where you logged in, so a cookie copied from your home PC to a VPS often fails with &lt;code&gt;403 trust-rule-denied&lt;/code&gt;. Run it on the machine (or exit IP) where you logged in, or route it through &lt;code&gt;NOTION_PROXY&lt;/code&gt;. Tool calling is experimental and feature-gated, because Notion AI has no native function calling — the bridge compiles model text into &lt;code&gt;tool_calls&lt;/code&gt; with guardrails.&lt;/p&gt;

&lt;h2&gt;
  
  
  What adoption taught me
&lt;/h2&gt;

&lt;p&gt;Maintaining someone else's code is a different discipline from writing your own. You earn the right to change things by first proving — with tests — that you understood them. The boring work (tests, CI, hardening) &lt;em&gt;is&lt;/em&gt; the contribution; features are what it unlocks.&lt;/p&gt;

&lt;p&gt;If the proxy saves you time, a star on the &lt;a href="https://github.com/HoosseinRahimi/Notion-AI-to-OpenAI-Compatible" rel="noopener noreferrer"&gt;maintained repo&lt;/a&gt; helps the next person find the version that's still alive. Issues and PRs are welcome — and if the original project resumes, I'll happily upstream everything.&lt;/p&gt;

</description>
      <category>python</category>
      <category>opensource</category>
      <category>fastapi</category>
      <category>llm</category>
    </item>
  </channel>
</rss>
