AI engineering

Automate blog and social media posting with Claude, GitHub Actions and Make

An architecture for publishing one researched article a day and turning it into a narrated vertical video for YouTube Shorts, Instagram, Facebook and LinkedIn, with the platform limits that shape it.

9 min read
On this page

A daily content pipeline can be built from four parts: a scheduled GitHub Actions workflow, Claude Code running inside it to research and write, a set of small scripts that render a narrated vertical video and check every draft, and publishing connectors for each platform (the YouTube Data API directly, and Make.com for LinkedIn, Facebook and Instagram). EpiFive itself runs on this design. This guide explains each part and the platform rules that decide how it has to be built.

Who this is for and what you end up with

This is for people who publish technical content and want it to reach the platforms where their readers are, without spending an hour a day reformatting the same article five times. At the end you have:

  • A workflow that writes and publishes one long-form article per day after a quality check.
  • A second workflow that picks the newest article not yet shared, writes a post for each platform, renders a 30 to 70 second vertical video with a voice-over, validates everything, and publishes.
  • A log in the repository that guarantees no article is posted twice.
  • Tracking parameters on every link, so analytics show which platform sends readers.

Architecture overview

GitHub Actions (cron, daily)
  |
  |-- pick.mjs ............ newest live article not in social-log.md
  |-- Claude Code ......... writes posts, points, narration, YouTube title and tags
  |-- video.mjs ........... headless Chrome renders slides, Kokoro speaks, ffmpeg encodes
  |-- validate.mjs ........ lengths, banned phrases, hashtags, required files
  |
  |-- youtube.mjs ......... YouTube Data API: upload as a Short
  |-- make-send.mjs ....... Make.com webhook -> LinkedIn Page, Facebook Page, Instagram
  |
  '-- log.mjs ............. append to social-log.md, commit, push

The key design decision is that the language model only writes. Picking the article, rendering, validating, publishing and logging are ordinary scripts. This keeps the expensive and unpredictable step small, makes failures easy to locate, and means the step that holds publishing credentials never runs model-generated commands.

Running Claude Code on a schedule in GitHub Actions

The Claude Code GitHub Action (anthropics/claude-code-action@v1) runs in automation mode whenever the workflow passes a prompt input, including on a schedule trigger. It can authenticate with a Claude subscription: the CLAUDE_CODE_OAUTH_TOKEN secret is generated locally with claude setup-token and is available on Pro, Max, Team and Enterprise plans.

on:
  schedule:
    - cron: '18 6 * * *'
  workflow_dispatch:
 
jobs:
  share:
    runs-on: ubuntu-24.04
    steps:
      - uses: actions/checkout@v4
      - uses: anthropics/claude-code-action@v1
        with:
          claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
          claude_args: |
            --max-turns 60
            --allowedTools "Read,Write,Edit,Bash(node scripts/social/video.mjs:*)"
          prompt: |
            Read automation/SOCIAL.md and write the drafts for this article.
            Do not publish, commit or push.

Two details matter. In automation mode Claude has no shell access until --allowedTools grants it, so list only the commands the writing step needs. And keep publishing credentials out of this step entirely: pass them as environment variables only to the later steps that run your own scripts.

Writing posts that platform search can find

Each platform has its own search, and all of them weight the first line heavily. The writing instructions therefore require a primary keyword, the phrase people actually type, usually the core of the article's SEO title, to appear in the first line of every post, plus two to four secondary keywords used once each. Error codes, cmdlet names and full product names (Microsoft Entra ID rather than an internal abbreviation) are what people search for.

Per platform:

  • YouTube Shorts: title starting with the keyword, a description whose first sentence states the answer, a link to the article, and a Tags: line that the upload script sends as the video's tags.
  • Instagram: captions are searchable, so the keyword goes in the first line and once more in the body, followed by three to six specific hashtags. Links in captions are not clickable, so the caption points to the link in bio.
  • LinkedIn and Facebook: a hook under 150 characters so it survives the "see more" cut-off, concrete takeaways, and the article link.

Every post also carries one plain sentence that answers the core question and names the source guide. Search snippets and AI assistants quote sentences like this, which is the practical side of generative engine optimization.

