Social Automation

How databayt drafts, approves, and broadcasts social posts across eight platforms — the Claude-native + Hermes pipeline, one brand at a time.

Social Automation

How databayt turns an idea into a published social post across eight platforms — with Claude writing the copy, /higgs making the media, and a thin gateway doing the broadcast. Each brand (databayt, Hogwarts, Mkan, Moallimee, Sijillee) runs its own presence off this shared pipeline. The brand rules every post obeys are in Brand & Voice; the build roadmap and credential plan are internal (docs/SOCIAL-AUTOMATION.md).

Business models → who we're talking to

databayt runs on four business models. Social marketing serves each one differently — B2B storytelling for the first three, B2C reach for the SaaS products.

ModelTypeWhat it sellsSocial job
Per ProjectB2BCustom automation builds for partnersShow the work — case studies, "automate the boring" proof
PartnersB2BOutsourced product dev + specialized talentAttract teams & talent — founder story, capability posts
CodebaseB2B / B2CReusable components, contributor revenue-share (the sharing economy)Grow the contributor community — open-source, dev-rel
SaaSB2C / B2BReady-to-deploy products (Hogwarts, Mkan, Souq, Shifa, …)Sell the product — per-brand consumer & buyer marketing

The first three are the databayt umbrella story (B2B, developer- and partner-facing). The fourth is where the product brands live — each with its own audience, channel mix, and voice.

Strategy

How we actually win is its own page — Strategy: six lenses (organic · viral · when · grow · pay off · moral), the create-once-repurpose-everywhere multiplier that makes 5 brands × 8 channels feasible for a small team, per-brand cadence, and the kill criteria that keep social honest against the cash-flow-first drive.

Brand pages

Every brand has its own presence. Its audience, channel mix, content pillars, and account status live on its page:

  • Databayt — the umbrella: open source, sharing economy, Kun & dev-rel (B2B)
  • Hogwarts — school management SaaS (B2B schools + B2C parents)
  • Mkan — property rentals (B2C renters + B2B hosts)
  • Moallimee — teachers & tutoring (pre-launch)
  • Sijillee — Sudan business accounting (pre-launch) (B2B)

Souq and Shifa are live SaaS products but are not in the current priority-5 social push. When they join, they get a page on this same template.

The pipeline

calendar ══► draft ══════════════► review ─► publish ─────────► measure
    │          │  ▲                   │         │                  │
  pillars   Claude │  media        human      drain | hermes  SocialMetric
  rotate    AR-first│ (pick|make)   gate      | copy-out       + UTM tags
  into the         └── higgs · carousel      ▲ HARD STOP
  queue                library.json          (/social/publish)

A full draft is copy AND/OR media — text · text + image(s) · text + video · image(s) only · video only. The stages are wired to each other: pillars seed the draft queue, media attaches to the draft (SocialDraftRequest.mediaUrls), and the answered full draft queues for a human on /social/publish.

StageOwnerNotes
Plancalendar skill · weekly cadence · growth agent pillarsWhat to say, to whom, when. content/social/pillars.json IS the recurring calendar — the Monday seeder rotates it into the draft queue, and /social/calendar renders it with a Queue now button
Draftdraft skill — Claude, natively — or the Social Agent window on /socialArabic-first bilingual copy against Brand & Voice, plus the media half. The window queues the brief and a Claude Code session on the Max pool answers it — the API lane of D-20260730 has no funded key, so drafting stays on the subscription. Contributor-gated
Mediamedia keyword → /higgs (text-free) · /carousel (text-bearing)Pick before you generate: library.json by brand + type, or the brief's (library: id) hint. New renders register in the library, then attach to the draft (social-drafts.mjs attach, or the showroom's Attach)
Approveapprove skill → human (Abdout / Ali / Samia)The gate — the review queue on /social/publish is the primary lane; a signed 12h link covers a remote approver. See Autonomy ladder
ScheduleSocialVariant.scheduledFor + /api/social/drainThe review queue's approve-mode setting: publish right away, or schedule and delegate to the drain, which publishes due variants every ~15 minutes from GitHub Actions. No keyword of its own — it is publish's second verb ("schedule it for tomorrow")
Publishpublish skill → egress layer — Telegram + Facebook direct APIs · Hermes-transport channels via the gateway · WhatsApp as a copy-out blockEgress only — dumb relays, never the LLM. Media routes by shape: text · one photo · 2–10 album/carousel · one video; mixed kinds are refused by name
Measuremeasure skill → /api/social/metricsSocialMetricFacebook reach + engagement, six-hourly. UTM tags are applied but not yet consumed — no PostHog in the app

