Here's the goal. You type one idea. You get back a short video of your AI avatar saying it in your own voice. (Your AI avatar is a talking video of you, made by AI.) It comes with captions, and it gets scheduled to YouTube, Facebook, Instagram, TikTok and Threads.
You don't need to know code. The easy way: paste one prompt into Claude Code, which is Claude working right on your computer. Claude asks you questions one at a time, tells you exactly what to click, and builds it with you. It's the simple version of the setup I post with. Same four tools, same order.
The easy way: let Claude build it with you
This is the way I'd do it. You paste one prompt into Claude Code, and Claude turns into your setup helper. It asks you questions one at a time, tells you exactly where to click, and builds the whole thing with you. Your job is answering questions and clicking a few buttons. Claude does the typing.
What you need
- A computer. Mac, Windows or Linux. On Windows, you'll turn on WSL first (Windows' built-in Linux). It's one command and a restart. The steps are below.
- A paid Claude plan. Claude Pro ($20 a month) or higher. The free plan doesn't include Claude Code.
- About an afternoon. Most of it is waiting on accounts.
- A clear photo of you, with your eyes and mouth easy to see, and a short voice recording made in a quiet room. These become your AI avatar. Only you. Never anyone else.
- Two more accounts. Claude walks you through both. HeyGen Creator ($29 a month) makes your avatar and copies your voice. Upload-Post Basic ($24 a month) schedules your posts. HyperFrames, the video editor, is free.
- Your social accounts. Instagram has to be a Business or Creator account, and Facebook posts go to a Page.
That's $73 a month at list prices, checked on 2026-10-04. Prices change, so check each page before you buy.
Open Claude Code
Three quick steps. You'll paste a couple of lines into the terminal (the window where you type commands). Never opened one? Anthropic's terminal guide for new users walks you through it.
Open the terminal
- Mac: press Command + Space, type
Terminal, and press Return. - Windows: press Windows + X, pick Terminal (Admin), type
wsl --installand press Enter. Restart when it's done. Then open Ubuntu from the Start menu and pick a username and password. Use Ubuntu from here on (Microsoft's steps). - Linux: press Ctrl + Alt + T, or open Terminal from your apps.
- Mac: press Command + Space, type
Install Claude Code
Paste this line and press Enter. (Mac: Command + V. Ubuntu or Linux: Ctrl + Shift + V.) When it's done, close the window and open a new one.
curl -fsSL https://claude.ai/install.sh | bashMake your folder and start Claude
This makes an empty folder, steps into it, and starts Claude. The first time, your browser opens so you can log in. If it asks whether you trust this folder, say yes.
mkdir -p ~/content-engine cd ~/content-engine claude
Paste in the setup prompt
Click Copy, paste it into Claude Code, and press Enter. Claude Code may show it as one short line that says Pasted text. That's normal. It's long on purpose: it tells Claude how to talk to you, what it must never do, and the order to build things in. You don't have to read all of it. The safety rules are worth a look, though.
Hi Claude. Please be my patient setup helper for the next few hours.
I want to build a simple content engine in this folder. I type one idea. It makes a short vertical video of my AI avatar (a talking video of me, in my own voice) saying it, adds captions, and schedules it to my social accounts. I don't know how to code, so treat me like a smart beginner.
THE GUIDE
We're building the starter engine from this guide:
https://receiptsgroup.com/guides/ai-content-engine
Its "manual way" sections are the source of truth for every file, command and menu path. When you need a file, download the page with curl (ask me first) and copy that file exactly from its code box. Each box is labeled with its file name, like engine/check.py. Don't rewrite the scripts. Only fill in my details where the guide shows <angle brackets>. If you can't reach the page, tell me before you build anything from memory. If a website or command looks different from the guide, tell me, and check the official page the guide links to.
HOW TO TALK TO ME
- Plain words and short sentences. If you need a tech word, explain it in five words or less.
- Ask ONE question at a time. Then stop and wait for my answer.
- Before each step, tell me in one sentence what it does and why.
- Before each command, tell me in plain words what it does, so I know what I'm saying yes to.
- Tell me exactly where to click and what I should see. If something looks different, ask me what's on my screen.
- If I'm stuck, slow down and try another way. Never make me feel dumb.
SAFETY RULES (never break these)
1. Never ask me to paste a password, API key or secret into this chat. If I paste one by accident, tell me to delete that key and make a new one.
2. Keys go only in a file called .env in this folder. You make it with the names and blank values. I type the values in myself. Never show what's inside .env. To check that a value is filled in, use a command that only prints yes or no.
3. Ask me first before anything that costs money or credits, and before anything that posts in public. Tell me what it costs or does. Do the free dry run first, every time.
4. AI disclosure stays on. Every video and every caption says it was made with an AI avatar.
5. My avatar is only me: my own face and my own voice, with my OK. Never anyone else's.
6. Stay in this folder. Ask before you install anything, and tell me what it is in plain words.
7. No made-up numbers, results, promises or quotes in my scripts or captions.
THE PLAN
Go one step at a time. After each step, if there's something to test, run a free test that spends nothing and tell me in one line if it passed. Keep a short checklist in SETUP-NOTES.md of what's done (no keys in it). If we stop partway, read it first next time and pick up where we left off.
Step 1. Get to know me.
Say hi. In two or three short sentences, tell me what we'll do and that it takes about an afternoon. Then ask me these, one at a time:
a) What do you do, and who do you talk to?
b) What job title and business name should your videos use?
c) How do you talk? (Offer to learn my voice from 3 to 5 sentences I paste in: a text, an email, a post, or how I'd explain my work out loud.)
d) Anything you never want said? Words you hate, topics to skip?
e) Which apps do you want to post to: YouTube, Facebook, Instagram, TikTok, Threads?
f) Which of these do you already have: a paid Claude plan, HeyGen, Upload-Post?
Check for yourself which computer I'm on (Mac, Windows or Linux). Only ask if you can't tell.
Then sum up my answers in a few lines and ask if you got it right.
Step 2. The shopping list.
Give me a short checklist of only what I still need, with the link and the plan to pick:
- Claude Pro or higher, $20/mo: https://claude.com/pricing (I'm probably on it already, since I'm talking to you.)
- HeyGen Creator, $29/mo, for my avatar and my voice: https://app.heygen.com (plans: https://www.heygen.com/pricing)
- Upload-Post Basic, $24/mo, to schedule my posts (the free plan can't post to TikTok): https://upload-post.com (plans: https://docs.upload-post.com/resources/pricing-and-limits)
- HyperFrames, the video editor: free. Nothing to sign up for.
Say these are list prices checked on 2026-10-04, and I should check each page. Remind me that Instagram must be a Business or Creator account, and that Facebook posts go to a Page. Then wait while I sign up. Ask me to type "done" for each one.
Step 3. Get my computer ready. (Guide: "Setup, step by step", steps 1, 2 and 4.)
On Windows, this whole build runs inside WSL (Windows' built-in Linux), because HeyGen's tool needs it. If I'm not in WSL, help me get there first.
Check what I already have: Node.js 22 or newer, FFmpeg, Python 3. Install only what's missing, one at a time, after you ask. Make the subfolders from the guide (rules, prompts, engine, runs). Add the HyperFrames plugin to Claude Code. Test: the version checks and npx hyperframes doctor.
Step 4. My avatar and my voice. (Guide: Setup step 5.)
First, ask me to confirm that the photo and the voice will be mine, and that I'm OK with HeyGen using them. Then walk me through HeyGen's site, one click at a time: Avatars, New Avatar, Upload Photo (a clear photo of me, eyes and mouth easy to see). Then Voice, + New Voice, Create New Voice, Instant Voice Cloning (I record in a quiet room). HeyGen takes a while to check the avatar, so move on to step 5 while it does.
Step 5. Build the engine. (Guide: Setup steps 3 and 8.)
Make the files from the guide: CLAUDE.md, rules/VOICE.md, rules/GUARDRAILS.md, rules/banned.txt, prompts/01-script.md, prompts/02-edit.md, prompts/03-captions.md, engine/check.py, engine/avatar.py and engine/post.py. Write VOICE.md and GUARDRAILS.md from my answers, in my words, and swap the guide's example lines for mine. Show me VOICE.md and ask if it sounds like me. Test: the guide's Test 1 (do you follow my rules?).
Step 6. Keys and IDs, one at a time. (Guide: Setup steps 6 and 7, and "Environment variables".)
Make .env with these names and blank values, plus .env.example and .gitignore from the guide:
UPLOAD_POST_API_KEY=
UPLOAD_POST_PROFILE=
FACEBOOK_PAGE_ID=
HEYGEN_AVATAR_ID=
HEYGEN_VOICE_ID=
Open .env for me in a simple text editor. Then, for each value: tell me where to click to get it and which line it goes on (right after the = sign, no spaces, no quotes). Wait while I paste it and save. Check it with your yes/no command, then test it.
- HeyGen: install the HeyGen CLI (ask first) and log in with heygen auth login --oauth. It opens my browser. My avatar and voice IDs aren't secret, so find them with heygen avatar looks list and heygen voice list, show me which ones are mine, and I'll paste them in.
- Upload-Post: in Manage Users, I make a profile (that name goes on UPLOAD_POST_PROFILE) and connect each app I picked. Then API Keys, Generate New API Key, and I paste it into .env. Test it with the guide's "me" call. It shows my email and plan, never the key.
- FACEBOOK_PAGE_ID: only if I post to Facebook and manage more than one Page. Use the guide's Facebook Pages call and show me the list.
Step 7. Free tests. (Guide: "First test".)
Run the guide's Test 2: a test script, the guardrail check, the avatar dry run, the HyperFrames check and the Upload-Post check. None of these spend anything. Explain each result in one plain line. Fix anything that fails before we spend a cent.
Step 8. My first video. (Guide: "One video, start to finish".)
Ask me for one idea, in my own words. Write the script. I read it and say OK. Ask before the avatar render (it uses HeyGen credits). Build the edit, then have me watch the whole video with sound. Write the captions and run the check. Show me the post dry run. Ask me before you schedule anything. The AI label stays on.
Step 9. Wrap up.
Give me one short paragraph on how to make a video every day from now on: what I type, and the two things I always check myself (the script and the finished video). Then a short cheat sheet of the commands, in order.
Ready? Say hi and ask me your first question.What to expect
- It starts with a chat. Claude says hi and asks you questions, one at a time: what you do, who you talk to, how you talk, where you post. Answer like you're texting a friend. Typos are fine. If it asks something you don't get, say so. It won't get annoyed. That's kind of the whole point.
- Then a shopping list. You get a short list of the accounts you still need, with links and which plan to pick. Sign up, then type “done.”
- Then the build. Claude makes your folder one piece at a time. After each piece, it runs a free test and tells you in one line if it worked.
- Then your keys. A key is a password for apps. Claude makes a private file called
.env, opens it for you, and tells you where to get each key and which line to paste it on. Keys go in that file. Never in the chat. - Claude Code asks before it does things. Before it runs a command or changes a file, it asks you. That's normal. Read the one-line reason, then say yes. If something looks off, say no and ask why.
- Nothing costs money or goes public without your yes. Making the avatar video uses HeyGen credits, and posting is public, so Claude asks first. Every time.
How long: plan on an afternoon. Most of it is waiting: HeyGen checking your avatar, each app confirming its connection, and the first video render.
What you'll have at the end: a folder on your computer that turns one idea into a finished video. Your first video, made and (if you said yes) scheduled, with the AI label on. And a short note from Claude on how to make the next one.
Need a break? Just close the window. When you're back, open the terminal and paste these two lines. Claude picks up where you left off. It keeps a checklist in SETUP-NOTES.md, too.
cd ~/content-engine
claude --continueWant to see every step yourself? The manual way is below.
What you're building
This is the manual way: every step, every file and every command. Use it to do the whole thing by hand, or keep it open to check what Claude is doing. It's also what the setup prompt tells Claude to follow.
Here's the whole chain. Each box is one tool doing one job, and each one leaves a file behind. So when something comes out weird (it will), you can open the files in order and see exactly which step did it.
- 1Your thoughtYou, typing into a text file→ thought.txt
- 2The scriptClaude Code, reading your rule files→ script.json
- 3The guardrail checkA short Python script→ PASS, or a list of what to fix
- 4The avatarHeyGen: your photo avatar + your cloned voice, Avatar IV→ avatar.mp4
- 5The editHyperFrames, driven by Claude Code→ final.mp4 with captions and graphics
- 6The captionsClaude Code again, one caption per platform→ captions.json
- 7The scheduleUpload-Post, AI label on→ queued on YouTube, Facebook, Instagram, TikTok, Threads
Words you'll see
- Terminal: the text window where you type commands. Terminal on a Mac; on Windows, your WSL (Linux) terminal.
- Claude Code: Anthropic's AI assistant that works inside a folder on your computer. It reads files, writes files, and runs the commands you allow.
- Headless: running Claude Code as
claude -p "...". One instruction in, one result out, no chat window. That's what lets a script call it. - Rule files: plain text files that say how you talk and what you never say. Claude reads them before every script.
- CLI: a command-line tool. HeyGen's is called
heygen; HyperFrames runs asnpx hyperframes. - API key: a long password that lets a script use your account. Treat it like your bank PIN.
- Environment variable: a named setting a script reads, like
UPLOAD_POST_API_KEY. Ours live in a file called.envthat never gets shared. - JSON: plain text with curly braces that holds structured data. The script and captions are saved this way so the next step can read them.
- Render: turning a project into an actual MP4 file.
Before you start
Four tools plus some plumbing. Here's what each one does in this build, which plan actually unlocks the part we use, and what it costs. Prices are list prices on 2026-10-04, US dollars.
| Tool | What it does here | Plan you need | List price (2026-10-04) |
|---|---|---|---|
| Claude Code | Writes the script and captions; drives the edit | Claude Pro or higher (Max, Team, Enterprise), or an Anthropic Console account. The free Claude plan doesn't include Claude Code. | Pro $20/mo, or $17/mo billed yearly ($200). Max from $100/mo. |
| HeyGen | Your avatar and your cloned voice, rendered as video | Creator or higher. Creator lists voice cloning, 1080p export and unlimited photo avatars. The free plan lists 3 videos a month, up to 1 minute each. | Creator $29/mo with 600 credits. Pro $49/mo with 1,000 credits. |
| HyperFrames | Turns the avatar clip into the edited video (HTML in, MP4 out) | None. It's open source (Apache 2.0), and rendering on your own machine doesn't use HeyGen credits. | $0 |
| Upload-Post | Schedules the video to all five platforms | Basic or higher if you want TikTok. Free covers 10 uploads a month on 2 profiles, without TikTok. | Basic $24/mo, or $16/mo billed yearly ($192). Free $0. |
| Node.js, FFmpeg, Python | The plumbing HyperFrames and the glue scripts run on | Node.js 22 or newer (24 is the current LTS), FFmpeg, Python 3 | $0 |
Your computer
- macOS 13 or newer, Ubuntu 20.04+ or Debian 10+, or Windows 10 (1809+) through WSL. Windows has to be WSL for this build, because HeyGen's CLI runs on macOS and Linux, and on Windows only through WSL.
- At least 4 GB of RAM (Claude Code's minimum). Rendering likes more; HyperFrames switches itself to a low-memory mode at 8 GB or less.
- Disk space for video. Every run folder keeps its clips.
Time
Plan an afternoon for setup. Most of it is waiting: HeyGen validating your avatar, each social network confirming its connection, the first render. After that, a video is mostly machine time. Your part is reading the script and watching the cut, which, by the way, are the two parts you should never automate.
What you'll have at the end
- A folder (your engine) with your rule files, three prompts and three small scripts.
- One finished vertical video of your avatar, captioned and edited, in
runs/<name>/final.mp4. - That video scheduled to YouTube, Facebook, Instagram, TikTok and Threads, labeled as AI-generated wherever the platform supports it.
- A repeatable set of commands for the next one.
Setup, step by step
Do these in order. Every command goes in the terminal unless I say otherwise, and anything in <angle brackets> is yours to fill in.
Install Node.js, FFmpeg and Python
HyperFrames needs Node.js 22 or newer plus FFmpeg; the glue scripts need Python 3. On nodejs.org, take the LTS download (version 24 on the day I checked). FFmpeg's download page lists builds for macOS and Windows and the packages for Linux. Check first, though: you may already have some of these.
node --version # want v22 or higher ffmpeg -version # prints a version banner python3 --version # the scripts were tested on 3.9; newer is finePermissionsAn installer may ask for your computer's password to install system-wide. None of these three touch your online accounts.What you'll seeTerminal showing the three version numbers. Install Claude Code and log in
WhereTerminal · docs: code.claude.com/docs/en/setupYou need Claude Pro, Max, Team or Enterprise, or an Anthropic Console account (the free plan doesn't include Claude Code). Install it, open a new terminal window, check the version, then start it once inside your new engine folder to log in.
curl -fsSL https://claude.ai/install.sh | bash # open a NEW terminal window, then: claude --version claude doctor # read-only health checkmkdir -p ~/content-engine/rules ~/content-engine/prompts ~/content-engine/engine ~/content-engine/runs cd ~/content-engine claude # follow the browser login, then type /exitPermissionsLogging in opens your browser so you can sign in to your Claude account and approve Claude Code. In the headless commands later, every call names a permission mode and the exact tools that step may use, so a script can't wander off and run whatever it likes.What you'll seeThe terminal after claude --version, then the browser sign-in page. Write your rule files
WhereYour engine folder (~/content-engine), in any text editorThis is the whole repo. Small on purpose. Claude Code reads
CLAUDE.mdat the start of every session, headless ones included, andCLAUDE.mdcan pull in other files with@pathlines. So your rules ride along on every call without you pasting them.Save each block below as the file named on its label. Fill in the <angle brackets>. The before→after examples in
VOICE.mdmatter more than anything else here: replace mine with three to five of your own.content-engine/ ├── CLAUDE.md loads your rules into every Claude Code run ├── rules/ │ ├── VOICE.md how you talk │ ├── GUARDRAILS.md what never goes out │ └── banned.txt phrases check.py fails on ├── prompts/ │ ├── 01-script.md thought -> script.json │ ├── 02-edit.md avatar.mp4 -> final.mp4 (HyperFrames) │ └── 03-captions.md script -> captions.json, one per platform ├── engine/ │ ├── check.py the guardrail check │ ├── avatar.py HeyGen render (Avatar IV) │ └── post.py Upload-Post scheduling, dry run by default ├── .env your keys and ids (never shared) ├── .env.example the names, no values ├── .gitignore keeps .env and runs/ out of git └── runs/ └── 2026-10-06-retest/ one folder per video ├── thought.txt ├── script.json ├── avatar.mp4 ├── edit/ the HyperFrames project ├── final.mp4 ├── captions.json └── sent.json Upload-Post's replies (job ids)# My content engine This folder turns one thought into a short vertical video and a scheduled post. Every script, caption and word on screen follows these files: @rules/VOICE.md @rules/GUARDRAILS.md Work only inside this folder. Never open, print or copy .env.# How I talk Who's talking: <your job title> at <your company>, explaining one idea to one person, the way I would on a call. Casual, plain words, short sentences, a little dry humor where it fits. Not a training video. Not a stand-up set. - Open with the point or a question. Never "In today's video". - One idea per video. Two ideas means two videos. - Explain any technical word the first time it comes up, in plain words. - End on the lesson, or a question to the viewer. ## Before -> how I'd actually say it Replace these with 3-5 of your own. They teach Claude more than any adjective. - "Leverage AI to streamline your operations." -> "I let the AI do the boring first draft. I still read it." - "Consistency is key to social media success." -> "Posting twice and vanishing for a month mostly trains people not to wait for you."# Never publish - I'm the <exact title> of <company>. Never call me anything else. - I appear only as my own avatar, in my own voice. Nobody else's face, voice or name. - No numbers stated as facts unless they're in my thought. No "studies show". - No income, revenue or results promises. No guarantees. No testimonials. - No client names, customer details, or anything from a real account or screen. - Every video says it was made with an AI avatar (on screen and in the caption). - Scripts run 60-110 words. Banned phrases live in rules/banned.txt, and engine/check.py fails anything that uses one.# One phrase per line. engine/check.py fails any script or caption that uses one. guaranteed game-changer in today's video in today's fast-paced world studies show link in bio# Copy to .env and fill in. Never commit, paste or screen-share .env. UPLOAD_POST_API_KEY= UPLOAD_POST_PROFILE= FACEBOOK_PAGE_ID= HEYGEN_AVATAR_ID= HEYGEN_VOICE_ID=.env runs/PermissionsNone. These are plain text files on your computer.What you'll seeThe folder open in your editor: CLAUDE.md, rules/, prompts/, engine/, runs/. Add the HyperFrames plugin to Claude Code
WhereTerminal · docs: hyperframes.heygen.com/guides/pluginsHyperFrames is HeyGen's open-source “write HTML, render video” framework. The plugin teaches Claude Code how to build a valid HyperFrames project; the CLI renders it on your machine. The first
npxrun downloads the CLI, so give it a minute.claude plugin marketplace add heygen-com/hyperframes claude plugin install hyperframes@hyperframes claude plugin details hyperframes # should list hyperframes, general-video and media-use npx hyperframes doctor # checks Node.js, FFmpeg and ChromePermissionsA plugin adds skills (instructions) to Claude Code from HeyGen's GitHub marketplace. Third-party marketplaces don't auto-update by default: turn it on in Claude Code under /plugin → Marketplaces → hyperframes → Enable auto-update, or runclaude plugin update hyperframes@hyperframesnow and then.What you'll seeclaude plugin details hyperframes listing its skills, and npx hyperframes doctor with its checks passing. Make your avatar and clone your voice in HeyGen
Whereapp.heygen.com · Avatars → New Avatar → Upload Photo · Voice → + New Voice → Create New Voice → Instant Voice CloningSign up and pick a plan (Creator is where voice cloning and 1080p show up). Then:
- Avatar: open the Avatars tab, choose New Avatar, then Upload Photo. Use a photo of you with your eyes, mouth and lips clearly visible and your face filling a good part of the frame. Name it and submit. It has to be validated before you can use it.
- Voice: open the Voice section, click + New Voice, then Create New Voice, and pick Instant Voice Cloning. Record or upload a clean sample of you talking in a quiet room. HeyGen's tip: keep a mic 6 to 8 inches from your mouth, and not rubbing on your shirt.
Want a different outfit or setting later? Generate a new look from your own photo (HeyGen lists one credit per look). New looks of you: fine. Anybody else's face: no.
PermissionsYou're giving HeyGen your face and voice to model. HeyGen's docs say photo avatars get no consent check from them, which is exactly why the rule here is “only you.” (Digital twins, trained from video, do require the person to record a consent statement.)What you'll seeHeyGen's Avatars tab with New Avatar → Upload Photo open (use your own photo, or blur it). Install the HeyGen CLI and find your two ids
WhereTerminal · docs: developers.heygen.com/cliThe CLI is how
avatar.pytalks to HeyGen. It can log in two ways, and they bill differently:- OAuth (
heygen auth login --oauth): a browser sign-in; renders use your subscription credits. HeyGen's CLI readme describes this route for “Pro / Max subscription users”; it works on my Creator plan. - API key (from app.heygen.com/developers/api): renders draw on a separate pay-as-you-go API balance in US dollars, which needs no plan and is kept apart from your plan credits.
Pick one; the CLI keeps one login at a time. Then copy your look id and your voice id into
.envasHEYGEN_AVATAR_IDandHEYGEN_VOICE_ID. Ifavatar looks listasks for more,heygen avatar looks list --helpshows the options.curl -fsSL https://static.heygen.ai/cli/install.sh | bash heygen auth login --oauth # or: heygen auth login --api-key heygen auth status # which login is active heygen avatar looks list # JSON: find your photo avatar by name, copy its look id heygen voice list # JSON: find your cloned voice, copy its id heygen video create --request-schema # every field a render request acceptsPermissionsOAuth opens a HeyGen sign-in in your browser and asks you to approve the CLI on your account; the login is saved in ~/.heygen/credentials. A HEYGEN_API_KEY set in your shell always wins over the saved login. Nothing renders until you ask.What you'll seeheygen auth status showing the active login (blur your email). - OAuth (
Set up Upload-Post and connect your accounts
Whereapp.upload-post.com/manage-users (Manage Users) · app.upload-post.com/api-keys (API Keys → Generate New API Key)- Sign up at upload-post.com. Pick Basic if you want TikTok; the free plan can't post there.
- Open Manage Users and create a profile. The name you type is what the API calls
user; put it in.envasUPLOAD_POST_PROFILE. - Click each network (YouTube, Facebook, Instagram, TikTok, Threads) and finish its sign-in.
- Open API Keys, click Generate New API Key, and paste it into
.envasUPLOAD_POST_API_KEY. You won't need to look at it again, and you shouldn't.
Two gotchas before you hit them. Instagram has to be a Business or Creator account; personal accounts can't post through the API. And Facebook posts go to a Page: if your login manages more than one, find the right Page id with the second call below and put it in
.envasFACEBOOK_PAGE_ID.set -a; source .env; set +a # load .env into this terminal window curl -s -H "Authorization: Apikey $UPLOAD_POST_API_KEY" https://api.upload-post.com/api/uploadposts/me curl -s -H "Authorization: Apikey $UPLOAD_POST_API_KEY" https://api.upload-post.com/api/uploadposts/facebook/pagesThe first call should answer with your account email and plan. That's how you know the key works.PermissionsEach network shows its own permission screen; Upload-Post's quickstart says to grant the permissions it needs for uploading. Connect only the accounts you'll post from. Upload-Post can post as them until you disconnect them.What you'll seeManage Users with your profile and the five networks connected (blur the handles if you like). Add the three prompts and the three scripts
Whereprompts/ and engine/ in your engine folderThe prompts are plain-English instructions Claude follows. The scripts are the glue: one checks the guardrails, one calls HeyGen, one calls Upload-Post. Save each block as the file named on its label. Read every line; there's nothing clever in them, which is the point.
Write a short-video script from my thought. You're given a run folder (runs/<name>). 1. Read <run>/thought.txt. That's my idea, in my words. 2. Write the script I'll say to camera as my avatar, following CLAUDE.md and the rules it imports: - 60-110 words (about 25-45 seconds), one idea, plain words. - The first sentence is the hook: the point, or a question. - End on the lesson, or a question to the viewer. - Keep my phrasing wherever it works. No numbers, names or results I didn't give you. 3. Write <run>/script.json with exactly these keys: {"title": "the hook as a title, 60 characters max", "script": "the full script as plain text", "word_count": 0, "notes": ["anything you changed or dropped from my thought, and why"]} Don't create or edit any other file. End with one line: DONE or FAILED <reason>.Use the /hyperframes:hyperframes skill. Finish the video for the run folder you're given (runs/<name>). What's there: - <run>/edit/ is a HyperFrames project made by `npx hyperframes init` from <run>/avatar.mp4 (my avatar talking, 1080x1920). The clip is placed; nothing is designed yet. - <run>/edit/transcript.json has the speech timings from Whisper, if transcription worked. - <run>/script.json has the title and the exact words. Build: 1. Keep the avatar clip and its audio exactly as they are: no trims, no speed changes. 2. Readable captions in the lower third, a few words at a time, timed to the audio. Use script.json for the exact words. Captions never cover my mouth or eyes. 3. A title card over the first 2 seconds with the title from script.json. 4. At most two simple graphics, only where they show what I'm saying at that moment (a short checklist, a before/after, one key phrase). My face stays on screen at least half the time. 5. A small "AI avatar" label in a top corner, visible the whole time. Every word on screen follows CLAUDE.md. Then run `npx hyperframes check <run>/edit` and fix every error it reports. Render with `npx hyperframes render <run>/edit --output <run>/final.mp4`. Don't touch files outside <run>. End with one line: DONE <path> or FAILED <reason>.Write the post captions for the finished video. You're given a run folder (runs/<name>). Read <run>/script.json and follow CLAUDE.md. Write <run>/captions.json with exactly this shape: {"youtube": {"title": "the hook, under 70 characters", "description": "2-3 short lines, then: Made with my AI avatar.", "tags": ["3-6 plain tags"]}, "facebook": {"title": "the hook", "description": "1-3 short lines, then: (Made with my AI avatar.)"}, "instagram": {"caption": "hook line, one or two short lines, 3-5 hashtags at the end"}, "tiktok": {"caption": "hook line, one short line, 3-5 hashtags"}, "threads": {"caption": "one or two lines ending in a question, under 400 characters, no hashtags"}} Write each one for its platform, not one caption pasted five times. No links in the Instagram or TikTok captions (they aren't clickable there). No "follow for more" bait. Nothing that isn't in the script. Then run `python3 engine/check.py <run>` and fix captions.json until it prints PASS. End with one line: DONE or FAILED <reason>.#!/usr/bin/env python3 """Guardrail check for one run. Prints PASS, or FAIL and every reason (exit code 1). python3 engine/check.py runs/<name> Checks script.json (length, banned phrases) and, once it exists, captions.json (banned phrases, caption lengths, links where they can't be clicked). Edit the numbers and rules/banned.txt to fit you. """ import json import re import sys from pathlib import Path MIN_WORDS, MAX_WORDS = 60, 110 # about 25-45 seconds of speech LIMITS = {"instagram": 2200, "tiktok": 2200, "threads": 500} # Upload-Post's character limits page NO_LINKS = ("instagram", "tiktok") # links in these captions aren't clickable URL = re.compile(r"https?://|www\.", re.I) def size(platform, text): if platform == "threads": return len(text.encode("utf-8")) # Threads counts UTF-8 bytes if platform == "tiktok": return len(text.encode("utf-16-le")) // 2 # TikTok counts UTF-16 units return len(text) def main(run): run = Path(run) banned = [ln.strip().lower() for ln in Path("rules/banned.txt").read_text().splitlines() if ln.strip() and not ln.startswith("#")] fails = [] s = json.loads((run / "script.json").read_text()) words = len(s["script"].split()) if not MIN_WORDS <= words <= MAX_WORDS: fails.append(f"script is {words} words (want {MIN_WORDS}-{MAX_WORDS})") texts = {"title": s["title"], "script": s["script"]} cap_file = run / "captions.json" if cap_file.exists(): caps = json.loads(cap_file.read_text()) for plat, fields in caps.items(): for key, val in fields.items(): texts[f"{plat}.{key}"] = val if isinstance(val, str) else " ".join(val) for plat, limit in LIMITS.items(): text = caps.get(plat, {}).get("caption", "") if size(plat, text) > limit: fails.append(f"{plat} caption is over {limit}") for plat in NO_LINKS: if URL.search(caps.get(plat, {}).get("caption", "")): fails.append(f"{plat} caption has a link (not clickable there)") for where, text in texts.items(): for phrase in banned: if re.search(r"(?<!\w)" + re.escape(phrase) + r"(?!\w)", text.lower()): fails.append(f'{where}: banned phrase "{phrase}"') if fails: print("FAIL") for f in fails: print(" -", f) sys.exit(1) print(f"PASS ({words} words" + (", captions checked)" if cap_file.exists() else ")")) if __name__ == "__main__": if len(sys.argv) != 2: sys.exit(__doc__) main(sys.argv[1])#!/usr/bin/env python3 """Script -> your avatar speaking it (HeyGen, Avatar IV, 9:16, 1080p), through the HeyGen CLI. python3 engine/avatar.py runs/<name> --dry-run # print the request, spend nothing python3 engine/avatar.py runs/<name> # render (spends HeyGen credits) -> runs/<name>/avatar.mp4 Reads HEYGEN_AVATAR_ID and HEYGEN_VOICE_ID from the environment or .env. The CLI handles login. """ import json import os import subprocess import sys from pathlib import Path def env(name): if os.environ.get(name): return os.environ[name] if Path(".env").exists(): for line in Path(".env").read_text().splitlines(): key, _, val = line.partition("=") if key.strip() == name and val.strip(): return val.strip().strip('"') sys.exit(f"{name} is not set: add it to .env") def main(run, dry): run = Path(run) s = json.loads((run / "script.json").read_text()) req = {"type": "avatar", "avatar_id": env("HEYGEN_AVATAR_ID"), "voice_id": env("HEYGEN_VOICE_ID"), "script": s["script"], "aspect_ratio": "9:16", "resolution": "1080p", "engine": {"type": "avatar_iv"}, "title": s["title"][:60]} print(json.dumps(req, indent=2)) if dry: print("(dry run: nothing rendered, no credits spent)") return r = subprocess.run(["heygen", "video", "create", "-d", json.dumps(req), "--wait"], capture_output=True, text=True) if r.returncode: sys.exit(f"heygen video create failed (exit {r.returncode}): {(r.stderr or r.stdout)[:800]}") data = json.loads(r.stdout).get("data") or {} vid = data.get("video_id") or data.get("id") if not vid or data.get("status") == "failed": sys.exit(f"render failed: {r.stdout[:800]}") out = run / "avatar.mp4" r = subprocess.run(["heygen", "video", "download", vid, "--output-path", str(out)], capture_output=True, text=True) if r.returncode or not out.exists(): sys.exit(f"rendered {vid} but the download failed: {(r.stderr or r.stdout)[:500]}") print(f"saved {out} (HeyGen video {vid})") if __name__ == "__main__": if len(sys.argv) < 2: sys.exit(__doc__) main(sys.argv[1], "--dry-run" in sys.argv)#!/usr/bin/env python3 """Schedule a finished video on YouTube, Facebook, Instagram, TikTok and Threads through Upload-Post. python3 engine/post.py runs/<name> --at 2026-10-06T12:15 # dry run: print every job python3 engine/post.py runs/<name> --at 2026-10-06T12:15 --send # really schedule it (add --tz Europe/London if you're not on US Eastern; leave out --at to post right away) Needs runs/<name>/final.mp4 and captions.json, and a PASS from engine/check.py. Reads UPLOAD_POST_API_KEY, UPLOAD_POST_PROFILE and (optional) FACEBOOK_PAGE_ID from the environment or .env. AI disclosure is on for every job. The key is never printed. """ import argparse import json import os import subprocess import sys from pathlib import Path ENDPOINT = "https://api.upload-post.com/api/upload" def env(name, required=True): if os.environ.get(name): return os.environ[name] if Path(".env").exists(): for line in Path(".env").read_text().splitlines(): key, _, val = line.partition("=") if key.strip() == name and val.strip(): return val.strip().strip('"') if required: sys.exit(f"{name} is not set: add it to .env") return None def jobs(c, at, tz): """One upload per platform group, each with its own caption as the job's title.""" common = [("user", env("UPLOAD_POST_PROFILE")), ("is_ai_generated", "true"), ("async_upload", "true")] if at: common += [("scheduled_date", at), ("timezone", tz)] yt, fb = c["youtube"], c["facebook"] ytfb = [("platform[]", "youtube"), ("platform[]", "facebook"), ("title", yt["title"]), ("youtube_title", yt["title"]), ("youtube_description", yt["description"]), ("selfDeclaredMadeForKids", "false"), ("facebook_title", fb["title"]), ("facebook_description", fb["description"]), ("facebook_is_ai_generated", "true")] ytfb += [("tags[]", t) for t in yt.get("tags", [])] page = env("FACEBOOK_PAGE_ID", required=False) if page: ytfb.append(("facebook_page_id", page)) ig = c["instagram"]["caption"] tt = c["tiktok"]["caption"] th = c["threads"]["caption"] return [("youtube+facebook", common + ytfb), ("instagram", common + [("platform[]", "instagram"), ("title", ig), ("instagram_title", ig), ("media_type", "REELS")]), ("tiktok", common + [("platform[]", "tiktok"), ("title", tt), ("tiktok_title", tt)]), ("threads", common + [("platform[]", "threads"), ("title", th), ("threads_title", th)])] def main(): ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) ap.add_argument("run") ap.add_argument("--at", help="local date and time, e.g. 2026-10-06T12:15") ap.add_argument("--tz", default="America/New_York") ap.add_argument("--send", action="store_true", help="really upload (default: dry run)") a = ap.parse_args() run = Path(a.run) if subprocess.run([sys.executable, "engine/check.py", str(run)]).returncode: sys.exit("fix the check failures first") video = run / "final.mp4" if not video.exists(): sys.exit(f"no {video} yet") at = a.at + ":00" if a.at and len(a.at) == 16 else a.at js = jobs(json.loads((run / "captions.json").read_text()), at, a.tz) for label, fields in js: print(f"\n[{label}]") for k, v in fields: print(f" {k} = {v if len(v) < 100 else v[:97] + '...'}") if not a.send: print("\n(dry run: nothing sent; add --send to schedule)") return key = env("UPLOAD_POST_API_KEY") sent = {} for label, fields in js: cmd = ["curl", "-sS", "-X", "POST", ENDPOINT, "-H", f"Authorization: Apikey {key}", "-F", f"video=@{video}", "-w", "\n%{http_code}"] for k, v in fields: cmd += ["--form-string", f"{k}={v}"] # literal text: a caption starting with @ is not a file r = subprocess.run(cmd, capture_output=True, text=True) body, _, code = r.stdout.rpartition("\n") print(f"[{label}] HTTP {code}: {body[:300]}") sent[label] = {"http": code, "response": body[:2000]} (run / "sent.json").write_text(json.dumps(sent, indent=1)) print(f"\nsaved the responses (job and request ids) in {run / 'sent.json'}") if __name__ == "__main__": main()PermissionsNone yet. Money only moves when you run avatar.py without --dry-run, or post.py with --send.What you'll seeThe finished folder in your editor, all files in place.
Environment variables (names only)
These live in .env in your engine folder. .gitignore keeps it out of git. Never paste it into a chat, a doc or a screen recording.
UPLOAD_POST_API_KEYLets post.py schedule through your Upload-Post account.
Where: Upload-Post → API Keys → Generate New API KeyUPLOAD_POST_PROFILEThe profile name you created (the API's user). Not a secret, but keeping it here keeps the scripts generic.
FACEBOOK_PAGE_IDOptional. Which Facebook Page gets the post, if your login manages more than one.
Where: The Facebook Pages call in setup step 7HEYGEN_AVATAR_IDYour photo avatar's look id.
Where: heygen avatar looks listHEYGEN_VOICE_IDYour cloned voice's id.
Where: heygen voice listHEYGEN_API_KEYOnly if you use the API-key route instead of OAuth. It bills the separate API balance. Set it in your shell or save it with heygen auth login --api-key, not in .env (avatar.py doesn't pass .env to the CLI).
ANTHROPIC_API_KEYOnly if you run Claude Code on Console API billing instead of a Pro/Max login. Export it in your shell; Claude Code asks once to approve it.
Where: The Claude Console (platform.claude.com)First test: prove it before you spend a credit
First, make sure Claude actually reads your rules. Run this from inside your engine folder:
claude -p "Read CLAUDE.md and the rule files it imports. In three short bullets, tell me the rules you'll follow most closely when you write my scripts, then list every banned phrase from rules/banned.txt." --permission-mode dontAsk --allowedTools "Read"A good answer quotes your rules back: your title, your 60-110 word range, your actual banned phrases. When this guide was tested with the starter files above, it also pointed out the placeholders still unfilled and two phrases the rules forbid that weren't on the banned list yet. That's the kind of answer you want.
A bad answer is generic advice about “engaging content.” That means your files didn't load: check that CLAUDE.md is in the folder you ran the command from, and that its @rules/... lines match the file names exactly.
mkdir -p runs/test
echo "I used to answer every new lead myself. Now an assistant drafts the first reply and I call back the ones that need me." > runs/test/thought.txt
claude -p "Follow prompts/01-script.md. Run folder: runs/test" --permission-mode acceptEdits --allowedTools "Read,Write"
cat runs/test/script.json
python3 engine/check.py runs/test
python3 engine/avatar.py runs/test --dry-run
npx hyperframes doctor
set -a; source .env; set +a
curl -s -H "Authorization: Apikey $UPLOAD_POST_API_KEY" https://api.upload-post.com/api/uploadposts/meWhat good looks like: script.json has a title, a script that sounds like you, and a notes list explaining what Claude changed; check.py prints PASS (… words); the dry run prints the HeyGen request with your two ids, "engine": {"type": "avatar_iv"}, 9:16 and 1080p, and says no credits were spent; doctor finds Node.js, FFmpeg and Chrome; Upload-Post answers with your email and plan. The script step took about a minute when this guide was tested.
If check.py prints FAIL, that's the guardrail doing its job. Read the reasons, fix the thought (or the rules, if they're wrong for you), and run the script step again.
One video, start to finish
The real thing, in the order I'd run it, with the thought from the episode. Swap in yours. Only two steps spend money (the avatar render and the post), and both wait for you first.
Write the thought
One thought, in your words, no polish. Numbers only if you can back them up. Here's the one from the episode:
mkdir -p runs/2026-10-06-retest cat > runs/2026-10-06-retest/thought.txt <<'EOF' For a while my AI avatar just wasn't worth it. A cloned voice over my screen recordings was cheaper and honestly looked better, so I said no. Then the avatars got good enough, and I switched. This isn't really about avatars. Pick the cheapest thing that clears the bar today, and put a date on the calendar to re-test whatever you said no to. EOFTurn it into a script
acceptEditslets Claude write files without stopping to ask, and the only tools it gets are Read and Write, so apart from basic file commands it can't run anything in this step.claude -p "Follow prompts/01-script.md. Run folder: runs/2026-10-06-retest" --permission-mode acceptEdits --allowedTools "Read,Write"Read it, then run the guardrail check
Read it out loud. If it doesn't sound like you, edit
script.jsonby hand, or rerun step 2 with a note on the end (“shorter, less formal”). This is approval gate one, and it's yours.cat runs/2026-10-06-retest/script.json python3 engine/check.py runs/2026-10-06-retestRender your avatar (this one spends HeyGen credits)
The dry run shows exactly what goes to HeyGen. The real run waits for the render and downloads
avatar.mp4. Avatar IV is named in every request instead of left to HeyGen's default, so a default changing on their side can't quietly swap the engine on you.python3 engine/avatar.py runs/2026-10-06-retest --dry-run python3 engine/avatar.py runs/2026-10-06-retestStart the edit project
This makes a 1080×1920 HyperFrames project with your clip already placed, and transcribes the audio with Whisper into
transcript.json, all on your own machine. Nothing is designed yet; that's the next step. The first run can take a while because it sets up Whisper (about 12 minutes on the Mac this guide was tested on). Ahyperframes checkon this bare project can fail on purpose (there's no animation yet), so don't panic.npx hyperframes init runs/2026-10-06-retest/edit --video runs/2026-10-06-retest/avatar.mp4 --resolution portrait --non-interactiveLet Claude design and render the edit
Claude uses the HyperFrames skills to add captions, a title card and a graphic or two, runs
npx hyperframes checkuntil it's clean, then rendersfinal.mp4. This is the slow step. The allowed tools let it edit files and run HyperFrames, ffprobe and ffmpeg, and nothing else that changes your machine.claude -p "Using /hyperframes:hyperframes, follow prompts/02-edit.md. Run folder: runs/2026-10-06-retest" --permission-mode acceptEdits --allowedTools "Read,Write,Edit,Skill,Bash(npx hyperframes *),Bash(ffprobe *),Bash(ffmpeg *)"Watch it. All of it.
With sound, start to finish, on your phone if you can. Do the captions match the words? Is anything covering your mouth? Is the AI label there? If not, fix it in plain English (there's a prompt for that under Real use) and render again. Approval gate two.
open runs/2026-10-06-retest/final.mp4 # macOS; on Linux: xdg-openWrite the captions for each platform
Claude writes one caption per platform, then runs the guardrail check itself and fixes the captions until it passes. Run the check once more yourself; trust, but verify.
claude -p "Follow prompts/03-captions.md. Run folder: runs/2026-10-06-retest" --permission-mode acceptEdits --allowedTools "Read,Write,Edit,Bash(python3 engine/check.py *)" python3 engine/check.py runs/2026-10-06-retestSchedule it, and label it
The dry run prints every job it would send. Read it, then add
--send. The time is read in the--tzyou give it (US Eastern by default), and it has to be in the future and no more than 365 days out.Why four jobs instead of one? In my own setup on 2026-09-27, a scheduled job carrying a separate caption for Instagram and TikTok showed only the shared title on those two. So the script sends Instagram, TikTok and Threads as their own jobs, each with its caption as the title. YouTube and Facebook go together because their own fields worked. Every job carries
is_ai_generated=true, and the Facebook job addsfacebook_is_ai_generated(more on why below).python3 engine/post.py runs/2026-10-06-retest --at 2026-10-06T12:15 python3 engine/post.py runs/2026-10-06-retest --at 2026-10-06T12:15 --sendset -a; source .env; set +a curl -s -H "Authorization: Apikey $UPLOAD_POST_API_KEY" "https://api.upload-post.com/api/uploadposts/schedule?profile_username=$UPLOAD_POST_PROFILE" curl -s -X DELETE -H "Authorization: Apikey $UPLOAD_POST_API_KEY" https://api.upload-post.com/api/uploadposts/schedule/<job_id>
| Platform | What Upload-Post sets | Worth knowing |
|---|---|---|
| TikTok | is_aigc | Shows “Creator labeled as AI-generated.” Direct posts only, not drafts. |
is_ai_generated | Meta decides how the “AI info” label shows. It can't be added or removed after publishing. | |
| YouTube | containsSyntheticMedia | The altered or synthetic content disclosure. |
facebook_is_ai_generated (Reels) | The shared flag doesn't reach Facebook, so post.py sends this one too. Meta can also auto-label media that carries C2PA or IPTC metadata. | |
| Threads | No API flag in Upload-Post's labeling guide | Say it in the post. The on-screen label and the captions prompt cover it. |
Real use: prompts for real jobs
Start claude inside your engine folder and paste one of these. Swap <name> for the run folder. Claude will ask before running commands, which is fine: you're right there.
Read notes/this-week.md. Pull out five thoughts I could make a 30-second video about. For each one: a single sentence in my words, the one idea, and why someone would stop scrolling for it. No numbers I didn't write. Don't write scripts yet.Rewrite runs/<name>/script.json to 70-85 words. Keep my first sentence and my last sentence exactly as they are. Cut the middle, not the point. Update word_count and add a note saying what you cut. Then run python3 engine/check.py runs/<name> and fix anything it flags.Using /hyperframes:hyperframes: in runs/<name>/edit the captions sit too low and cover my chin. Move every caption up so it clears my face, change nothing else, run npx hyperframes check runs/<name>/edit, then render again to runs/<name>/final.mp4.Use ffmpeg to save a frame from runs/<name>/final.mp4 at 0.5 seconds and then every 3 seconds into runs/<name>/frames/. Look at each frame and list anything wrong: text too small to read on a phone, a caption touching my face, a graphic that doesn't match what I'm saying at that moment, a frame that's mostly empty, a missing AI avatar label. One line each: time, problem, fix. Don't change any files.Rewrite the threads and instagram captions in runs/<name>/captions.json so each ends with a question someone could answer in one line, using my words from the script. Follow CLAUDE.md, no links. Then run python3 engine/check.py runs/<name> and fix anything it flags.Number 4 is a baby version of my critic. It's surprisingly good at spotting the caption sitting on your chin before your audience does.
What it costs to run
| Item | Plan | Per month (list, 2026-10-04) |
|---|---|---|
| Claude Code | Claude Pro | $20 |
| HeyGen | Creator (600 credits) | $29 |
| Upload-Post | Basic (5 profiles, TikTok included) | $24 |
| HyperFrames | Open source, renders locally | $0 |
| Total | Monthly billing | $73 |
Billed yearly, Claude Pro is $17 a month ($200 a year) and Upload-Post Basic $16 a month ($192 a year). HeyGen bills yearly too; its page has the number.
What HeyGen's credits buy: HeyGen lists an Avatar IV photo look at 16 credits per minute of video. So 600 credits is about 37 minutes of avatar video a month, if every credit goes to that. A short is under a minute, but the same credits also pay for new looks (one credit each) and anything else you make in HeyGen. Unused plan credits roll over for one extra billing cycle.
Claude usage: every headless run counts toward your plan's limits (a rolling five-hour window plus a weekly cap on paid plans). One video's worth of runs is small, but a big batch can hit the cap; Claude's pricing page covers waiting, upgrading or paying for extra usage.
What my version adds (and this guide doesn't)
Honest part. What you just built is one clean slice. Mine is the same slice with a pile of guardrails and a feedback loop on top, and those took weeks of getting it wrong first. Here's what's in it, so you know what you're not getting from a diagram:
- A Studio page. A local web page where I pick a post type, type the thought, and watch each step run: Analyze, Avatar, Design, Captions, Publish. It stops for me after the script if I asked to read it, and always before Publish.
- A real precheck. Not just a banned-words list: a rules pack with my title rules, pace and length, and a list of facts, where a number only goes on screen if it's marked verified with a source.
- A scene designer and a critic. One agent plans the scenes from a library of tested layouts. A separate critic measures every cut (empty space, small text, tiny heads, captions running into faces, repeats) and fails it until there are zero fails. Nothing gets scheduled before it passes.
- Recipes. Every post gets a recipe (hook, opening, look, caption style, transitions, length) under no-repeat rules, so the feed doesn't turn into the same video forty times.
- Analytics back into the plan. Every three hours it pulls each post's numbers per platform, compares posts at the same age, and next week's plan tests one thing at a time.
- Comment GUIDE. Upload-Post's AutoDM watches an Instagram post's comments for the keyword and DMs this page. Its docs cap it at 2 new monitors per profile a day and 15 days per monitor, with 10 DMs a day on the free plan and 500 on paid.
Start with the slice. Add a critic the first time a video makes you wince.
When it breaks
claude: command not found right after installing
claude doctor shows install health.A claude -p run says it isn't allowed to do something
--allowedTools (for example Bash(npx hyperframes *); the space before the * matters), or do that step yourself.The script is generic and sounds nothing like you
@rules/ paths don't match). If it can, VOICE.md needs more before→after examples of how you actually talk.check.py says FAIL
The HeyGen CLI exits with code 3
heygen auth status, then log in again with heygen auth login --oauth. If HEYGEN_API_KEY is set in your shell, it overrides the saved login: unset it or check it's the right key.The HeyGen CLI exits with code 4
--wait window ran out (20 minutes by default, per the CLI readme). The render may still finish. Check it with heygen video get <video-id> and download it with heygen video download <video-id> --output-path runs/<name>/avatar.mp4.The photo avatar won't validate, or looks off
npx hyperframes init can't transcribe the clip
npx hyperframes doctor; it reports whisper-cpp. Or run init again with --skip-transcribe and let the edit prompt build the captions from script.json.hyperframes check reports errors
claude session in your engine folder and ask it to fix exactly those.The render is slow or runs out of memory
--workers 1, or --quality draft while you're iterating.Upload-Post answers 401
/api/uploadposts/me call from setup step 7.Upload-Post answers 429
TikTok answers 403
Instagram won't connect
Facebook wants to know which Page
“Your session has expired” or “Token expired”
Instagram says “Action suspected as spam”
Limits worth knowing
- Claude: a rolling five-hour usage window plus weekly caps on paid plans.
- HeyGen: your plan's monthly credits (Avatar IV photo look: 16 per minute of video); unused credits roll over one extra billing cycle.
- HyperFrames: no render fees locally. It needs Node.js 22+, FFmpeg and Chrome (
doctorchecks all three). - Upload-Post: the daily caps per connected account above; it also flags duplicate or near-duplicate posts to the same account within 48 hours. Scheduling goes up to 365 days ahead.
- Caption length (Upload-Post's limits page): Instagram and TikTok 2,200 characters, Threads 500 (counted in bytes, and an emoji is 4). check.py enforces these three.
What each tool can see
- Claude Code runs on your machine and works in the folder you start it in: your thought, rules, scripts and captions. To get answers it sends your prompts and the model's replies to Anthropic, and it keeps session transcripts on your computer under
~/.claude/projects/(30 days by default). In these commands it can only use the tools you list. - HeyGen has your photo, your voice sample, every script you render and the videos.
- HyperFrames renders on your machine. The CLI checks its skills against GitHub and has a
telemetrycommand for its anonymous usage data. - Upload-Post holds the connections to your social accounts and can post as them. It sees every video and caption you send, plus analytics and comments when you ask for them.
How to disconnect
- Upload-Post: Manage Users → disconnect each network. Manage or replace the key on the API Keys page, and delete it from .env.
- HeyGen CLI: delete
~/.heygen/credentials(that's where the login lives). If you used an API key, remove it from your shell too, and manage it from the API dashboard where you made it (app.heygen.com/developers/api). - Claude Code: type
/logoutin a session.claude plugin uninstall hyperframes@hyperframesremoves the plugin. To remove Claude Code itself (native install):rm -f ~/.local/bin/claudeandrm -rf ~/.local/share/claude.
That's the slice
One thought in, one labeled, scheduled video out, and you approve the script and the cut before anything goes anywhere. Build it, break it, then add the pieces from my version one at a time.
Checked on Oct 4, 2026 against
Every claim about a third-party tool in this guide (plans, prices, menu paths, commands, limits) was checked against these official pages on Oct 4, 2026. These screens change often: if something looks different, trust the page over this guide.
- Claude Code: Advanced setupInstall command, system requirements, which plans include Claude Code,
claude --versionandclaude doctor, uninstall. - Claude Code: Terminal guide for new usersOpening a terminal on macOS (Cmd + Space, Terminal), Linux and Windows; pasting the install line; starting
claudeand logging in through the browser. - Microsoft: Install WSL
wsl --installfrom PowerShell run as administrator, then restart; installs Ubuntu by default; open Ubuntu from the Start menu and create a Linux username and password. - Claude Code: Run Claude Code programmatically
claude -p,--allowedToolsrule syntax, permission modesacceptEditsanddontAsk, slash skills in-p. - Claude Code: Memory (CLAUDE.md)CLAUDE.md location, loaded every session,
@pathimports. - Claude Code: Tools referenceTool names (Read, Write, Edit, Bash, Skill) and which need permission.
- Claude Code: Commands
/logout,/plugin. - Claude Code: Data usagePrompts and outputs sent to Anthropic; local transcripts under ~/.claude/projects/ for 30 days by default.
- Claude: Plans and pricingFree excludes Claude Code; Pro $20/mo or $17/mo yearly ($200); Max from $100/mo; five-hour windows and weekly caps.
- HeyGen: PricingFree (3 videos/mo, up to 1 min, Avatar IV access), Creator $29/mo (600 credits, 1080p, voice cloning, unlimited photo avatars), Pro $49/mo (1,000 credits).
- HeyGen Help: How to use creditsAvatar IV photo look 16 credits/min, video look 31; one credit per generated look; rollover one extra cycle.
- HeyGen Help: API pricing explainedPay-as-you-go API credits in USD, no plan needed, separate from web plans.
- HeyGen Developers: CLIInstall command,
heygen auth login(API key, OAuth), video create/download,--request-schema. - HeyGen CLI on GitHubOAuth uses subscription credits, API key uses API credits; macOS, Linux, Windows via WSL; exit codes;
--waitdefault 20 min; ~/.heygen/credentials. - HeyGen Developers: API keyWhere to generate the key (app.heygen.com/developers/api),
HEYGEN_API_KEY. - HeyGen Developers: Create video (v3)
POST /v3/videos: avatar_id (look id), voice_id, script, aspect_ratio 9:16, resolution 1080p, engine avatar_iv. - HeyGen Developers: Avatar IVAvatar IV request shape and options.
- HeyGen Developers: Avatar consentDigital twins need a recorded consent statement; photo avatars get no consent check, so the subject's agreement is your responsibility.
- HeyGen Help: Photo avatarsAvatars → New Avatar → Upload Photo; photo requirements; validation.
- HeyGen Help: VoicesVoice → + New Voice → Create New Voice → Instant Voice Cloning; recording tips.
- HyperFrames on GitHubNode.js 22+ and FFmpeg; Apache 2.0; Claude Code plugin commands.
- HyperFrames: QuickstartFree and open source; local rendering doesn't use HeyGen credits.
- HyperFrames: Install the pluginClaude Code marketplace install,
plugin details, auto-update, uninstall. - HyperFrames: CLI reference
init --video --resolution portrait --non-interactivewith Whisper captions,check,render [DIR] --output,doctor, workers (~256 MB each), low-memory mode at 8 GB. - HyperFrames: Add an avatar presenterKeep the presenter clip as project media and build the scenes around it.
- Upload-Post: Pricing and limitsFree/Basic prices, uploads, profiles, TikTok paid-only, daily caps per account, scheduling up to 365 days, AutoDM limits.
- Upload-Post: QuickstartManage Users → create a profile → connect networks; API Keys → Generate New API Key;
Authorization: Apikeyheader. - Upload-Post: Upload video
platform[],title,scheduled_date,timezone,async_upload, per-platform titles,facebook_page_id,media_type. - Upload-Post: Labeling AI-generated content
is_ai_generatedmapping per platform,facebook_is_ai_generated, labeling is the publisher's responsibility. - Upload-Post: Manage scheduled postsList and cancel scheduled jobs.
- Upload-Post: Current user and Facebook Pages
/api/uploadposts/mereturns the account email and plan;/api/uploadposts/facebook/pages(docs.upload-post.com/api/get-facebook-pages) lists Page ids. - Upload-Post: Common errorsReconnect from Manage Users; Instagram spam restriction and the 24-hour pause.
- Upload-Post: Character limitsInstagram and TikTok 2,200; Threads 500 bytes.
- Upload-Post: FAQInstagram needs a Business or Creator account.
- Upload-Post: AutoDM monitorsInstagram comment keyword → DM; 2 new monitors per profile a day, 15-day expiry, 10/500 DMs a day.
- Node.js: Releasesv24 is the current LTS; v22 is also LTS.
- FFmpeg: DownloadBuilds for macOS and Windows, packages for Linux.
- Python: DownloadsCurrent Python releases.
