<?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: Agrici Daniel</title>
    <description>The latest articles on DEV Community by Agrici Daniel (@agricidaniel).</description>
    <link>https://dev.to/agricidaniel</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%2F3979633%2Fbebad882-f3c6-4567-bbcf-12316214ec8f.jpg</url>
      <title>DEV Community: Agrici Daniel</title>
      <link>https://dev.to/agricidaniel</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/agricidaniel"/>
    <language>en</language>
    <item>
      <title>Build a Source-Grounded Social Engine With a Human Approval Gate</title>
      <dc:creator>Agrici Daniel</dc:creator>
      <pubDate>Fri, 24 Jul 2026 06:00:01 +0000</pubDate>
      <link>https://dev.to/agricidaniel/build-a-source-grounded-social-engine-with-a-human-approval-gate-5gpp</link>
      <guid>https://dev.to/agricidaniel/build-a-source-grounded-social-engine-with-a-human-approval-gate-5gpp</guid>
      <description>&lt;h1&gt;
  
  
  Build a Source-Grounded Social Engine With a Human Approval Gate
&lt;/h1&gt;

&lt;p&gt;I connected seven channels to a self-hosted Postiz instance.&lt;/p&gt;

&lt;p&gt;That was the easy part.&lt;/p&gt;

&lt;p&gt;The difficult part is preventing a research note, an outdated number, or a generic draft from moving straight into a public calendar. A healthy API connection proves that Postiz can reach an account. It does not prove that the claim is current, the copy fits the platform, the media is correct, or the publication time is approved.&lt;/p&gt;

&lt;p&gt;My workflow separates those decisions.&lt;/p&gt;

&lt;p&gt;The system is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Current research&lt;/li&gt;
&lt;li&gt;An evidence ledger&lt;/li&gt;
&lt;li&gt;Platform-native drafts&lt;/li&gt;
&lt;li&gt;Human approval&lt;/li&gt;
&lt;li&gt;A Postiz preview&lt;/li&gt;
&lt;li&gt;Controlled scheduling&lt;/li&gt;
&lt;li&gt;Measurement that changes the next batch&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Nothing publishes automatically. The automation ends at a reviewable draft until a human approves the complete asset.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Start with a dated research record
&lt;/h2&gt;

&lt;p&gt;A trend is a time-bound observation, not a promise that a post will perform.&lt;/p&gt;

&lt;p&gt;For each topic, I keep:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the source URL&lt;/li&gt;
&lt;li&gt;the retrieval date&lt;/li&gt;
&lt;li&gt;what the source directly supports&lt;/li&gt;
&lt;li&gt;what I infer from it&lt;/li&gt;
&lt;li&gt;the confidence level&lt;/li&gt;
&lt;li&gt;the date the claim must be checked again&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Mutable repository counts, versions, product behavior, prices, and social metrics expire quickly. A draft can stay useful while the number inside it becomes wrong. The refresh date makes that visible before publication.&lt;/p&gt;

&lt;p&gt;Qualitative discussions need another boundary. Three useful community threads can reveal language, objections, and workflow pain. They do not represent the entire market. I write “the discussions clustered around these jobs,” not “marketers want this.”&lt;/p&gt;

&lt;p&gt;That distinction is small in the copy and important for trust.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Put every public claim in an evidence ledger
&lt;/h2&gt;

&lt;p&gt;The evidence ledger is the handoff between research and production.&lt;/p&gt;

&lt;p&gt;A minimal record looks like this:&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"claim_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;"postiz-seven-connections"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"claim"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Seven social integrations are connected and healthy."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Postiz Public API integration list"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"observed_at"&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-07-16T14:00:00+03:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"confidence"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"high"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"expires_at"&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-07-19T14:00:00+03:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"limitation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"No provider has completed a controlled public test."&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;p&gt;The limitation is part of the record. Seven healthy connections do not mean seven proven publishing paths.&lt;/p&gt;