Each stage is also its own keyword: say "draft a post for hogwarts" and only that stage runs. social is the orchestrator that chains all six.

The Hub is this table, as routes

/social mirrors the pipeline: seven stages across five routes (/social/<stage>), in order, with brand · channels · status above them as context that spans all of them. Each stage is a page — linkable, shareable — and the bare /social opens on Draft.

RouteStagesWhat is there
/social/calendarPlanThe pillars, live: each brand's briefs with this ISO week's rotation picks highlighted (the same math the Monday seeder runs), a queue chip per brief — in the queue · answered · approved · dismissed — and Queue now to file one as a draft ask on the spot
/social/draftDraftThe Social Agent window. Shows the answered draft's media beside its copy; "Use …" hands copy and media to the review queue
/social/mediaMediaThe showroom — generated assets (library.json) and kept references (references.json), filterable by brand and type; decks link to their live renders. Attach puts an asset in the publish tray — no copy-pasting URLs
/social/publishApprove · Schedule · PublishThe review queue: the next answered full draft awaiting approval, every upcoming one browsable, an editor that fine-tunes copy, attachments and channels — and never creates from blank. Approve (now, or scheduled per the settings), Dismiss, or send the signed review link. An empty queue points back at Draft
/social/measureMeasureThe activity ledger — last 20 variants with status, time, and latest reach/views

Cross-stage state lives in the layout's provider, which persists across stage navigations: the agent's queue poll runs on for minutes, the editor keeps its copy, and the attachment tray survives a trip to the showroom and back — switching stages must not throw any of it away. Taking a draft ("Use Arabic") moves the Hub to Publish carrying copy, media, and the request id, so approving there consumes the queue entry instead of leaving it approvable twice.

Drafting from the Hub — the two-sided loop

The agent window is how a teammate without a Claude Code session gets copy on the Max subscription. It has two halves and both have to run:

WhoWhereDoes
Any contributor/socialDraftPicks the brand, writes a brief, presses send. The ask becomes a SocialDraftRequest and the window polls
Any Claude Code sessionthe repoSays draft with no topic → queue mode drains the pending asks through the same brand-voice doctrine, and writes the copy back
node scripts/social-drafts.mjs list                                    # what is waiting (incl. mediaUrls)
node scripts/social-drafts.mjs answer <id> --ar ar.txt --en en.txt \
  --media "https://cdn…/hero.png"                                     # the copy + its media half
node scripts/social-drafts.mjs attach <id> --media "url1,url2"         # media alone, after a render
node scripts/social-drafts.mjs fail   <id> --note "what is missing"    # shown to the asker

The brief is the whole input. The answering session receives the brand and the brief and nothing else, so a brief that names only a topic gets copy that says only a topic. Name the fact, the audience, the call to action, and any date, name or link that must appear. fail --note is rendered to the asker verbatim — use it to ask for the missing piece rather than inventing one.

The queue drains on a schedule: scripts/drain-drafts.sh runs every 5 minutes from launchd on a teammate's Mac, beats a draft-drain heartbeat on every look (even when empty), and only invokes Claude when asks are pending — zero tokens idle. The window shows the queue's truth while waiting (position + last check), backs off its poll, and stops after 10 minutes into a "still queued" state; the drain sweep expires asks nobody answered within an hour — see Current status.

Which of the three loops a post travels — Hub, cron draft-and-approve, or the inbound Hermes relay — plus the actor map, the cron inventory, and the keyword table, are on Architecture & Loops.

Layer boundaries

The one rule that keeps the architecture honest: Hermes is a broadcast relay, not a brain. Anthropic offers no native "post to X" primitive, so a self-hosted last-mile gateway is a legitimate egress — but the writing stays Claude-native. This is what reconciles a self-hosted gateway with the "anthropic-native over self-hosted" doctrine.

