DEV Community

Mateo Ruiz
Mateo Ruiz

Posted on

Build an Automated Video Content Distribution Pipeline with n8n, Dropbox, Claude & Opus Clip

Video content distribution is one of the highest-ROI automation targets for any team publishing regularly. The manual process — download, clip, caption, schedule, repeat across platforms — is genuinely painful at scale, and the automated version is achievable with tools most teams already have.

This post covers the full technical architecture of a content distribution pipeline built in n8n, using Dropbox as the entry point, Claude for AI copywriting, and Opus Clip for short-form video generation. Includes parallel processing architecture, Slack approval gate, error handling per failure type, and a free downloadable workflow JSON at the end.


The Problem This Solves

For a single long-form video, the manual distribution process:

  1. Download raw recording from storage
  2. Scrub through timeline to find highlight moments
  3. Clip each moment, export per platform aspect ratio
  4. Write separate captions for LinkedIn, Instagram, X, YouTube
  5. Schedule across platforms individually

For one video: 4–6 hours of manual work. For a team publishing 4+ videos a week: a part-time job with no clear owner, and the first thing to slip when the team gets busy.

The workflow below collapses all of that into one upload and one review.


Architecture

┌──────────────────────────────────────────────────────────────────┐
│                n8n Content Distribution Workflow                  │
│                                                                  │
│  [Dropbox Trigger] ← watches /content-inbox/ on file creation   │
│           ↓                                                      │
│  [Dropbox: Download file binary]                                 │
│           ↓                                                      │
│  [Transcription Service] → raw transcript text                   │
│           ↓                                                      │
│  ┌────────────────────────────────────────┐                      │
│  │         PARALLEL BRANCHES              │                      │
│  │                                        │                      │
│  │  [Claude API]          [Opus Clip API] │                      │
│  │  ↓                     ↓              │                      │
│  │  LinkedIn caption      Short clips     │                      │
│  │  Instagram caption     Auto-captions   │                      │
│  │  X thread              Aspect ratios   │                      │
│  │  YouTube description                   │                      │
│  │  3 title variants                      │                      │
│  └──────────────┬─────────────┬───────────┘                      │
│                 ↓             ↓                                  │
│            [Merge Node: Wait for both branches]                  │
│                       ↓                                          │
│           [Slack: Send to #content-review]                       │
│                  ↓           ↓                                   │
│             [Approve]    [Reject]                                │
│                  ↓           ↓                                   │
│          [Distribution]  [Flag for edit]                        │
│                  ↓                                               │
│          [Google Sheets: Log run]                                │
│                  ↓                                               │
│          [Error Branch: Log + Alert]                             │
└──────────────────────────────────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

Stack

Tool Role Required?
n8n (self-hosted or Cloud) Orchestration Yes
Dropbox + API access File trigger + storage Yes
Transcription service Audio → text Yes
Claude API (Anthropic) AI copywriting Yes
Opus Clip Short-form video clipping Yes
Slack Bot Approval gate + review Recommended
Google Sheets Run logging Optional

Node-by-Node Breakdown

1. Dropbox Trigger

Configure the n8n Dropbox node to watch one specific folder, not the whole account.

Folder path: /content-inbox/
Event type: File Created (NOT File Modified)
Enter fullscreen mode Exit fullscreen mode

Why file creation only? Re-saving or renaming an existing file inside the watched folder must not re-trigger the workflow. Creation events only prevents duplicate processing.

Recommended folder structure:

/content-inbox/
  /2026-09-10/
    webinar-product-demo.mp4
  /2026-09-12/
    podcast-ep-47.mp4
Enter fullscreen mode Exit fullscreen mode

Date-based subfolders keep uploads organized and make it easy to trace which workflow run produced which output.

Opus Clip format note: Standardise on one video export format before uploads start. A format mismatch (webm instead of mp4, for example) discovered mid-run is harder to debug than a pre-upload naming convention enforced at the source.


2. Download File from Dropbox

After the trigger fires, use a second Dropbox node to download the actual file binary:

// Dropbox node config
Operation: Download File
File Path: {{ $json.path_display }}
Binary Property: videoFile
Enter fullscreen mode Exit fullscreen mode

Pass the binary to the transcription node in the next step.


3. Transcription

Send the video binary (or a Dropbox shared URL) to your transcription service.

Critical: Transcription accuracy is the foundation of everything downstream. Claude works from text — it has no access to the original audio. A garbled product name or misheared technical term in the transcript appears in every caption generated from it.

Test the transcription service on several different recording types before going live:

  • Different speakers and accents
  • Variable audio quality (room acoustics, microphone quality)
  • Technical vocabulary specific to your industry

Store the clean transcript as {{ $json.transcript }} for the next stage.


4. Parallel Branch: Claude Copywriting

Start the Claude branch and the Opus Clip branch simultaneously using n8n's parallel execution. Do not run them sequentially — Claude + Opus Clip in parallel is what gets turnaround from days to hours.

Brand Brief Structure

Claude has no memory between API calls. Every request must include the brand brief fresh:

You are a content writer for [Brand Name].

Brand brief:
- Audience: [description of target reader/viewer]
- Tone: [conversational / thought-leadership / technical / warm]
- Voice: [active sentences, avoid passive voice, no corporate jargon]
- Vocabulary to avoid: [list specific phrases]
- Example sentences that match our voice:
  "[Example 1]"
  "[Example 2]"
  "[Example 3]"

Using the video transcript below, generate the following:

1. LinkedIn post (hook + 3-4 body lines + CTA, under 300 words)
2. Instagram caption (conversational, 5-7 hashtags at end)
3. X thread (4-5 tweets, each under 280 chars, numbered)
4. YouTube description (keyword-rich, 150-200 words, include timestamps if available)
5. Three title variants for the video (different angles, under 70 chars each)

Transcript:
{{ $json.transcript }}

Return ONLY valid JSON  no preamble, no markdown fences:
{
  "linkedin": "...",
  "instagram": "...",
  "x_thread": ["tweet 1", "tweet 2", "tweet 3", "tweet 4"],
  "youtube_description": "...",
  "titles": ["title 1", "title 2", "title 3"]
}
Enter fullscreen mode Exit fullscreen mode

The brand brief is not optional. Without it, Claude produces output that is technically correct but generic — immediately recognisable as AI-generated. The brief is the difference between copy that reads like the brand and copy that reads like a template.

Parse Claude Response

// Function Node — parse Claude JSON
const raw = $input.first().json.content[0].text;

// Strip accidental markdown fences
const clean = raw
  .replace(/^```
{% endraw %}
json\s*/i, '')
  .replace(/\s*
{% raw %}
```$/,'')
  .trim();

let parsed;
try {
  parsed = JSON.parse(clean);
} catch(e) {
  throw new Error(`Claude JSON parse failed. Raw: ${raw.substring(0, 300)}`);
}

return [{
  json: {
    linkedin: parsed.linkedin,
    instagram: parsed.instagram,
    x_thread: parsed.x_thread,
    youtube: parsed.youtube_description,
    titles: parsed.titles
  }
}];
Enter fullscreen mode Exit fullscreen mode

5. Parallel Branch: Opus Clip

While Claude generates copy, route the video to Opus Clip simultaneously.

Opus Clip identifies clip-worthy moments automatically — no manual scrubbing — and returns:

  • Short clips (typically 30–90 seconds)
  • Auto-generated captions per clip
  • Multiple aspect ratios (9:16 vertical, 1:1 square)

Store returned clip URLs as an array for the merge step.

Rate limit note: Opus Clip's processing queue can back up during burst uploads (multiple videos submitted simultaneously at the start of a week). Build in a queue delay if processing multiple videos in one batch run.


6. Merge Node

After both branches complete, use n8n's Merge node to combine outputs before routing to review.

Merge mode: Wait for All Inputs
Input 1: Claude copy branch
Input 2: Opus Clip clips branch

Output: {
  copy: { linkedin, instagram, x_thread, youtube, titles },
  clips: [ clip_url_1, clip_url_2, clip_url_3 ]
}
Enter fullscreen mode Exit fullscreen mode

The merge node pauses until both branches return results. If either branch fails, the error branch triggers instead of the merge proceeding with incomplete data.


7. Slack Approval Gate

Do not skip this step. Everything goes through human review before any content reaches a public channel.

// Build Slack Block Kit review message
const { linkedin, titles, clips } = mergedOutput;

const blocks = [
  {
    type: "header",
    text: { type: "plain_text", text: "📹 New Content Ready for Review" }
  },
  {
    type: "section",
    text: {
      type: "mrkdwn",
      text: `*Source file:* ${sourceFileName}\n*Titles generated:*\n• ${titles.join('\n')}`
    }
  },
  {
    type: "section",
    text: { type: "mrkdwn", text: `*LinkedIn draft:*\n${linkedin.substring(0, 300)}...` }
  },
  {
    type: "section",
    text: {
      type: "mrkdwn",
      text: `*Clips generated:* ${clips.length}\n${clips.map((url, i) => `<${url}|Clip ${i+1}>`).join(' · ')}`
    }
  },
  {
    type: "actions",
    elements: [
      {
        type: "button",
        text: { type: "plain_text", text: "✅ Approve & Schedule" },
        style: "primary",
        value: "approve",
        action_id: "content_approve"
      },
      {
        type: "button",
        text: { type: "plain_text", text: "❌ Reject" },
        style: "danger",
        value: "reject",
        action_id: "content_reject"
      }
    ]
  }
];
Enter fullscreen mode Exit fullscreen mode

Approval response handling:

  • Slack sends a POST to a second n8n webhook when the button is clicked
  • n8n's Wait node pauses the workflow until that webhook fires
  • approve → continue to distribution
  • reject → flag source file for manual editing, stop workflow, log outcome

For regulated industries (healthcare, finance, legal): Add a second approval gate for compliance review before the first distribution step. Log approver identity and timestamp on every run.


8. Error Handling

Every external API node needs an error branch. Build one that covers the full pipeline:

// Error branch function node
const errorDetails = {
  timestamp: new Date().toISOString(),
  pipeline: "Content Distribution",
  failed_node: $input.first().json.failedNode || "unknown",
  error_message: $input.first().json.error?.message || "Unknown error",
  source_file: $('Dropbox Trigger').first().json?.name || "unknown",
  status_code: $input.first().json.error?.statusCode
};
return [{ json: errorDetails }];
Enter fullscreen mode Exit fullscreen mode

After capturing error details:

  1. Log to Google Sheets (source_file, failed_node, error_message, timestamp)
  2. Send Slack alert to #content-ops with error summary
  3. Do NOT retry automatically for format errors or auth failures

Common Failure Points

Failure Cause Handling
Transcription timeout Long video (60+ min), slow service Retry once (60s delay), then alert
Claude empty response Prompt too long, API overload Retry once, then flag for manual
Claude JSON parse error Model added markdown fences Strip with regex, retry once
Opus Clip content rejection Policy violation on clip content Flag for manual review, do not retry
Dropbox 401 OAuth token expired Alert team, manual token refresh required
Claude/Opus rate limit (429) Burst of uploads Queue with delay, retry after backoff
Merge timeout One branch never returned Alert, log which branch failed

Customisation Options

Swap Dropbox for Google Drive

If the team stores recordings in Google Drive instead of Dropbox, replace the trigger and download nodes with their Google Drive equivalents. The rest of the workflow is unchanged. Test the file binary format — Google Drive exports may need format conversion before transcription.

Add a Video Length Filter

Skip processing for videos under a threshold (e.g. under 5 minutes rarely have enough content to clip):

// IF node condition
{{ $json.duration_seconds }} > 300
// true → continue
// false → log as skipped, notify uploader
Enter fullscreen mode Exit fullscreen mode

Route by File Naming Convention

Apply different processing rules based on file name:

const filename = $json.name.toLowerCase();

if (filename.includes('internal')) {
  return [{ json: { route: 'skip_distribution' } }];
} else if (filename.includes('client-')) {
  return [{ json: { route: 'client_approval_chain' } }];
} else {
  return [{ json: { route: 'standard' } }];
}
Enter fullscreen mode Exit fullscreen mode

Multi-Client / Multi-Brand Setup

Run separate workflow instances per client, each with:

  • A dedicated Dropbox folder
  • A client-specific brand brief in the Claude node
  • A separate Slack channel for review
  • Individual Google Sheets log per client

Avoid conditional branching inside one workflow for multi-client setups — separate instances are easier to debug, permission, and hand off.

Add Google Sheets Logging

After every successful run (approved or rejected), log to a sheet:

Columns: source_file | run_date | clips_generated | approved_by | approved_at | platforms_scheduled | status
Enter fullscreen mode Exit fullscreen mode

This creates a searchable content record without maintaining it manually.


Limitations

Transcription quality gates everything. Poor audio = poor transcript = poor AI copy. No amount of prompt tuning compensates for a bad transcript. Improve recording quality at the source before optimising the pipeline.

Claude can't evaluate factual accuracy. It generates from the transcript. If the speaker said something imprecise, the caption reflects it.

Opus Clip selects candidates, not guarantees. Some clips will miss context obvious to a human reviewer. The approval gate catches these.

No persistent dedup by default. If the same file is uploaded twice (re-saved to the same folder), the workflow fires twice. Add a filename-based check in a Function node before the transcription step if this is a concern.

API rate limits at scale. Claude API and Opus Clip processing queues can throttle under burst load. For high-volume setups (multiple videos per day), stagger uploads or add queue delays between workflow runs.


Get the Free Workflow JSON

IT Path Solutions published the complete n8n workflow — Dropbox trigger, transcription node, parallel Claude + Opus Clip branches, merge node, Slack approval gate, error handling branch, and Google Sheets logging — all pre-connected.

Import into any n8n instance (self-hosted or Cloud), add credentials, run against one video first.

👉 Download the free workflow JSON + full setup guide

Setup guide covers: Dropbox API setup, Claude API key configuration, Opus Clip account connection, Slack bot token scoping, brand brief structure, and how to test end-to-end before going live.


Summary

The pipeline works because it runs Claude and Opus Clip in parallel, routes everything through a single human review gate, and logs every run automatically. A 45-minute recording becomes a full set of clips and platform-specific copy in a few hours — ready to approve, not ready to rework from scratch.

Run it against one video. Review the output. Adjust the brand brief if the copy tone is off. Then decide if it's ready for the full pipeline.

Full guide and JSON: itpathsolutions.com/automated-content-distribution-pipeline


What breaks first in your content distribution setup? Drop it in the comments.

Top comments (0)