&lt;p&gt;In my current setup, Pinterest is connected but Postiz returns no board. The ten Pin assets can be completed, reviewed, and stored. They cannot become schedules until the integration returns a real board ID.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Produce native assets from one evidence packet
&lt;/h2&gt;

&lt;p&gt;The core idea can stay consistent. The delivery should change.&lt;/p&gt;

&lt;p&gt;An X post needs a compact observation and one action. LinkedIn can explain the operating decision across a carousel. Mastodon needs enough context to stand alone. Pinterest needs a durable, keyword-forward title and a vertical visual. Dev.to should hold the full implementation, not a padded social caption.&lt;/p&gt;

&lt;p&gt;The same paragraph pasted across seven networks is distribution, not adaptation.&lt;/p&gt;

&lt;p&gt;I store each derivative as its own asset with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a unique ID&lt;/li&gt;
&lt;li&gt;the target integration&lt;/li&gt;
&lt;li&gt;the scheduled local time and UTC offset&lt;/li&gt;
&lt;li&gt;the copy path&lt;/li&gt;
&lt;li&gt;the media paths&lt;/li&gt;
&lt;li&gt;the provider settings&lt;/li&gt;
&lt;li&gt;the destination&lt;/li&gt;
&lt;li&gt;the source ledger&lt;/li&gt;
&lt;li&gt;approval and Postiz state&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This matters because approval of the topic is not approval of every derivative. One Pin may have a weak destination. One carousel may contain a stale figure. One Short may expose a private screen. Every public asset needs its own decision.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Make the human gate explicit
&lt;/h2&gt;

&lt;p&gt;The reviewer approves the whole publication unit:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;copy&lt;/li&gt;
&lt;li&gt;media&lt;/li&gt;
&lt;li&gt;claim source&lt;/li&gt;
&lt;li&gt;account&lt;/li&gt;
&lt;li&gt;destination&lt;/li&gt;
&lt;li&gt;provider settings&lt;/li&gt;
&lt;li&gt;time&lt;/li&gt;
&lt;li&gt;rollback plan&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The state machine is simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;researched
  -&amp;gt; drafted
  -&amp;gt; media-ready
  -&amp;gt; approved
  -&amp;gt; postiz-draft
  -&amp;gt; scheduled
  -&amp;gt; published
  -&amp;gt; measured
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An asset cannot skip from &lt;code&gt;drafted&lt;/code&gt; to &lt;code&gt;scheduled&lt;/code&gt; because a script can construct the payload.&lt;/p&gt;

&lt;p&gt;Representative previews come first. I create one draft for each provider, inspect the rendered result inside Postiz, and correct the shared template. Only then does the batch move forward.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Keep the Postiz operator dry-run by default
&lt;/h2&gt;

&lt;p&gt;The local operator uses the self-hosted Public API documented by Postiz. Media goes through &lt;code&gt;POST /public/v1/upload&lt;/code&gt;. Posts go through &lt;code&gt;POST /public/v1/posts&lt;/code&gt;. The API supports &lt;code&gt;draft&lt;/code&gt;, &lt;code&gt;schedule&lt;/code&gt;, and &lt;code&gt;now&lt;/code&gt; post types.&lt;/p&gt;

&lt;p&gt;The safe interface keeps network writes explicit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 scripts/postizctl.py health &lt;span class="nt"&gt;--skip-custom&lt;/span&gt;
python3 scripts/postizctl.py integrations
python3 scripts/postizctl.py batch production/manifest/2026-07-20--2026-08-02.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those commands inspect or validate. They do not create anything.&lt;/p&gt;

&lt;p&gt;A network write requires &lt;code&gt;--send&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;python3 scripts/postizctl.py batch &lt;span class="se"&gt;\&lt;/span&gt;
  production/manifest/2026-07-20--2026-08-02.json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--asset&lt;/span&gt; W1-05-LI &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--send&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The operator hashes every media file before upload. If the exact file already has a valid Postiz receipt, the upload is reused. The receipt stores the media ID, public path, hash, and upload time. It never stores the API key.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Validate provider rules before the request
