← All case studies

A social post every day, that cannot publish without a human

Every business is sitting on data that already says what it should be posting — most viewed, most purchased, most interacted with this week. It just never gets posted. We built Dispatch to close that gap: it connects to the client's own data, turns whatever is performing into a finished post every morning, and then stops and waits for a person to say yes.

Client
Digital marketplaceName withheld at client request
Engagement
Custom build & handoffStandalone system, client infrastructure
Delivered
August 2026Built, tested and handed off
System
DispatchBackend, dashboard, CLI, MCP server

The risk was never a missed post

Before

Four manual jobs, every single day

Choosing an item cleared for promotion, rendering it to three aspect ratios, writing copy that carried the creator's attribution correctly, and posting on schedule. Done by hand, under deadline, the attribution is the step that slips — and publishing a creator's work without the credit their licence requires is a rights problem, not a marketing one.

After

A short review, and credit that cannot be removed

Dispatch drafts the post overnight, renders every format, writes the copy and locks the creator credit as immutable data. A person opens one screen, checks the licence, tunes the caption and approves. Publishing then happens on schedule, under lease, with duplicate delivery structurally prevented.

0

Posts that can reach a live account without human approval

3

Platform-ready crops rendered from every source item

0

Automated tests, re-run on a clean machine six weeks after handoff

Four surfaces, one authenticated data plane

Backend

Generation & publishing engine

A scheduled daily draft generator, a rendering pipeline, and a worker that wakes every five minutes to claim due posts and walk them through the provider's delivery lifecycle.

Frontend

Review dashboard

The human gate. Framing, caption, hashtags, rights check and schedule on a single screen, so approving a post is a short, unambiguous task.

Local tools

Command line & agent access

The same workflow from a terminal, or from an AI agent through a local MCP server. Credentials live in the operating system keychain — never a config file, never an environment variable.

Operations

Runbooks & release gates

Ten documented failure and recovery procedures, a deployment boundary enforced by a script, secret scanning across full version history, and three separately pinned dependency sets.

The dashboard, the command line tool and the agent interface are all clients of the same API. None of them reaches the database, the file store or the provider directly — which means every permission check happens in exactly one place.

It doesn't care what the data is

The client's catalogue ranks itself. Every day there is a list of whatever their users interacted with most, and Dispatch turns the top eligible entry into a finished post.

Partway through the build it became obvious that nothing in that pipeline is specific to what the items are. The selection rules, the licence checks, the rendering, the approval gate and the delivery are all indifferent to the subject matter. They only need a ranked list and a source record.

Point it at a different signal — top purchased, most viewed, most saved, highest rated, most returned — and the same machine runs. Adapting it to a new source is a connector, not a rebuild.

We set out to build one client a daily post. What we actually built was a pattern for any business whose own data already knows what's working.

Same machine, different signal

  • MarketplaceMost interacted-with listing today
  • Retail / e-commerceTop purchased item this week
  • Media / catalogueMost viewed or most saved
  • Marketplace supply sideNewest listing from a top-rated seller

Each row is the same daily run with a different query behind it. The review, rights and delivery stages are unchanged.

Seven steps, and a full stop in the middle

  1. A deterministic identity for the day

    The scheduler resolves the local date and mints a post ID from it. Running twice produces the same ID, which is what makes everything after this point safe to retry.

  2. A candidate chosen under licence rules

    The system reads the client’s curated catalogue — read-only, never written — then applies eligibility, licence class, a cooldown ledger so the same item cannot resurface, and a stable ranking. The public source listing is re-verified before it is used.

  3. The day claimed in one transaction

    A single database transaction claims the post, the daily run record and the usage record together. A retry after a crash resumes the same logical post instead of starting a second one.

  4. Copy written, credit locked

    Platform-specific bodies and alt text are generated, along with the creator, source, licence and modification notice — stored as an immutable block that ordinary copy edits cannot overwrite.

  5. Assets rendered — then the automation stops

    Three versioned crops are built into a private storage prefix. Hashes and metadata are recorded; storage paths are never exposed to a client. Nothing further happens until a person opens the dashboard.

  6. A person reviews and approves

    An editor checks framing, tunes the caption, confirms the licence and marks the post ready. Licences that need judgement escalate further: only a named administrator can clear those, and they must record an explicit basis.

  7. Publishing under lease

    A worker wakes every five minutes, transactionally claims a due post, takes a lease, reads the current token version from the secret store, checks quota and creates the delivery. The image is fetched through a short-lived link signed against that one job and that exact content.

What the reviewer actually sees

Captured from the delivered build running in its offline review mode — a state that loads fixture content and is structurally unable to reach the provider or the live API. Names and branding shown are placeholder fixture data.

The review desk
A three-column publishing dashboard: an item queue on the left, a framing canvas with rulers and a safe-area overlay in the centre, and a rights check and caption editor on the right.