Rendering a narrated vertical video

The video is built without a video editor:

  1. Slides: an HTML template per slide (title, one slide per key point, closing slide) is screenshotted at 1080 by 1920 with headless Chrome through the DevTools protocol. Three visual themes are chosen per article so consecutive videos do not look interchangeable.
  2. Voice-over: Claude writes a narration line per slide that explains the point rather than reading it. Kokoro-82M, run through kokoro-onnx on the CPU, turns each line into a WAV file. Each slide stays on screen until its line has been spoken.
  3. Encoding: ffmpeg chains the slides with xfade, places each voice clip at its slide's start with adelay, mixes them with amix, normalises loudness with loudnorm, and draws a progress bar with an overlay whose position is re-evaluated every frame.
ffmpeg -loop 1 -t 6.2 -i slide-0.png -loop 1 -t 8.9 -i slide-1.png \
  -f lavfi -i anullsrc=channel_layout=stereo:sample_rate=48000 -i voice-0.wav -i voice-1.wav \
  -filter_complex "[0:v][1:v]xfade=transition=fade:duration=0.5:offset=5.7[v];
    [3:a]adelay=250|250[a0];[4:a]adelay=6200|6200[a1];
    [2:a][a0][a1]amix=inputs=3:duration=first:normalize=0,loudnorm[a]" \
  -map "[v]" -map "[a]" -c:v libx264 -pix_fmt yuv420p -r 30 -c:a aac -ar 48000 \
  -t 14.6 -movflags +faststart reel.mp4

The output matches what the platforms ask for. Instagram Reels must be MP4 or MOV with H.264 or HEVC video, AAC audio at up to 48 kHz, 23 to 60 frames per second, and 3 seconds to 15 minutes long. Facebook recommends 1080 by 1920 Reels of 3 to 90 seconds.

Choosing a voice you are allowed to use

Check the licence of both the model and its training data. Kokoro-82M is published under Apache-2.0, and its model card states it was trained exclusively on permissive and non-copyrighted audio. By contrast, the Piper voice en_US-lessac is trained on the Blizzard 2013 Lessac data, whose licence allows research use only and excludes commercial use, and en_US-ryan lists CC BY-NC-SA 4.0. For a monetized site or channel, that difference decides the choice.

Publishing to each platform

YouTube Shorts with the Data API

videos.insert uploads a vertical video under three minutes as a Short. Each call costs one unit of the separate Video Uploads quota, which allows 100 calls per day, and the request needs the youtube.upload scope. The upload script uses a resumable upload and sends the title, description, tags, category and selfDeclaredMadeForKids: false.

One trap: if the OAuth consent screen is External and left in Testing, Google issues refresh tokens that expire after 7 days, and the daily upload silently stops working a week later. Publish the app to production. For an app only you use, verification is not required, and you click through an "unverified app" warning once when you authorize it.

LinkedIn, Facebook and Instagram through Make

Posting directly through the platform APIs is harder than it looks:

  • LinkedIn Pages need the Community Management API. LinkedIn's review says it is available only to registered legal organizations for commercial use cases, and that personal email addresses won't pass vetting.
  • Facebook and Instagram need a Meta developer app with page and Instagram permissions. On a new device, Meta may refuse to let the account register as a developer until the device has been used for a while.

Make.com already holds approval with these platforms. Its LinkedIn app has a "Create a Company Video Post" module, its Facebook Pages app has "Create a Post" and "Publish a Reel", and its Instagram for Business app has "Create a reel post". The pipeline sends one JSON payload to a Make custom webhook protected with an API key header, and a router posts to all three.

{
  "slug": "example-article",
  "article_url": "https://www.example.com/blog/example-article?utm_source=facebook&utm_medium=social",
  "video_url": "https://www.example.com/social/example-article.mp4",
  "linkedin_text": "...",
  "facebook_text": "...",
  "fb_reel_text": "...",
  "instagram_caption": "..."
}

Two requirements caught out the first test. Make's Instagram connection works only with an Instagram Business account ("Creator accounts are not supported"), linked to a Facebook Page. And Make's photo and reel modules need media on a publicly accessible URL, so the workflow commits the day's video to the site's public folder, waits until it is served, and only then calls the webhook.

