DEV Community

Cover image for Build a Source-Grounded Social Engine With a Human Approval Gate
Agrici Daniel
Agrici Daniel

Posted on

Build a Source-Grounded Social Engine With a Human Approval Gate

Build a Source-Grounded Social Engine With a Human Approval Gate

I connected seven channels to a self-hosted Postiz instance.

That was the easy part.

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.

My workflow separates those decisions.

The system is:

  1. Current research
  2. An evidence ledger
  3. Platform-native drafts
  4. Human approval
  5. A Postiz preview
  6. Controlled scheduling
  7. Measurement that changes the next batch

Nothing publishes automatically. The automation ends at a reviewable draft until a human approves the complete asset.

1. Start with a dated research record

A trend is a time-bound observation, not a promise that a post will perform.

For each topic, I keep:

  • the source URL
  • the retrieval date
  • what the source directly supports
  • what I infer from it
  • the confidence level
  • the date the claim must be checked again

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.

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.”

That distinction is small in the copy and important for trust.

2. Put every public claim in an evidence ledger

The evidence ledger is the handoff between research and production.

A minimal record looks like this:

{
  "claim_id": "postiz-seven-connections",
  "claim": "Seven social integrations are connected and healthy.",
  "source": "Postiz Public API integration list",
  "observed_at": "2026-07-16T14:00:00+03:00",
  "confidence": "high",
  "expires_at": "2026-07-19T14:00:00+03:00",
  "limitation": "No provider has completed a controlled public test."
}
Enter fullscreen mode Exit fullscreen mode

The limitation is part of the record. Seven healthy connections do not mean seven proven publishing paths.

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.

3. Produce native assets from one evidence packet

The core idea can stay consistent. The delivery should change.

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.

The same paragraph pasted across seven networks is distribution, not adaptation.

I store each derivative as its own asset with:

  • a unique ID
  • the target integration
  • the scheduled local time and UTC offset
  • the copy path
  • the media paths
  • the provider settings
  • the destination
  • the source ledger
  • approval and Postiz state

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.

4. Make the human gate explicit

The reviewer approves the whole publication unit:

  • copy
  • media
  • claim source
  • account
  • destination
  • provider settings
  • time
  • rollback plan

The state machine is simple:

researched
  -> drafted
  -> media-ready
  -> approved
  -> postiz-draft
  -> scheduled
  -> published
  -> measured
Enter fullscreen mode Exit fullscreen mode

An asset cannot skip from drafted to scheduled because a script can construct the payload.

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.

5. Keep the Postiz operator dry-run by default

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

The safe interface keeps network writes explicit:

python3 scripts/postizctl.py health --skip-custom
python3 scripts/postizctl.py integrations
python3 scripts/postizctl.py batch production/manifest/2026-07-20--2026-08-02.json
Enter fullscreen mode Exit fullscreen mode

Those commands inspect or validate. They do not create anything.

A network write requires --send:

python3 scripts/postizctl.py batch \
  production/manifest/2026-07-20--2026-08-02.json \
  --asset W1-05-LI \
  --send
Enter fullscreen mode Exit fullscreen mode

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.

6. Validate provider rules before the request

Each provider has a different contract.

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.

The manifest translates those differences into provider settings:

{
  "platform": "linkedin",
  "settings": {
    "__type": "linkedin",
    "post_as_images_carousel": true
  }
}
Enter fullscreen mode Exit fullscreen mode

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.

7. Roll out one provider at a time

The first real post on each platform is a controlled test.

After it publishes, I verify:

  • the public URL
  • copy formatting
  • media order and quality
  • captions and thumbnails for video
  • destination behavior
  • provider-specific settings

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.

Week 2 stays on hold for a provider until its Week 1 controlled post succeeds.

8. Measure quality cost, not only reach

The useful metrics depend on the job of the post.

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.

I also track the cost of the system:

  • editing time
  • claim corrections
  • upload failures
  • provider failures
  • reply burden

A topic is not worth repeating when it creates more review and cleanup than useful action.

Current boundary

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.

That is the point of the gate. It keeps the public claim equal to the system that exists today.

Sources

Join the free AI Marketing Hub and share the workflow you want to put behind a human approval gate.

Top comments (0)