ConcernLayerWhy here
Write the copyClaude (in Claude Code / Cowork)Best model, brand-aware, bilingual — never Hermes' LLM
Make the image/video/higgs → Higgsfield CLIAlready governed by budget + brand kit
Decide it's goodHuman (→ automated gate at L4)Brand accounts are public and irreversible
Reach the platformEgress layer, routed per transportTelegram and Facebook: direct APIs (plain HTTPS — works from Vercel), drained by kun. Other channels via the Hermes gateway, which pulls its own work. WhatsApp: no API exists, so a copy-out block
Prove it worked/api/social/metrics + UTMFacebook reach and engagement into SocialMetric; UTM tags attribute traffic at our own site

Note what is not in that table: Slack. It is where approvals and notices are relayed, which is a different job from reaching an audience — see the channel model.

Done (Phase 2, 2026-07-10). The old generateAndPublishPost action drafted via the gateway's own LLM and never published — a name that lied twice. Rather than rename it, Phase 2 removed the in-app draft lane entirely: drafting is Claude-native via the /social skill, and the hub is compose → stage → publish only.

The eight channels

The full set: Facebook, Instagram, Telegram, TikTok, Snapchat, X, LinkedIn, WhatsApp. Each has a very different posting-API reality — some trivial, some paid, some with no organic-post API at all. Build order follows feasibility, not wish-list:

ChannelPosting APIEffortNote
TelegramBot APITrivialWired direct (2026-07-10) but not configured — the token was never pushed to Vercel and no TELEGRAM_CHANNEL_ID exists. Two variables from live
XAPI v2Easy (paid)Pay-per-use since Feb 2026 (~$0.015/post) → /decide
FacebookGraph APIMediumThe one live channel — hogwarts, mkan, databayt
InstagramGraph APIMediumBusiness account linked to a FB Page
LinkedInPosts APIMediumCommunity Management API approval + page admin
TikTokContent Posting APIHardApp audit (~1–4 wks); posts private until passed
SnapchatPublic Profile APILimitedAllowlist-only via a Snap contact
WhatsApp(none, ever)ManualNo organic posting API for Channels or broadcast lists — not a gate to pass, a capability Meta does not offer. Modelled as transport: "manual": drafted, rendered, and approved like any channel, then handed out as a copy-out block for a human to forward

Per-channel state — wired vs configured vs live — is on Status, which is the page to trust when the two disagree.

And one channel that is not one of them

Slack is the communication channel, not a distribution channel. It is the team surface where approvals and notices land, and it is deliberately absent from the table above: counting an internal chat as audience reach is how "1 of 9 channels live" came to describe a system with one live audience channel. The composer does not offer it, the write gate rejects it, and /api/social/relay refuses it by name. Nothing needs selecting — sendReview routes there unconditionally.

The registry expresses this directly: a channel carries a kind (distribution | communication) that says what it is for, orthogonal to the transport that says how its bytes move.

Full matrix, costs, and the aggregator option (Ayrshare covers 7 of 8) are in the internal roadmap. Per-brand channel priorities are on each brand page.

Autonomy ladder

The north star is fully autonomous publishing. We climb to it in stages — not because autonomy is hard to code, but because handing a public brand account to an unsupervised bot needs guardrails first. We are at L2 on Facebook — the one live channel: compose or schedule in the Hub, one-click publish from a signed approval link, drain delivers — and L1 elsewhere. (This previously read "L2 on Telegram", which had the ladder pointing at the channel that cannot publish at all rather than the one that can.)

LevelWhat runsHuman role
L0 — ManualHuman writes and postsEverything
L1 — AssistedClaude drafts + /higgs mediaHuman posts manually
L2 — Stagedkun stages copy+media to the dashboardOne-click approve → publish
L3 — Semi-autoAuto-publish low-risk channels (Telegram); gate brand channelsApproves brand posts only
L4 — Full-autoAutonomous end-to-endNone — replaced by an automated gate

L4 is gated behind /decide + credentials + an automated guardrail layer: an LLM-judge content + Arabic-correctness check, per-channel rate limits, and a kill switch. Autonomy without that layer is not a feature — it's an incident waiting to happen.

The keywords

