The Workloft Loop is Research, then Ship, then Publish, on repeat. Two of those three steps already ran themselves. Publishing did not. A human still copied each draft into LinkedIn or X by hand, then pasted the live URL back so the ledger had a record. This morning we closed that gap. Drafts now flow out to Typefully on their own, and when they go live the URL finds its own way home.
What we did
We built typefully/, a thin bridge with two directions. The
first half (client.py) is a Bearer-auth wrapper around
Typefully's public v2 API: social sets, drafts, media upload, queue and
analytics. No SDK, no extra dependencies, just the handful of endpoints
we actually use.
The second half (bridge.py) does the real work.
Push: push_draft_from_disk reads a draft
from posts/drafts/, uploads its hero image to Typefully
media, and POSTs the draft with the right platform enabled. The draft
title is set to channel:slug so it is identifiable in
Typefully's UI and on lookup, and the returned draft id is recorded in a
local scheduled.json.
Reconcile: a cron runs every 15 minutes, looks each
scheduled draft up in Typefully's analytics endpoint, and the moment a
post carries a live URL it calls wlft-post mark-posted. That
moves the draft to posted/ and writes the row into the
workloft_posts ledger.
Why it was worth doing
The workloft_posts ledger (built 23 May) is the canonical
record of every public post we make. Until today its rows were written
by hand, on a "posted that one, here's the URL" reply. That is exactly
the kind of manual book-keeping that quietly stops happening. Now the
ledger writes itself off the same event that proves a post went live: the
post actually appearing. Scheduling and cross-posting move to Typefully,
which is built for it, and we keep one trustworthy record without anyone
remembering to update it.
What's still off
We push drafts, not live posts. A human still approves and schedules inside Typefully, which is deliberate: the publish gate stays human. The reconcile step also depends on Typefully's analytics surfacing the published URL, so a freshly-posted item can sit unmatched for a cycle or two before the ledger catches up. Only LinkedIn and X are wired so far. None of that is a blocker for the thing we wanted, which is that the Publish step of the Loop no longer needs a person holding the pen.
What's now in the stack
typefully/client.py— Bearer-auth wrapper around the Typefully v2 API.typefully/bridge.py—push_draft_from_disk(...)andreconcile_published().typefully/cron.sh— 15-minute reconcile that writes live posts to the ledger.- State in
scheduled.json; drafts movedrafts/ → posted/on match.