&lt;/h2&gt;

&lt;p&gt;Each provider has a different contract.&lt;/p&gt;

&lt;p&gt;In this self-hosted instance, the live non-Premium X schema returns a 200-character limit. LinkedIn image carousels need at least two images. Pinterest requires media and a board. Dev.to needs a title and accepts up to four tags in the current schema. YouTube needs one MP4 plus a title and visibility setting.&lt;/p&gt;

&lt;p&gt;The manifest translates those differences into provider settings:&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"platform"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"linkedin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"settings"&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;"__type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"linkedin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"post_as_images_carousel"&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="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;p&gt;Local validation catches missing settings, wrong integration mappings, expired timestamps, missing media, character-limit failures, and duplicate asset IDs before the API sees the payload.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Roll out one provider at a time
&lt;/h2&gt;

&lt;p&gt;The first real post on each platform is a controlled test.&lt;/p&gt;

&lt;p&gt;After it publishes, I verify:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the public URL&lt;/li&gt;
&lt;li&gt;copy formatting&lt;/li&gt;
&lt;li&gt;media order and quality&lt;/li&gt;
&lt;li&gt;captions and thumbnails for video&lt;/li&gt;
&lt;li&gt;destination behavior&lt;/li&gt;
&lt;li&gt;provider-specific settings&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If X fails, the X rows stop. LinkedIn, Bluesky, Mastodon, Dev.to, and YouTube do not need to stop with it. Provider isolation keeps one integration problem from turning into a batch-wide incident.&lt;/p&gt;

&lt;p&gt;Week 2 stays on hold for a provider until its Week 1 controlled post succeeds.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Measure quality cost, not only reach
&lt;/h2&gt;

&lt;p&gt;The useful metrics depend on the job of the post.&lt;/p&gt;

&lt;p&gt;For reach, I track impressions or views. For interest, I track qualified replies, saves, profile visits, and completion rate where the platform exposes them. For trust, I track clicks to the full guide, repository visits, and video watch depth. For conversion, I track attributable Free Hub visits and join starts.&lt;/p&gt;

&lt;p&gt;I also track the cost of the system:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;editing time&lt;/li&gt;
&lt;li&gt;claim corrections&lt;/li&gt;
&lt;li&gt;upload failures&lt;/li&gt;
&lt;li&gt;provider failures&lt;/li&gt;
&lt;li&gt;reply burden&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A topic is not worth repeating when it creates more review and cleanup than useful action.&lt;/p&gt;

&lt;h2&gt;
  
  
  Current boundary
&lt;/h2&gt;

&lt;p&gt;As of July 16, 2026, the app, Public API, MCP endpoint, and seven integrations are healthy. Pinterest scheduling remains blocked because no board is returned. No provider has completed a controlled Postiz publish test. The two-week batch is a reviewed production system, not proof of an autonomous publishing loop.&lt;/p&gt;

&lt;p&gt;That is the point of the gate. It keeps the public claim equal to the system that exists today.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.postiz.com/public-api/uploads/upload-file" rel="noopener noreferrer"&gt;Postiz: upload a file&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.postiz.com/public-api/posts/create" rel="noopener noreferrer"&gt;Postiz: create a post&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.postiz.com/public-api/posts/get-posts" rel="noopener noreferrer"&gt;Postiz: list posts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.postiz.com/public-api/posts/delete-post" rel="noopener noreferrer"&gt;Postiz: delete a post&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Join the &lt;a href="https://www.skool.com/ai-marketing-hub/about" rel="noopener noreferrer"&gt;free AI Marketing Hub&lt;/a&gt; and share the workflow you want to put behind a human approval gate.&lt;/p&gt;

</description>
      <category>automation</category>
      <category>socialmedia</category>
      <category>opensource</category>
      <category>marketing</category>
    </item>
  </channel>
</rss>