Six stage keywords — the pipeline table's seven rows fold onto six words because Schedule rides publish. Each runs on its own; social chains all of them, and carousel is the multi-slide sibling with its own chapter.

KeywordStageSay
calendarPlan"what are we publishing next week"
draftWrite"draft a post for hogwarts about admission"
higgsMedia"make the image for it"
approveGate"send it for review"
publishDeliver + schedule"publish it" · "schedule it for tomorrow"
measureRead back"how did the post do"
socialAll six"social post for mkan about the new listings"
carouselMulti-slide"carousel for hogwarts intro"

They were split out (2026-07-27) because the pipeline above had named these stages and assigned them owners for months while four of them had no keyword — and Abdout types prose, not slash commands, so a stage with no name is a stage that cannot be reached. Each skill's when_to_use carries an explicit negative clause naming its siblings, which is what stops "publish it" from routing to /ship.

The growth agent owns the strategy layer above them; .claude/vocabulary.json holds them all in the Pensieve school. No slash needed — say the word.

Current status

The Social Hub at /social is multi-product (Phase 3, 2026-07-23):

  • Works — bilingual (en/ar, RTL) composer with a config-driven channel registry (8 distribution channels, plus Slack as the one communication channel, which the picker does not offer) × a product registry (hogwarts · mkan · databayt · sijillee · moallimee). A product select — same visual language as the shadcn theme selector — scopes the whole page: channel toggles, health checks, and publish all carry the chosen brand.
  • Per-product Facebook — each brand has its own Page and its own permanent Page token, read from FACEBOOK_PAGE_ID_<PRODUCT> / FACEBOOK_PAGE_ACCESS_TOKEN_<PRODUCT>. The Egress panel names the Page the token resolved to, which is how you catch a crossed token before publishing. Live for hogwarts, mkan, and databayt; sijillee and moallimee have no Page yet.
  • Per-product wiring — a channel is publishable only when the global transport and that brand's own destination are wired (productChannelWired). Telegram stays databayt-only: there is exactly one Telegram channel, org-level, so claiming it for a product brand would post "as Hogwarts" into the org channel. Slack is excluded structurally rather than per-brand — it is a communication channel, and productChannelWired refuses it for every product.
  • Auto-post is draft-and-approve — a daily Vercel cron (/api/social/cron, CRON_SECRET-guarded) drafts per opted-in product and sends the draft to a private review channel with a signed, 12-hour, single-use Publish link. The link opens a read-only confirm page and only its POSTed button publishes — a chat client's link-preview crawler can never consume an approval. Nothing publishes unattended. Opt in per product with SOCIAL_AUTOPOST_PRODUCTS; empty (the default) means the cron does nothing.
  • Inbound relay — because Vercel can never reach Hermes on localhost, /api/social/relay inverts the arrow: Hermes drafts, a human approves in Slack, Hermes POSTs the approved text to kun, and kun delivers with its own tokens. Hermes never holds a Page token.
  • Drafting stays out of the relaysSOCIAL_DRAFT_SOURCE=hermes (default) hands the ask to Hermes; =anthropic drafts server-side via the API and is opt-in because the engine's billing posture is subscription-only.
  • Media is a set, and it rides the whole chain — a draft carries mediaUrls (≤10 public URLs, typically /higgs or /carousel renders on the CDN) from the ask through approval to delivery, which routes by shape: text · one photo (/photos edge, sendPhoto) · 2–10 as a Facebook carousel or a Telegram album (sendMediaGroup) · one video (/videos, sendVideo). Over-long copy spills into a follow-up message rather than being truncated. Mixed image+video, two videos, or more than ten images are refused by name before anything is delivered, because no platform edge carries them. The confirm page behind a signed link lists every URL.
  • Multi-channel, with per-channel truth — the picker selects any subset of a brand's wired channels, the three transports fan out in parallel, and the result names each channel individually. A half-landed post no longer reads as one flat failure.
  • Stage for review from the Hub — the review editor can push the draft down the cron's approval path (signed 12h link into the private review channel) instead of deciding in-app, for when someone else signs off. Staging retires the queue entry, so a draft cannot be approved through both lanes.
  • Scheduling and the drain — the review queue's approve-mode setting (publish right away, or schedule) decides what Approve does; scheduled variants land as scheduled and /api/social/drain publishes them from GitHub Actions every ~15 minutes, claiming each row before delivering so two overlapping runs cannot double-post. Its channel filter is an allow-list, so a transport added later waits visibly in the queue rather than being attempted and silently lost.
  • Metrics/api/social/metrics reads Facebook reach and engagement back into SocialMetric six-hourly. A missing scope or a retired metric name is terminal on the first attempt with a named remedy, rather than retried forever.
  • Ledger/social renders the last 20 variants server-side (brand, channel, copy, status, time, latest reach/views), so pending approvals, scheduled posts, and publishes are visible without SQL.
  • The Social Agent window (2026-07-30) — the Hub's Draft tab: describe the post and Claude drafts it, Arabic first with its English sibling; one press moves the Hub to Publish with the copy in the editor. It is the same ai block as the sales agentatom/agent-heading, atom/prompt-input, atom/ai-response-display, ported from hogwarts — so the two windows read as one product. The prompt names the selected brand, and says what a good brief contains, because the brand and the brief are the only things the answering session receives. Asynchronous by design. The window records a SocialDraftRequest and polls; a Claude Code session on the Max pool answers it through draft's queue mode. The API lane of .claude/memory/decisions/2026-07-30-in-app-draft-spend.md shipped but is blocked — no funded key exists behind ANTHROPIC_API_KEY — so the queue is the live path, and the reasoning panel describes that queue rather than performing invented thinking. This is not the old gateway-LLM draft card coming back: that card was removed (2026-07-23) because a relay was writing copy; here the writer is Claude itself.
  • The composer is an editor (2026-07-30) — the Publish tab wears the agent's shape: the same band, heading and rounded surface, sized for writing. Media URL and schedule collapsed from stacked labelled inputs into two popover pills, so the copy is the only thing on the page with weight.
  • Publish became a review queue (2026-08-05) — the blank-slate composer is gone. Answered full drafts (copy AND/OR media) queue on Publish oldest-first: the next one up is highlighted, every upcoming one is browsable, and loading one opens the editor prefilled with its copy and its attachments. The only actions are decisions — Approve (now or scheduled, per a settings popover), Dismiss, or send the signed review link — and an empty queue points back at Draft rather than offering a textarea. Approval claims the request (answered → consumed) with the same conditional update the drain uses, so two reviewers race safely; a total delivery failure hands the draft back with the reason, while a partial one stays consumed because re-approving would double-post the channels that landed.
  • Media is wired to the draft (2026-08-05) — the showroom's Attach fills a provider-owned tray shared by the draft ask, the review editor, and the showroom itself; the drain's answering session picks assets from library.json by the brief's (library: id) hint or brand + type and passes them with answer --media; /higgs and /carousel end by attaching their render to the ask it answers. Copy-and-paste-a-URL is no longer the hand-off.
  • The calendar is real (2026-08-05) — /social/calendar renders pillars.json with this ISO week's rotation picks highlighted (a TS mirror of the seeder's math, parity-tested), a queue chip per brief, and Queue now to file one immediately. Calendar → draft is a click.
  • The queue drains itself (2026-07-30) — scripts/drain-drafts.sh --install arms a launchd tick every 5 minutes: heartbeat on every look, Claude only when asks are pending. The window reads the heartbeat back (position, last check), stops polling after 10 minutes, and the drain sweep expires hour-old unanswered asks — a stall now reads as a stall, not an outage.
  • Still pending — adapters beyond Slack (see Hermes), per-brand Telegram channels, a PostHog destination to consume the UTMs, and per-channel drafting in the cron. Tracked in docs/SOCIAL-AUTOMATION.md.

See also

  • Status — what is configured and live right now, versus what is merely wired.
  • Ownership & Accounts — who actually holds the Meta assets, and what Instagram needs.
  • Channel Setup — per-platform setup guides, written to be usable on your own accounts.
  • Carousel — multi-slide bilingual decks rendered at exact platform sizes.
  • Brand & Voice — the rules every post obeys.
  • Connectors — the MCP-backed service integrations.
  • /higgs skill — media generation · calendar · draft · approve · publish · measure · carousel · growth agent.