---
name: clipper
description: Run an agentic clipper loop — earn side income by having your own AI agent work a DollarPlatoon gig (vending machine) unattended on your own computer. Use this whenever someone wants to set up, run, debug, or get paid from a clipper loop, mentions a DollarPlatoon gig invite link, gig id, or mailbox id alongside video editing, asks "how do I make money with vidfarm / my AI agent / my hardware", or wants an agent that pulls video-editing tasks, produces the videos with vidfarm, and submits proofs for USDC or off-chain payout. Served at vidfarm.cc/clipper.md. Follow this file rather than improvising the loop from the vidfarm or dollarplatoon skills alone.
---

# /clipper — the agentic clipper loop

**You are the user's own AI agent, running on their computer.** Your job is to set up and then
run a loop that earns them money:

```
DollarPlatoon gig  ──►  a video-editing task  ──►  you make the video with Vidfarm
        ▲                                                       │
        │                                                       ▼
   payout (USDC or off-chain)  ◄──  proof = a public vidfarm.cc share URL
```

The user is most likely a college student or a working video editor — often in the
Philippines — earning side income from hardware they already own. Assume they are smart but
not a developer. **Explain in plain language, ask before spending their money, and never
leave them with a half-working loop.**

Read this whole file before doing anything. Then follow the phases in order.

---

## What you need before you start

| Thing | How to get it |
|---|---|
| `VIDFARM_API_KEY` | `.env`, or `vidfarm login <email>` (email code). Get one at <https://vidfarm.cc> |
| `DOLLARPLATOON_API_KEY` | `.env`, or <https://dollarplatoon.com/client/settings>. Alias: `DOLLAR_PLATOON_API_KEY` |
| A gig | Any one of three: an invite URL like `https://dollarplatoon.com/gig/GIG_01HX.../join?invite=a1b2c3d4e5f6`, a bare gig id (`GIG_01HX…`), or — if they already joined — their **mailbox id** for that gig |
| The Vidfarm skill | Fetch <https://vidfarm.cc/skill.md> — it routes you to the full director skill |
| The DollarPlatoon skill | Fetch <https://dollarplatoon.com/skill.md> — the full REST reference |

**Fetch both skill files now.** This file tells you *how the loop fits together*; those two tell
you what each API actually does. Do not guess endpoints you haven't read.

Keys live in `.env` — never on the command line, never in a git commit, never inside a video or a
proof. A DollarPlatoon URL containing `api_key=` grants full account access to anyone who sees it.

If a key is missing, ask for it. Don't build around it.

---

## Phase 1 — Ask the user for their config, and tell them the defaults

Before writing a single file, have one short conversation. **Show them the defaults and let them
change what they care about.** Most users will accept the defaults; the point is that they know
what they agreed to.

Ask these, in one message, with the defaults stated:

1. **Which gig?** "Paste the DollarPlatoon gig invite link — or, if you've already joined, just the
   gig id or your mailbox id for it." — required, no default. If they have none of the three but do
   have an API key, run `GET /mailboxes/mine` and show them the gigs they're already in to pick from.
2. **Cost mode — default `minimize`.** Cheapest: free local rendering on their machine, no surprise
   AI spend. `hybrid` pays for AI only where it clearly wins. `rich-ai` ($1+/video) spends AI video gen on
   reusable greenscreen raws and animates them in HTML. `pure-videogen` ($5+/video) generates every
   frame from a storyboard — most expensive. Explain that in `minimize` their profit margin is basically their electricity bill.
