You’ve just finished recording. The video’s done — the hard part is over. Then you remember everything that comes next.
Upload the file somewhere public. Wait for the transcript. Write a LinkedIn post, an X post, maybe an Instagram caption. Log into each platform. Paste the content. Schedule it. Repeat.
That’s five separate jobs, every single time, following exactly the same pattern. It’s the kind of work that looks like progress but isn’t.
In this post I’ll show you the pipeline I built with Claude Code and the Blotato API that handles all of it — upload, transcription, writing, visuals, scheduling — automatically, from a single agent run.
Why Post-Recording Work Is the Hardest Part to Fix
The recording itself has a clear end point. The editing has a clear end point. But the distribution work? It bleeds. Every platform wants something slightly different. LinkedIn wants a thought-leadership angle. X wants a punchy two-liner. Instagram wants a hook and a CTA. TikTok wants something different again.
So you either write everything from scratch four times (slow), use the same post everywhere (lazy), or skip platforms entirely (wasteful).
The real problem isn’t that it’s hard — it’s that it’s predictable. Every video goes through the same steps. Same sequence, same platforms, same types of content. That’s exactly what agents are built for.
The research agent I built to plan my content — The Research Agent That Plans My YouTube Content — runs the same way. One command, structured output, no manual work. The publishing pipeline follows the same logic.
What Blotato Does (and Why It’s the Missing Piece)
Blotato is a social media publishing API. You call it programmatically — no UI, no dashboards, no logging into anything. Claude Code talks to it directly.
Four specific capabilities make it the right tool for this:
1. Transcribe — feed it an audio URL and it extracts the full transcript. No Whisper setup, no manual copy-paste, no third-party subscription.
2. Generate visuals — turn transcript content into carousels, quote cards, or captioned clip compilations using pre-built templates.
3. Schedule and publish — push text and media to Twitter/X, LinkedIn, Instagram, TikTok, YouTube, Facebook, Threads, Bluesky, and Pinterest. Immediate, at a scheduled ISO timestamp, or queued to the next free slot in your calendar.
4. Upload video to YouTube — publish an MP4 directly via the API, with title, description, and privacy status set programmatically.
Claude Code sits above all four of these. It decides what to do, calls each endpoint in sequence, and passes the output of one step into the next.
The 5-Step Pipeline
Here’s the full sequence, step by step.
Step 1: Upload to Cloudinary
Blotato needs a publicly accessible URL — it can’t reach a file sitting on your Mac. The first job is getting the video onto the internet.
I use a Python uploader script (Agents/shorts-uploader.py) that pushes the file to Cloudinary and returns two URLs:
video_url— the direct MP4 linkaudio_url— an MP3 version Cloudinary generates automatically via a URL transform
That audio URL is what feeds Blotato’s transcriber. One command, two outputs.
Step 2: Transcribe With Blotato
With the audio URL in hand, one API call extracts the full transcript:
blotato_create_source(sourceType: "audio", url: audio_url)
Blotato processes it asynchronously — you poll the status until it returns ready, then pull the transcript text. No Whisper, no manual setup, no cost beyond the API call.
This is the same kind of source creation that works with YouTube URLs, TikTok links, articles, and PDFs. The thumbnail agent I built — I Replaced Canva With a Claude Code Thumbnail Agent — reads an outline file to understand the video content. The Blotato approach is cleaner for longer-form videos: it reads the actual content.
Step 3: Write the Posts
The transcript goes to Claude. A skill file (skills/social-writer.md) tells Claude exactly what to write for each platform:
- X — under 240 characters, punchy hook, no hashtags
- LinkedIn — insight-led opener, no “I” as the first word, three body paragraphs, link and hashtags at the bottom
- Instagram — hook, storytelling body, strong CTA
Claude adapts the content for each platform from the same transcript. No rewriting. No copy-pasting the transcript into ChatGPT four times. The outputs land in outputs/[slug]/ as separate files, ready to use.
Step 4: Generate a Visual (Optional)
If you want more than text posts, Blotato has a visual pipeline. blotato_list_visual_templates shows what’s available — carousels, quote cards, captioned clip compilations, AI-narrated video. You pick a template ID, pass the content, and Blotato’s AI generates the visual. Typically 30 seconds to a couple of minutes.
The image URLs come back in the response, ready to pass directly into the post scheduler.
Step 5: Schedule Everything
The final step: blotato_create_post per platform. Pass the account ID, the text, optional media URLs, and a schedule time.
Three scheduling modes:
- Immediate — publishes now
- Scheduled — pass an ISO timestamp (“2026-03-20T10:00:00Z”)
- Next free slot — Blotato picks the next open time in your posting queue
One call per platform. Watch each confirm. Done.
What This Means for You
If you run a channel or business: this is your post-recording workflow. Upload the video, run the agent, everything is scheduled. You review before it goes live, or you don’t — that’s up to you. The time saving is real: what used to take 45–60 minutes of manual work across platforms happens in the time it takes to run one terminal command.
If you offer services: every step here is customisable per client — their brand voice in the skill file, their platform accounts, their posting schedule. Build this workspace once per client and run it every time they record. That’s a retainer product. The workspace itself — brand files, skills, templates, agent scripts — is the deliverable.
The Master Prompt
Copy both blocks below into your Claude Code workspace and you’ll have this pipeline running.
Block 1 is the skill file — save it as skills/shorts-scheduler.md. Fill in your Cloudinary credentials, Blotato account IDs, and brand voice. That’s the only customisation required.
Block 2 is a CLAUDE.md snippet — paste it into a CLAUDE.md file at the root of your workspace so Claude knows how to trigger the skill.
Block 1: Skill File — save as skills/shorts-scheduler.md
# Skill: Shorts Scheduler
> Run this skill when you say "process the shorts" or "schedule the shorts inbox".
---
## What This Does
Takes MOV/MP4 files from `Shorts videos inbox/`, uploads them to Cloudinary, transcribes the audio via Blotato, writes platform-specific descriptions using your brand voice, and schedules them to your chosen social platforms. One conversation per video — no manual copy-paste.
---
## Prerequisites
Before running this workflow, complete these one-time steps:
**1. Cloudinary (free)**
- Sign up at cloudinary.com
- Go to Dashboard → copy your "API Environment variable" string (starts with `cloudinary://`)
- Create a `.env` file in your workspace root and add: `CLOUDINARY_URL=cloudinary://your_key:your_secret@your_cloud_name`
- Install deps: `pip install cloudinary python-dotenv`
**2. Blotato**
- Sign up at blotato.com
- Connect your social accounts in the Blotato dashboard
- Install the Blotato MCP server in Claude Code: add it via Settings → MCP servers
- Once connected, run `blotato_list_accounts` to get your account IDs — you'll need these below
**3. Shorts uploader script**
- Save `Agents/shorts-uploader.py` to your workspace (copy the script from the blog post below)
---
## Your Account IDs
Fill these in once after running `blotato_list_accounts`:
| Platform | accountId | Notes |
|---|---|---|
| YouTube | [YOUR_YOUTUBE_ACCOUNT_ID] | |
| Instagram | [YOUR_INSTAGRAM_ACCOUNT_ID] | |
| TikTok | [YOUR_TIKTOK_ACCOUNT_ID] | |
| LinkedIn | [YOUR_LINKEDIN_ACCOUNT_ID] | pageId: [YOUR_LINKEDIN_PAGE_ID] — if posting as a company page |
| Twitter/X | [YOUR_TWITTER_ACCOUNT_ID] | |
---
## Your Brand Voice
[YOUR_BRAND_VOICE] — e.g. "direct and plain English, no jargon, conversational but professional"
Your niche/topic: [YOUR_NICHE] — e.g. "AI tools for small business", "fitness coaching", "property investment"
Your handle/name: [YOUR_HANDLE] — e.g. "@yourbrand"
---
## Pipeline (repeat for each video file found in the inbox)
### Step 0 — Check the inbox
List all MOV/MP4 files in `Shorts videos inbox/`. Process each one in sequence.
### Step 1 — Upload to Cloudinary
Run:
python3 Agents/shorts-uploader.py "Shorts videos inbox/<filename>"
Note the `public_id`, `video_url`, and `audio_url` from the output.
### Step 2 — Transcribe the audio
Call `blotato_create_source`:
- sourceType: "audio"
- url: the `audio_url` from Step 1 (Cloudinary MP3 transform URL)
Poll `blotato_get_source_status` every 10 seconds until status is `ready`. Extract the transcript text.
### Step 3 — Write platform-specific descriptions
Using the transcript + your brand voice above, write a description for each platform:
Platform | Format
------------|-------
YouTube | 100 characters MAX (including hashtags). One punchy sentence + 1-2 hashtags.
Instagram | Punchy opener, conversational body, 3-5 short sentences. End with a question or CTA.
TikTok | Very short, 1-3 sentences. Hashtags feel native here.
LinkedIn | Professional but human. 2-4 sentences. Frame around business value.
Twitter/X | Under 260 chars. Hook-first. One or two punchy sentences.
Show descriptions before scheduling. Ask for approval or edits.
### Step 4 — Confirm platforms
Ask: "Which platforms do you want to schedule this to? YouTube, Instagram, TikTok, LinkedIn, Twitter/X — or all five?"
### Step 5 — Schedule on all chosen platforms
Call `blotato_create_post` once per platform, using `useNextFreeSlot: true`.
Platform-specific fields:
- YouTube: title (derived from transcript), privacyStatus: "public", shouldNotifySubscribers: false
- Instagram: mediaType: "reel", shareToFeed: true
- TikTok: privacyLevel: "PUBLIC_TO_EVERYONE", disabledComments: false, disabledDuet: false, disabledStitch: false, isBrandedContent: false, isYourBrand: false, isAiGenerated: false
- LinkedIn: pageId: "[YOUR_LINKEDIN_PAGE_ID]"
- Twitter/X: no extra fields
For each post: text = platform description, mediaUrls = [video_url from Step 1]
### Step 6 — Move the file
Run:
mv "Shorts videos inbox/<filename>" "Shorts video posted/<filename>"
(Create `Shorts video posted/` folder first if it doesn't exist.)
### Step 7 — Log Cloudinary files to delete
The Cloudinary video must stay live until Blotato has published it. After each post goes live, delete it:
python3 Agents/shorts-uploader.py --delete <public_id>
Log the `public_id` and scheduled post time so I know when to delete each one.
---
## After All Videos Are Processed
Summarise:
- How many videos were scheduled
- Which platforms
- When they're due to go live (from `useNextFreeSlot`)
- Which Cloudinary `public_id`s to delete, and when
Block 2: CLAUDE.md Snippet
# [YOUR_BUSINESS_NAME] — Shorts Scheduler
This workspace automates posting short-form videos to multiple social platforms from a single folder.
## How to Use
Say: "process the shorts"
Drop one or more MOV/MP4 files into `Shorts videos inbox/` first. Claude will upload, transcribe, write descriptions, and schedule everything.
## Files
- `skills/shorts-scheduler.md` — the full pipeline with your account IDs and brand voice
- `Agents/shorts-uploader.py` — uploads to Cloudinary and returns video + audio URLs
- `Shorts videos inbox/` — drop new videos here to trigger the workflow
- `Shorts video posted/` — processed videos are moved here automatically
Shorts Uploader Script — save as Agents/shorts-uploader.py
#!/usr/bin/env python3
"""Upload a video to Cloudinary and return video_url + audio_url."""
import sys
import os
from urllib.parse import urlparse
from dotenv import load_dotenv
import cloudinary
import cloudinary.uploader
load_dotenv()
CLOUDINARY_URL = os.getenv("CLOUDINARY_URL")
if not CLOUDINARY_URL:
print("ERROR: CLOUDINARY_URL not set in .env")
sys.exit(1)
parsed = urlparse(CLOUDINARY_URL)
cloudinary.config(
cloud_name=parsed.hostname,
api_key=parsed.username,
api_secret=parsed.password
)
if len(sys.argv) < 2:
print("Usage: python3 shorts-uploader.py <filepath>")
print(" python3 shorts-uploader.py --delete <public_id>")
sys.exit(1)
if sys.argv[1] == "--delete":
public_id = sys.argv[2]
result = cloudinary.uploader.destroy(public_id, resource_type="video")
print(f"Deleted: {public_id} — result: {result}")
sys.exit(0)
filepath = sys.argv[1]
if not os.path.exists(filepath):
print(f"ERROR: File not found: {filepath}")
sys.exit(1)
print(f"Uploading {filepath} to Cloudinary...")
result = cloudinary.uploader.upload(filepath, resource_type="video")
public_id = result["public_id"]
video_url = result["secure_url"]
cloud_name = parsed.hostname
audio_url = f"https://res.cloudinary.com/{cloud_name}/video/upload/{public_id}.mp3"
print(f"public_id: {public_id}")
print(f"video_url: {video_url}")
print(f"audio_url: {audio_url}")
Final Thoughts
Record once. Run the agent. Done.
Upload to Cloudinary, transcribe with Blotato, write platform posts with Claude, schedule everything. None of those steps require you to touch a platform UI, log into anything, or write the same content four different ways.
If you want to see how the research and content planning side of this pipeline works, start with The Research Agent That Plans My YouTube Content.
What part of your post-recording workflow takes the most time right now? Drop it in the comments.