Three columns, one job each. The day’s queue with draft, ready and live counts; the framing canvas with a safe-area overlay; and the review rail. The rights check sits at the top of that rail, flagged and tinted — the first thing in the reviewer’s eyeline rather than a checkbox at the bottom of a form.

Credit that cannot be edited out
The dashboard with a 9:16 story crop selected, showing a locked source credit block underneath the hashtag field.

Switching crop re-frames the asset without touching the copy. Below the hashtags sits the locked credit block: the work, the creator, their handle, and a plain statement that the image was cropped and resized — the modification disclosure the licence requires. The caption editor writes around it.

Format is a decision, not a default
The dashboard with a motion reel format selected, showing a vertical motion treatment preview derived from the still image.

A still-derived short reel treatment. The format choice has to be saved before the media is created, so a reviewer cannot approve one format and ship another. The header switches to “unsaved changes” until they do.

Destructive actions state their consequences
A confirmation dialog asking whether to use a different item, listing what will be rebuilt and what approvals will be cleared.

Swapping the day’s pick rebuilds images, captions and motion assets, and clears both the rights review and the Ready approval. The dialog names all four consequences, then answers the question the reviewer is actually asking: this does not publish anything. The safe choice holds focus.

Delivery is opt-in, per post
A delivery panel showing a secure handoff diagram, an automatic publish toggle, and a notice that the current mode is simulated.

Automatic publishing is a per-post switch, not a global setting. In the offline review mode shown here the panel keeps its full layout but marks itself simulated and states that no provider request can leave — the demo boundary is visible rather than silently inert.

Approval from a phone
The dashboard at phone width, stacked into a single column with a fixed save and next action bar at the bottom.

Three columns become one flow with a pinned action bar. Approving a post from a phone is a real workflow for a small team, so it was a delivery requirement rather than a courtesy breakpoint.

Getting a credential onto a laptop
A two-panel sign-in page explaining that the system only connects to a verified one-time request from a tool running on the same computer.

The command-line tool authenticates in the browser, then receives its credential through a one-time loopback callback — in the request body, never the address bar, never a log. This is the rejection state: a request that cannot be verified explains itself in plain language and refuses. The exchange succeeds only once the credential is durably stored and reads back byte for byte.

Closed by default
The signed-out screen reading “One pick. One finished post.” beside a technical drawing of a 4:5 format with the safe area enabled.

The unauthenticated state. No queue, no content, no counts — the shell renders and stops. The drawing at right is the product’s thesis in one image: a source item on the left, a finished post package on the right, and the format spec in between.

Attribution is enforced, not remembered

Most content automation treats credit as a caption convention — a template someone can edit out at eleven at night. Dispatch treats it as a data constraint.

The creator, source, licence class and modification notice are generated once at draft time and stored as a locked block. The copy editor writes around it. There is no path through the dashboard, the command line or the agent interface that removes it.

Licences are then classed by how much judgement they need. A permissive licence clears with an editor's checkbox. A licence that needs review cannot be approved by an editor at all, cannot be approved by a shared access key under any circumstances, and requires a named administrator to record an explicit basis for the decision.

Every edit is revision-checked, so two people working on the same post cannot silently overwrite each other — the second write is rejected rather than merged.

Authorisation, as delivered

CapabilityViewerEditorAdmin
Read posts, download assetsYesYesYes
Edit a draftNoYesYes
Regenerate copy or assetsNoYesYes
Schedule a publishNoYesYes
Approve review-class rightsNoNoNamed only
Manage the publishing connectionNoNoNamed only

Every row is enforced again on the server. Hiding a control in the interface is treated as presentation, not security.

We re-ran the delivery six weeks later

Not a claim carried over from the handoff. The repository was cloned to a clean machine, its pinned environments rebuilt from the committed lock files, and every suite executed.

318

Tests executed across four independent suites

317

Passing. One environment-specific failure, described below

4

Standing integrity checks passed: deployment boundary, secret scan, tooling manifest, dependency lock hashes

0

Console errors when the dashboard booted at desktop and phone width

The one failure

A test asserting that a demo media fixture re-encodes to identical bytes failed on the audit machine, because its video encoder is a different build from the one the fixture was pinned against. That is the test doing its job: it guards fixture reproducibility across environments and correctly reported an environment mismatch. No product code path is implicated.

We report it rather than omit it, because a case study that claims a perfect number is a case study nobody checked.

Automation people are willing to leave switched on

The interesting result is not that a daily post can be generated. It is that the client can leave a system running against a live account, holding a real token, on a five-minute loop — and not think about it.

That comes from a handful of decisions made early and held: a deterministic daily identity so a retry cannot double-post; transactions and leases so two workers cannot race; attribution as a locked field rather than a caption convention; a deployment boundary a script enforces; and a human gate the automation is structurally unable to route around.

The work that genuinely needed a person — is this creator credited correctly? — is now the thing the person is asked.