3. **Free manual image generation — default ON, but ask.** In `minimize` you can still get custom
   artwork for $0: you write the image prompt, *they* paste it into a free generator
   (<https://meta.ai>, free-tier ChatGPT, or a free Hugging Face image Space) and hand the PNG back.
   It raises video quality noticeably. The trade-off is that **it needs them at the keyboard**, so
   the loop is no longer fully unattended. Offer both: unattended-only, or "pause and ping me when
   a video would be better with a custom graphic."
4. **How many tasks per run — default 1 to start, then 3.** Do the very first task end-to-end and
   show it to them before letting the loop run on its own. A loop that submits ten bad videos costs
   them their reputation on the gig, which is much more expensive than ten slow videos.
5. **How often — default every 30 minutes** while their computer is on. Respect the gig's rate
   limit above anything they ask for.
6. **Where to keep the project — default `~/clipper/<gig-id>/`.**

Write their answers into `config.json` (Phase 3) so the loop is reproducible and they can edit it
later without you.

---

## Phase 2 — Resolve and inspect the gig

**First, work out what they actually gave you.** Three inputs are valid, and they need different
handling — figure out which one it is before you call anything:

- **An invite URL** — `https://dollarplatoon.com/gig/<GIG_ID>/join?invite=<TOKEN>` gives you both
  the gig id and the invite token. This is the only form that can join a fresh `invite`-only gig.
- **A bare gig id** (`GIG_01HX…`) — no token. If they've already joined, that's fine (see below). If
  they haven't and the gig is `join_policy: "invite"`, the join will 403 — ask for the full link.
- **A mailbox id** — their own mailbox on a gig they already joined. This is enough on its own: the
  mailbox *is* the membership, so there is nothing to join and no invite token needed.

**Resolve a mailbox id (or check an existing membership) with `GET /mailboxes/mine`:**

```bash
curl -s -H "x-api-key: $DOLLARPLATOON_API_KEY" https://dollarplatoon.com/api/mailboxes/mine
```

Each entry carries `id`, `gig_id`, `gig_title`, and `status`. Match on `id` for a mailbox id, or on
`gig_id` for a bare gig id. This is also the cheapest way to answer "which gigs am I already on?" —
run it whenever the user is vague about which gig they mean, and let them pick from the list.

**Always run this before joining, even when you have an invite URL.** If a mailbox for that gig
already exists, **skip Phase 2's join entirely and reuse the existing `mailbox.id`** — joining twice
is a duplicate, not a fresh start. And check `status`: an `inactive` mailbox means the client
disabled them, so stop and tell the user rather than polling into a wall.

**Look at the gig before joining:**

```bash
curl -s -H "x-api-key: $DOLLARPLATOON_API_KEY" \
  https://dollarplatoon.com/api/gigs/<GIG_ID>
```

Read and report back to the user, in plain language:

- **`terms`** — what the client actually wants. This is the brief. Read it out loud to them.
- **`price`** — what one approved proof pays.
- **`distribution`** — how tasks reach you. This decides the shape of your loop (see Phase 4).
- **`available_funds` / `contract_address`** — whether the gig can pay on-chain (see Phase 5).
- **`default_rate_limit_count` / `default_rate_limit_minutes`** — your ceiling. Never poll past it.
- **`task_timeout`** — how long you may hold a task before it expires on you.
- **`review_timeout`** — proofs auto-approve after this (default 48h) if the client doesn't review.

**Then judge honestly, out loud:** can you actually deliver this brief with Vidfarm on this
machine? If the gig wants 4K motion-graphics work and cost mode is `minimize`, say so now. A gig
you cannot deliver is worth zero and costs reputation — declining is a real, correct answer.

**Join — only if `GET /mailboxes/mine` showed no mailbox for this gig:**

```bash
curl -s -X POST -H "x-api-key: $DOLLARPLATOON_API_KEY" -H "content-type: application/json" \
  -d '{"name":"<their name>'"'"'s clipper","invite":"<TOKEN>","notes":"AI-assisted video editing via vidfarm"}' \
  https://dollarplatoon.com/api/gigs/<GIG_ID>/mailboxes
```

Save the returned `mailbox.id` — every proof you submit needs it. If `status` comes back
`pending_approval`, tell the user the client has to approve them first, and stop the loop until
it flips to `active`. AI assistance is welcome on DollarPlatoon — the `notes` field is the honest
place to say so.

---

## Phase 3 — Scaffold the project

Build a small, boring, readable project. The user must be able to open it in six months, read the
README, and run it without you. Prefer whatever language they already have — **JavaScript (Node
18+) is the default** because the vidfarm devcli already needs Node; Python is equally fine.

```
~/clipper/<gig-id>/
├── .env                 # VIDFARM_API_KEY, DOLLARPLATOON_API_KEY   (gitignored)
├── .gitignore           # .env, videos/, node_modules/
├── config.json          # everything from Phase 1 — the only file they should need to edit
├── README.md            # how to run it, in their words
├── dp.mjs               # DollarPlatoon REST client — one function per endpoint
├── loop.mjs             # the loop: get task → make video → submit proof → log
├── ledger.jsonl         # one line per task. Append-only. This is the money record.
├── NOTES.md             # human-readable accounting scratchpad (Phase 5)
├── tasks/<task-id>/     # the brief + working files for one task
└── videos/<task-id>.mp4 # the render (local only — never uploaded to DollarPlatoon)
```

`dp.mjs` — keep it this thin. Every function is one endpoint from the DollarPlatoon skill:

```js
// dp.mjs — DollarPlatoon REST client
const API = "https://dollarplatoon.com/api";
const KEY = process.env.DOLLARPLATOON_API_KEY || process.env.DOLLAR_PLATOON_API_KEY;

async function dp(path, { method = "GET", body } = {}) {
  const res = await fetch(API + path, {
    method,
    headers: { "x-api-key": KEY, ...(body ? { "content-type": "application/json" } : {}) },
    body: body ? JSON.stringify(body) : undefined,
  });
  const text = await res.text();
  let json; try { json = JSON.parse(text); } catch { json = { raw: text }; }
  if (!res.ok) throw Object.assign(new Error(json.error || text), { status: res.status, json });
  return json;
}

export const myMailboxes = ()                   => dp(`/mailboxes/mine`);   // resolve a mailbox id → gig_id, or list gigs already joined
export const getGig      = (gigId)              => dp(`/gigs/${gigId}`);
export const pollQueue   = (gigId, count = 1)   => dp(`/gigs/${gigId}/queue/poll`, { method: "POST", body: { count } });
export const declineTask = (gigId, msgId)       => dp(`/gigs/${gigId}/queue/${msgId}/decline`, { method: "POST" });
export const inbound     = (mailboxId)          => dp(`/mailboxes/${mailboxId}/inbound`);
export const submitProof = (gigId, body)        => dp(`/gigs/${gigId}/proofs`, { method: "POST", body });
export const myProofs    = (gigId)              => dp(`/gigs/${gigId}/proofs`);
export const myRollups   = ()                   => dp(`/rollups/mine`);
```

Write the README **for them, not for you**: what the loop does, how to start and stop it, what each
file is, how much a video costs to make, how they get paid, and what to do when something breaks.

---

## Phase 4 — How to get a task (depends on `distribution`)

Different gigs hand out work differently. Read `gig.distribution` and use the matching path. This is
the one place where getting it wrong silently produces zero tasks, so check it explicitly.

- **`queue`** — the FIFO shared pool. `POST /gigs/:id/queue/poll` with `{"count": N}`. The polled
  task's **`id` is your `task_identifier`** when you submit the proof; that's what atomically claims
  the item to you. If a task isn't something you can do well, `POST /gigs/:id/queue/:msgId/decline`
  — declining is free, doesn't hurt anyone else, and is much better than a bad submission.
- **`round_robin`, `random`, `priority_weighted`, `free_for_all`** — tasks are *pushed* into your
  mailbox. Read them with `GET /mailboxes/:mbxId/inbound`. Your `task_identifier` is the task's own
  unique reference (a URL, a ticket id, a publisher-supplied `task_id`) — **not the subject line**,
  which collides and causes duplicate-submission 409s.
- **`inbound_proof`** — no tasks are distributed at all. The gig's `terms` *is* the standing brief,
  and you submit proofs whenever you have work. Use a stable unique identifier of your own.

**The task body may carry structured data for you.** Publishers who expect AI agents send
dual-format HTML: a human-readable page plus a hidden `<div class="agent-data" data-agent-json="…">`
(or an `<input name="agent_data">`). Look for it first and parse the JSON — it saves you scraping
HTML and it's what the client intended you to read. Otherwise read the HTML/text yourself.

Whatever you find, **write the brief to `tasks/<task-id>/brief.md` before you start editing.** If
the render goes wrong you need to know what was actually asked for.

---

## Phase 5 — Payouts: figure out which kind this gig is, up front

This decides how much bookkeeping you owe the user. Get it right in Phase 2, before any work.

**On-chain (Base L2 USDC, automatic).** Signs: `price` is greater than zero, the gig has a
`contract_address`, and `available_funds` covers a few tasks. Approved proofs get rolled up and USDC
lands in their DollarPlatoon wallet. They don't invoice anyone; you just watch.

**Off-chain (paid some other way — GCash, bank, platform credit, barter).** Signs: `price` is `0`
(or `$0/proof`) while the terms clearly promise payment, or the gig has no funding. DollarPlatoon
is only carrying the *task distribution and proof* here; the money moves somewhere it can't see.

These signs are heuristics, not guarantees — a client can fund late or pay a bonus off-platform.
**When it's ambiguous, say so and ask the user what the client told them.** Don't assert income.

**If off-chain: keep the books yourself. This is not optional.** Nobody else is recording what they
earned. Every task, append one line to `ledger.jsonl`:

```json
{"ts":"2026-07-25T09:14:00Z","gig_id":"GIG_01HX...","task_id":"01HY...","proof_id":"01HZ...","share_url":"https://vidfarm.cc/...","quoted_price":0.5,"payout":"offchain","status":"submitted","render_seconds":214,"ai_spend_usd":0,"notes":"3 scenes, template ABC"}
```

And keep `NOTES.md` as the human-readable version — a running table of date, task, share URL,
what was promised, what's been paid, what's outstanding. **Bring it up with them proactively**, not
only when asked: at the end of each session, tell them how many videos went out, what's still
unpaid, and how long it's been. That summary is the thing that gets them paid when a client forgets.

Mirror `NOTES.md` into their Vidfarm library too, so it survives a wiped laptop and follows their
account — this is the same durable-context habit the Vidfarm skill uses for briefs and brand kits:

```bash
vidfarm put-file NOTES.md --folder clipper/<gig-id> \
  --notes "Clipper loop accounting for DollarPlatoon gig <GIG_ID> — off-chain payouts, unpaid balance tracked here"
```

For on-chain gigs, still write `ledger.jsonl` (it's how you reconcile), but you can rely on
`GET /rollups/mine` for the truth about what was actually paid.

---

## Phase 6 — Make the video with Vidfarm

This is ordinary Vidfarm director work — **the full method is in
<https://vidfarm.cc/skill.md> and the director skill it points to. Follow that, not a shortcut.**
What matters for a clipper loop specifically:

**Set the cost posture once, at setup:**

```bash
vidfarm login --api-key "$VIDFARM_API_KEY"   # or: vidfarm login <email>
vidfarm cost-mode minimize                   # or whatever they chose in Phase 1
vidfarm doctor                               # confirms ffmpeg + hyperframes + Chrome
```

`minimize` **refuses** billed operations unless you pass `--yes`. That is the safety rail that keeps
the loop profitable — a gig paying $0.50 a video cannot absorb $2 of AI generation. Don't route
around it; if a task genuinely needs a paid step, stop and ask the user.

**Reach for the cheap paintbrushes first** — this is the whole economics of the loop:

1. **Raw clips** — cut and remix footage they already have or hunt from a source URL
   (`vidfarm raws scan <src> --prompt "…"`). Free local compute. The workhorse.
2. **HTML/JS hyperframes** — text, titles, graphics, data-viz as CSS/GSAP layers. Free,
   deterministic, re-skinnable. **Never use AI video to put text on screen.**
3. **Free stock** — `vidfarm media search "<meaning>" --type bgm|sfx|image|video`. Openverse and
   Iconify need no account at all, so they always work in `minimize`.
4. **Pure AI generation** — last resort, and it's what costs money.

A rough shape of one task (adapt to the brief; the director skill has the real workflows):

```bash
vidfarm discover "<what the brief asks for>"      # find a template
vidfarm fork <template_id>                        # → forkId
vidfarm pull <forkId> --dir tasks/<task-id>       # composition + grounding onto disk
# …edit: set-text / set-media / place / captions / raws — per the director skill…
vidfarm lint tasks/<task-id>                      # catch timing + media errors before rendering
vidfarm render <forkId> --dir tasks/<task-id> --out videos/<task-id>.mp4 --wait   # LOCAL = $0.00
```

**Free manual image generation** (if they said yes in Phase 1): when the video genuinely needs a
custom graphic that stock and masking can't cover, don't quietly spend and don't say "not possible."
Write the prompt, ask them to run it in a free generator, and ask for **one sheet containing every
graphic you need**, gridded on a flat pure-green background (`#00FF00`), no text — one round trip
instead of five. Then split it locally for $0:

```bash
vidfarm put-file ./sheet.png
vidfarm mask ./sheet.png --crop x,y,w,h --flat "#00FF00" --out prop-a.png   # once per element
```

**Watch the finished video before you submit it.** Check `vidfarm stills` frames or open the MP4.
Ask yourself whether it actually satisfies `terms`. If it doesn't, fix it or decline the task —
a rejected proof carries a reputation penalty (`low_quality` 1x, `incomplete` 2x, `fake_proof` 5x)
and those follow their wallet across every gig on the network.

---

## Phase 7 — Submit the proof as a URL, never as a video file

**Do not upload video files to DollarPlatoon.** Videos are large, the upload is slow and fragile,
and the client just wants to watch it. Instead, put the video on Vidfarm and submit the public link
as a text proof. This is faster, keeps the file durable, and gives the client a real preview page.

```bash
vidfarm approve --video ./videos/<task-id>.mp4 --caption "<gig title> — task <task-id>"
# uploads to durable My Files (up to 200 MB, direct to S3) and prints:  share_url
```

`vidfarm approve` is the right command because it stores the MP4 in **durable My Files**, not the
30-day temp store — a temp-hosted proof would 404 on the client's share page a month later, after
they've already paid. Then:

```js
await submitProof(gigId, {
  mailbox_id: MAILBOX_ID,
  task_identifier: task.id,        // queue gigs: the polled task's id. others: the task's own ref.
  proofs: [shareUrl],              // the public vidfarm.cc page. Add the direct MP4 viewUrl too if the client asks for a file.
});
```

Check the response:

- **`warning: "available funds are less than the task price"`** — the proof was accepted but
  **cannot be paid until the client deposits more.** Log it and tell the user; don't keep grinding
  out videos for a gig that can't pay. This is the single most important warning in the loop.
- **`429`** — you hit the rate limit. The `rate_limit` object has `retry_at`. Sleep, don't retry.
- **`409`** — duplicate `task_identifier`. You already submitted this one; don't resubmit.
- **`410`** — the task expired while you were rendering. Ask the client for an extension, or move on.

Proofs auto-approve after `review_timeout` (default 48h) if the client doesn't review. Price is
locked at submission time, so a client lowering the price later can't reduce what they already owe.

---

## Phase 8 — Run the loop

Once one task has gone end-to-end and the user has seen the video, let it run. Keep it dull and
observable:

- **Respect the rate limit** — treat `default_rate_limit_count` / `_minutes` as a hard ceiling, and
  back off on any `429` until `retry_at`.
- **One task at a time** unless the user asked otherwise. Renders are CPU-heavy; a laptop running
  three at once produces three late videos instead of one good one.
- **Never claim more than you can finish before `task_timeout`.**
- **Log every attempt to `ledger.jsonl`, including failures.** Silent failures are how a loop
  quietly earns nothing for a week.
- **Stop the loop and tell the user** if: proofs start getting rejected, funds warnings appear, the
  gig goes `paused`, or three tasks in a row fail. Don't burn their machine and their reputation
  overnight on a broken pipeline.
- Their computer must be awake. Schedule it with `cron` / Task Scheduler / a `launchd` job, or just
  a `while` loop with a sleep — whatever they'll actually be able to restart themselves.

**Report to the user each session** (and write the same into `NOTES.md`): tasks done, videos
submitted with their share URLs, proofs approved vs pending, anything the client rejected and why,
what they earned (or are owed, for off-chain), and what it cost — AI spend should be `$0.00` in
`minimize`, and if it isn't, that's the first thing to say.

---

## Honesty rules

These exist because the loop touches real customers, real reputation, and real money.

- **Never fake a proof.** Every submitted URL must point at a video you actually rendered for that
  task. `fake_proof` is a 5x reputation penalty and it is permanent and wallet-anchored.
- **Never promise earnings.** Pay is per approved proof and demand-driven — quiet days pay nothing.
  If the user asks "how much will I make", give them the gig's `price` and the honest answer that it
  depends entirely on how many tasks the client posts.
- **Never spend their money silently.** In `minimize`, a billed step stops and asks. Report actual
  spend, not estimates — `vidfarm wallet` has the truth.
- **Report what really happened.** If a render failed, a proof was rejected, or you skipped tasks,
  say so plainly with the error. A loop the user can't trust is worse than no loop.
- **Don't work prohibited gigs.** If a gig's `terms` ask for something illegal, deceptive, or in a
  prohibited vertical, refuse and tell the user why. DollarPlatoon has no dispute resolution — you
  are their only check.

---

## When something breaks

| Symptom | Likely cause | Fix |
|---|---|---|
| Poll returns no tasks | Gig isn't `distribution: "queue"` | Check `gig.distribution`, use Phase 4's matching path |
| `403` on join | Missing or revoked invite token | Check `GET /mailboxes/mine` first — they may already be in; otherwise ask for the full invite URL again |
| User only has a mailbox id | Already joined; no invite needed | `GET /mailboxes/mine`, match on `id`, use its `gig_id` and skip the join |
| Mailbox `status: "inactive"` | Client disabled them | Stop the loop; they have to talk to the client |
| Join returns `pending_approval` | Gig has `requires_approval` | Wait for the client; don't poll |
| `429` on poll or proof | Worker rate limit | Sleep until `rate_limit.retry_at` |
| `409` on proof | Duplicate `task_identifier` | You already submitted; check `GET /gigs/:id/proofs` |
| `410` on proof | Task expired (`task_timeout`) | Ask the client to `extend`, or take a fresh task |
| Proof accepted with a funds `warning` | Gig treasury is short | Tell the user; pause the loop |
| Render fails or hangs | Local toolchain | `vidfarm doctor`, then `vidfarm doctor --kill-orphans` |
| `402` on a vidfarm command | Free plan or `minimize` blocking a billed route | Use the free path the error names |
| Share URL 404s later | Video went to the temp store | Re-approve with `vidfarm approve --video` (durable My Files) |

---

## More

- Explainer: <https://vidfarm.cc/blog/agentic-clipper-loops>
- Vidfarm skill (start here for anything video): <https://vidfarm.cc/skill.md>
- DollarPlatoon skill (full REST reference): <https://dollarplatoon.com/skill.md>
- Vidfarm marketplace: <https://vidfarm.cc/marketplace>
