<?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: Nelson Lin</title>
    <description>The latest articles on DEV Community by Nelson Lin (@nelsonlin).</description>
    <link>https://dev.to/nelsonlin</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%2F3899825%2F4fd3fb25-6f36-4898-8da9-23364d1afd9f.jpg</url>
      <title>DEV Community: Nelson Lin</title>
      <link>https://dev.to/nelsonlin</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nelsonlin"/>
    <language>en</language>
    <item>
      <title>Notion-Like Headless CMS with API Publishing &amp; AI Writing Assistant</title>
      <dc:creator>Nelson Lin</dc:creator>
      <pubDate>Mon, 27 Apr 2026 10:20:03 +0000</pubDate>
      <link>https://dev.to/nelsonlin/notion-like-headless-cms-with-api-publishing-ai-writing-assistant-30g0</link>
      <guid>https://dev.to/nelsonlin/notion-like-headless-cms-with-api-publishing-ai-writing-assistant-30g0</guid>
      <description>&lt;h2&gt;
  
  
  Introduction:
&lt;/h2&gt;

&lt;p&gt;&lt;a href="//agent-engineering.dev"&gt;Agent-Engineering.dev&lt;/a&gt; is now a headless, Notion-like rich text CMS. Users can write posts and share them via API on their own platforms.&lt;/p&gt;

&lt;p&gt;Registered users can create posts with an excellent user experience using a rich text editor similar to Notion. Slash commands allow you to insert elements like tables, headers, and images.&lt;/p&gt;

&lt;p&gt;Agent Engineering includes an AI writing assistant to help craft better content. Built-in prompts for improving writing, correcting spelling, and expanding ideas save time and turn rough drafts into complete articles.&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%2Fw8qfir0374ovsarht2fa.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%2Fw8qfir0374ovsarht2fa.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Three Types of Publish Modes
&lt;/h2&gt;

&lt;p&gt;We support three publish modes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Public&lt;/li&gt;
&lt;li&gt;Semi-Public&lt;/li&gt;
&lt;li&gt;Private&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Posts published in Public mode appear in the agent-engineering.dev article feed, and subscribers receive notifications upon publication. This mode offers high visibility for sharing content on the platform.&lt;/p&gt;

&lt;p&gt;In Semi-Public mode, posts do not appear in the agent-engineering.dev article feed but are accessible via their URL. This is ideal for content unrelated to agent engineering, leveraging the platform for organic traffic without full exposure.&lt;/p&gt;

&lt;p&gt;Private posts are accessible only to their creator. Edit and view them on agent-engineering.dev, and share via API using your API key for authentication.&lt;/p&gt;

&lt;p&gt;To share posts on other platforms like your blog, publish them in Private mode. This prevents Google from indexing the content on agent-engineering.dev, avoiding duplicate content issues that could harm SEO.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to use API to share the content at the third parties, such your own blog system ?
&lt;/h2&gt;

&lt;p&gt;Sign-up and get your first API Key&lt;/p&gt;

&lt;p&gt;The first step is to sign-up. Create your first draft and publish it. After that, click the user avatar at the top right corner to go to developer page: &lt;a href="https://www.agent-engineering.dev/developer" rel="noopener noreferrer"&gt;https://www.agent-engineering.dev/developer&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Create your first API key by setting its name and expiration. The API key is similar to the password. The user who has the API key has the access to have your access to your published blog at agent-engineering.dev&lt;/p&gt;

&lt;p&gt;Get your first API result:&lt;/p&gt;

&lt;p&gt;Retrieve your published article using the endpoint: &lt;a href="https://www.agent-engineering.dev/api/v1/articles/:urlTitle" rel="noopener noreferrer"&gt;https://www.agent-engineering.dev/api/v1/articles/:urlTitle&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;GET https://www.agent-engineering.dev/api/v1/articles/:urlTitle&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The urlTitle is a slug from your published blog post that appears in the URL. For this post, it is: &lt;a href="https://www.agent-engineering.dev/article/fix-for-langgraph-compatibility-issues-executioninfo-importerror-with-langgraph-prebuilt" rel="noopener noreferrer"&gt;https://www.agent-engineering.dev/article/fix-for-langgraph-compatibility-issues-executioninfo-importerror-with-langgraph-prebuilt&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;fix-for-langgraph-compatibility-issues-executioninfo-importerror-with-langgraph-prebuilt is the urlTitle.&lt;/p&gt;

&lt;p&gt;For authentication, include your API key in the request header using one of these methods:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;x-api-key: &amp;lt;YOUR_API_KEY&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Authorization: Bearer &lt;/p&gt;

&lt;p&gt;An example is shown as below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"x-api-key: &amp;lt;YOUR_API_KEY&amp;gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"https://www.agent-engineering.dev/api/v1/articles/testing"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The example response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nl"&gt;"success"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"OK"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"article"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"…"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"…"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"imageUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"…"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"topic"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &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="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"clapCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"seoTitle"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"…"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"seoDescription"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"…"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"wordCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"minutesToRead"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"snippet"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"…"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"urlTitle"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"…"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"… or blocks JSON …"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"createdAt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-01-01T00:00:00.000Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"updatedAt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-01-01T00:00:00.000Z"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Two format of your content
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Block Format&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We provide two content formats for posts. The "blocks" format is the original structure saved in our database. We use BlockNote (&lt;a href="https://www.blocknotejs.org/" rel="noopener noreferrer"&gt;https://www.blocknotejs.org/&lt;/a&gt;) as our rich text editor. Its block schema matches BlockNote's default schema. For details, see the BlockNote schema documentation: &lt;a href="https://www.blocknotejs.org/docs/foundations/schemas" rel="noopener noreferrer"&gt;https://www.blocknotejs.org/docs/foundations/schemas&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Retrieving content as blocks ensures it remains in its original format without any changes.&lt;/p&gt;

&lt;p&gt;To render blocks on third-party platforms, we provide a Next.js solution. See the details here: &lt;a href="https://www.agent-engineering.dev/developer/blocknote" rel="noopener noreferrer"&gt;https://www.agent-engineering.dev/developer/blocknote&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Rendering methods may vary by frontend framework, but the standard approach is converting blocks to HTML. BlockNote provides an example here: &lt;a href="https://www.blocknotejs.org/examples/interoperability/converting-blocks-to-html" rel="noopener noreferrer"&gt;https://www.blocknotejs.org/examples/interoperability/converting-blocks-to-html&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Markdown Format&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Markdown is a lightweight rich text format supported by many platforms. Retrieving content as markdown avoids the need to install BlockNote utilities for HTML conversion.&lt;/p&gt;

&lt;p&gt;By default, content is returned in markdown format. To specify a format, add it as a query parameter to your API request:&lt;/p&gt;

&lt;p&gt;?format=markdown | block&lt;/p&gt;

&lt;p&gt;Last but not least, we highly recommend caching your API responses and invalidating the cache only when the content is modified. This reduces latency for subsequent retrievals from the source.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