Validation and the posting log

Before anything is published, a validation script checks every draft: length ranges per platform, the article link where it is required and absent where it is not clickable, hashtag counts, the narration line count matching the slides, and a list of banned phrases (sales offers, engagement bait, first-person experience claims, generic hashtags such as #viral). If validation fails, nothing is posted and the article is retried the next day.

After publishing, the result for each platform is appended to a log in the repository and pushed. The picker skips any slug in the log, so a re-run or a partial failure never produces duplicate posts.

Monetization: what automation can and cannot do

  • The site: ad revenue on the articles is the realistic income source, and the social posts exist to send readers there. UTM parameters on every link show which platform does that best.
  • YouTube: the Partner Program requires 1,000 subscribers plus either 4,000 public watch hours in 12 months or 10 million Shorts views in 90 days, followed by a review. Its inauthentic content policy, renamed in July 2025, excludes mass-produced and repetitive content, including image slideshows or scrolling text with minimal narrative and AI content made with generic templates. Explanatory narration and varied visuals help, but a channel that wants monetization also needs content that is clearly original.

Checklist

  1. Scheduled workflow with the Claude Code GitHub Action and a subscription or API token.
  2. Writing instructions with per-platform rules and a primary keyword in every first line.
  3. Video renderer: HTML slides, a commercially licensed TTS voice, ffmpeg encoding to platform specs.
  4. Validation script that blocks publishing on any problem.
  5. YouTube OAuth app published to production, credentials stored as encrypted secrets.
  6. Make scenario with a key-protected webhook, an Instagram Business account and publicly hosted media.
  7. A posting log committed to the repository, and UTM parameters on every link.

If you would rather have this pipeline set up on your own site and accounts, see the automation setup service.

References

Questions people ask

Can a LinkedIn Company Page be posted to through the LinkedIn API?

Posting as a Page needs the Community Management API, which LinkedIn vets. Its review page says the API is only available to registered legal organizations for commercial use cases and that personal email addresses won't pass. Integration platforms such as Make, which already hold LinkedIn approval, offer a Company video post module instead.

Why does a YouTube upload script stop working after a week?

If the Google Cloud OAuth consent screen is External and still in Testing, Google issues refresh tokens that expire in 7 days. Publishing the app to production avoids that expiry for an app you only use yourself.

Can automated, templated Shorts be monetized on YouTube?

YouTube's inauthentic content policy excludes mass-produced and repetitive content, including image slideshows or scrolling text with minimal narrative and AI content made with generic templates. Automated Shorts can still drive discovery and traffic, but monetization needs content that is clearly educational and varied.

Which text-to-speech voice can be used in commercial videos?

Check the licence of both the model and its training data. Kokoro-82M is Apache-2.0 and its model card says it was trained on permissive and non-copyrighted audio. Several popular Piper voices are trained on research-only or non-commercial datasets.

Claude CodeGitHub ActionsMake.comYouTube Data APIText to SpeechSocial Media Automation
  1. Production LLMOps & Enterprise RAG: Low-Latency, Privacy-Preserving Architecture at Scale

    A blueprint for building accurate, privacy-preserving Retrieval-Augmented Generation (RAG) systems, covering hybrid dense-sparse search, cross-encoder re-ranking, semantic caching and latency tuning.

    AI engineering6 min read
  2. Google Workspace to Microsoft 365 Migration: A Complete Technical Guide for Mail, Calendar, Contacts and Drive

    A step-by-step guide to moving from Google Workspace to Microsoft 365 with Exchange Online's native Gmail migration and Migration Manager, from routing subdomains and service accounts to MX cutover.

    Microsoft 36515 min read
  3. Microsoft 365 Tenant-to-Tenant Migration Architecture: Cross-Tenant Mailbox, OneDrive, Teams and Domain Move

    A practical guide to Microsoft 365 tenant-to-tenant migration for mergers and divestitures: native cross-tenant mailbox and OneDrive moves, SharePoint, Teams and the domain cutover.

    Microsoft 36515 min read