@officexapp/vidfarm-devcli 0.21.47 → 0.21.49
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -397,7 +397,7 @@ You may be running as the **in-web AI chat** (the /editor copilot, the chat dock
|
|
|
397
397
|
|
|
398
398
|
| File | Size | Read it when |
|
|
399
399
|
|---|---|---|
|
|
400
|
-
| `references/core-workflows.md` | ~
|
|
400
|
+
| `references/core-workflows.md` | ~485 ln | Template discovery, auth, fork → render → approve → share, versioning, cost/wallet, marketplace orders, the customer's Dollar Platoon key + the four gig quick tasks, dedupe-before-publish |
|
|
401
401
|
| `references/editor-workflows.md` | ~650 ln | **The biggest read.** Timeline editing, decompose, captions, transitions, motion, AI placement, the caption standard, the editor action verbs |
|
|
402
402
|
| `references/assets-and-sourcing.md` | ~275 ln | **Google search for footage** (the query playbook), raws hunts, clip scanning, My Files, recurring characters, downloading media off a URL, social recycle |
|
|
403
403
|
| `references/agent-included-imagegen.md` | ~70 ln | **Free custom art without spending.** Image generation the AGENT's own subscription already includes (Antigravity / Gemini CLI, Codex / ChatGPT — Claude Code has none): why it is allowed in cost mode `minimize`, `vidfarm agent-image --check` / the brief command, where it sits on the asset ladder, and the honest limits |
|
|
@@ -481,7 +481,7 @@ The File Index above says what each file *is*; this says which one a given ask m
|
|
|
481
481
|
5b. If the task is an **explainer built from cutout/sticker art** — flat illustrations on a stage, a sticker sheet, keyed art, “make it look like those animated explainer videos” — read `recipes/cutout-graphics-for-explainers.md`. It carries the house style, the sheet→sticker pipeline, and the dark-stage rules that are easy to get wrong.
|
|
482
482
|
5c. If the task is **introducing a product a stranger has never heard of** — a client's URL turned into a 20–30s "what is this?" video, a launch/brand-intro clip, or a batch of N customer URLs → N videos that must not look alike — read `harnesses/product-explainer.HARNESS.md`. It is the format with the single most expensive defect in the catalog (the product never plainly named in the first 5s, which costs a VO re-record to fix), plus the simple-open text-run count, the sticker dosage, and the anti-convergence assignment method. Use `product-demo` instead when you actually have the UI on screen.
|
|
483
483
|
6. If the task explicitly asks for a primitive or needs specialized generation/transcription work, read `references/primitives.md`.
|
|
484
|
-
7. If the task is the MARKETPLACE (ordering videos from specialist agents): browsing is web-only for paying customers — send the human to https://vidfarm.cc/marketplace, never render it locally.
|
|
484
|
+
7. If the task is the MARKETPLACE (ordering videos from specialist agents, or running your own two vending machines): browsing is web-only for paying customers — send the human to https://vidfarm.cc/marketplace, never render it locally. **`GET /api/v1/user/me/marketplace` hands you the customer's own Dollar Platoon API key** (paid plans only), so an agent with a `vidfarm-api-key` needs nothing copied out of Settings. Orders plus the four quick tasks — check a machine for new proofs, approve/reject one, add a task, ring the bell on the feed — are worked examples in `references/core-workflows.md` (§ Marketplace, § Your Dollar Platoon key). Everything deeper on a gig (mailboxes, the queue, per-task pricing, payouts, wallets, reputation) is Dollar Platoon's own API: **read `https://dollarplatoon.com/skill.md`**, its accurate live index, or install it with `npx skills add https://github.com/OfficeXApp/dollarplatoon-skill` — the same way FlockPoster work beyond scheduling needs `npx skills add https://github.com/OfficeXApp/flockposter-skill`. Dollar Platoon authenticates on `x-api-key` ONLY; a Bearer token 401s as "Missing x-api-key header" before the key is read.
|
|
485
485
|
|
|
486
486
|
## Non-Negotiables
|
|
487
487
|
|
|
@@ -264,7 +264,149 @@ POST /api/v1/marketplace/orders/:gigId/invite { "slug": "<agent portfolio slug
|
|
|
264
264
|
|
|
265
265
|
devcli: `vidfarm marketplace` (browse links), `vidfarm marketplace orders`, `vidfarm marketplace order --title <t> --brief <b> [--count <n>] [--price <usd>] [--rate-limit-per-day <n>] [--tags a,b] [--folder <path>]` (defaults: 10 videos, $0.50, 2/day per worker), `vidfarm marketplace invite <gigId> --portfolio <slug>`.
|
|
266
266
|
|
|
267
|
-
|
|
267
|
+
## Your Dollar Platoon key, from the vidfarm REST API
|
|
268
|
+
|
|
269
|
+
Every paid vidfarm account owns a Dollar Platoon account, and **one route hands the agent that credential** — so an agent holding only a `vidfarm-api-key` can drive the whole gig side without the human copying anything out of Settings.
|
|
270
|
+
|
|
271
|
+
```bash
|
|
272
|
+
curl -s -H "vidfarm-api-key: $VIDFARM_API_KEY" -H "accept: application/json" \
|
|
273
|
+
"https://vidfarm.cc/api/v1/user/me/marketplace"
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
```jsonc
|
|
277
|
+
{
|
|
278
|
+
"ok": true,
|
|
279
|
+
"dollarplatoon_api_key": "dp_live_…", // ← the customer's OWN key
|
|
280
|
+
"dollarplatoon_api_url": "https://dollarplatoon.com/api",
|
|
281
|
+
"dollarplatoon_skill_url": "https://dollarplatoon.com/skill.md",
|
|
282
|
+
"dollarplatoon_auth_header": "x-api-key",
|
|
283
|
+
"feed_id": "FEED_01M0BCTVTKSDFZQVEY1RXA4NJK",
|
|
284
|
+
"feed": { "notifications_url": "…", "notifications_result_key": "notifications",
|
|
285
|
+
"registry_url": "…", "registry_result_key": "items" },
|
|
286
|
+
"machines": [
|
|
287
|
+
{ "slug": "custom_requests", "title": "Custom Requests", "distribution": "queue",
|
|
288
|
+
"gig_id": "GIG_01H…", "invite_url": "https://dollarplatoon.com/gig/GIG_01H…/join?invite=…",
|
|
289
|
+
"proofs_url": "https://dollarplatoon.com/api/gigs/GIG_01H…/proofs",
|
|
290
|
+
"add_task_webhook_url": "https://dollarplatoon.com/api/inbound/webhook/GIG_01H…?token=abc123",
|
|
291
|
+
"add_task_email": "GIG_01H…_abc123.dollar-platoon@fwd.zoomgtm.com" },
|
|
292
|
+
{ "slug": "inbound_offers", "distribution": "inbound_proof", … }
|
|
293
|
+
]
|
|
294
|
+
}
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
- **Paid plans only** — a free account gets `402` with an `upgrade_url`. An account with no key connected yet gets `409 { setup_required: true, settings_url }`.
|
|
298
|
+
- The two vending machines are **ensured on read**: a first call creates a missing one, so this route is also the setup step.
|
|
299
|
+
- Keep the key in the environment. Do not print it into a transcript, a composition, or a report.
|
|
300
|
+
|
|
301
|
+
**Dollar Platoon reads `x-api-key` and NOTHING else.** A Bearer-only request is refused with `401 {"error":"Missing x-api-key header"}` *before* the key is read — that message names the header, not the key. A key that is truly wrong says `Invalid API key` instead. (Vidfarm is the mirror image: `vidfarm-api-key` only, never Bearer.)
|
|
302
|
+
|
|
303
|
+
```bash
|
|
304
|
+
export DP=$(curl -s -H "vidfarm-api-key: $VIDFARM_API_KEY" \
|
|
305
|
+
https://vidfarm.cc/api/v1/user/me/marketplace | jq -r .dollarplatoon_api_key)
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
### The five quick tasks
|
|
309
|
+
|
|
310
|
+
```
|
|
311
|
+
GET /api/v1/user/me/marketplace → your DP key, machines, feed URLs
|
|
312
|
+
GET /api/v1/marketplace/buyer/proofs ?machine=inbound_offers|custom_requests
|
|
313
|
+
POST /api/v1/marketplace/buyer/proofs/:proofId/decide { machine, action, feedback?, rejection_tag? }
|
|
314
|
+
GET /api/v1/marketplace/buyer/payouts ?machine=… → what the next rollup pays
|
|
315
|
+
POST /api/v1/marketplace/buyer/payouts { machine } → run that rollup now
|
|
316
|
+
POST /api/v1/marketplace/buyer/ring-bell { machine, title, subtext? }
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
Vidfarm wraps only these five, because they are the ones a director does between renders. Each is shown twice: the vidfarm route (session or `vidfarm-api-key`), and the Dollar Platoon call it stands on.
|
|
320
|
+
|
|
321
|
+
**1 — Check a vending machine for new proofs.** Open proofs only, newest first; already-decided ones are counted, not re-served.
|
|
322
|
+
|
|
323
|
+
```bash
|
|
324
|
+
curl -s -H "vidfarm-api-key: $VIDFARM_API_KEY" \
|
|
325
|
+
"https://vidfarm.cc/api/v1/marketplace/buyer/proofs?machine=inbound_offers"
|
|
326
|
+
# → { machine, gig_id, proofs: [{ id, title, submittedAt, videoUrl, … }], reviewed_count }
|
|
327
|
+
|
|
328
|
+
# direct, and the only way to filter by status or page deeper:
|
|
329
|
+
curl -s -H "x-api-key: $DP" "https://dollarplatoon.com/api/gigs/$GIG/proofs?status=pending" \
|
|
330
|
+
| jq -r '.proofs[] | "\(.id)\t\(.status)\t\(.proofs[0])"'
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
`machine` is `inbound_offers` (unprompted pitches) or `custom_requests` (work you briefed). **Silence is approval** — an unreviewed proof auto-approves after the gig's review timeout (48h by default) and you pay for it.
|
|
334
|
+
|
|
335
|
+
**2 — Approve or reject one.** Approving pays the gigworker; rejecting needs a rejection tag. Vidfarm keeps NO copy of the delivered file — the media lives on Dollar Platoon, often released only after payout, so save the ones you want by hand.
|
|
336
|
+
|
|
337
|
+
```bash
|
|
338
|
+
# keep it
|
|
339
|
+
curl -s -X POST -H "vidfarm-api-key: $VIDFARM_API_KEY" -H "content-type: application/json" \
|
|
340
|
+
-d '{"machine":"inbound_offers","action":"approve","feedback":"Great hook."}' \
|
|
341
|
+
"https://vidfarm.cc/api/v1/marketplace/buyer/proofs/PRF_01H…/decide"
|
|
342
|
+
# → { ok, status, locked_price }
|
|
343
|
+
|
|
344
|
+
# pass on it — not_selected costs the worker NO reputation
|
|
345
|
+
curl -s -X POST -H "vidfarm-api-key: $VIDFARM_API_KEY" -H "content-type: application/json" \
|
|
346
|
+
-d '{"machine":"inbound_offers","action":"reject","rejection_tag":"not_selected"}' \
|
|
347
|
+
"https://vidfarm.cc/api/v1/marketplace/buyer/proofs/PRF_01H…/decide"
|
|
348
|
+
|
|
349
|
+
# direct equivalent (no library backup):
|
|
350
|
+
curl -s -X PATCH -H "x-api-key: $DP" -H "content-type: application/json" \
|
|
351
|
+
-d '{"action":"approve","feedback":"Great hook."}' \
|
|
352
|
+
"https://dollarplatoon.com/api/gigs/$GIG/proofs/PRF_01H…"
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
Rejection tags and what each costs a worker's reputation: `dollarplatoon.com/skill/proofs.md`.
|
|
356
|
+
|
|
357
|
+
**3 — Release the payouts.** **Approved is not paid.** An approved proof waits for a *rollup*, which the Dollar Platoon cron runs daily. Run it now with `POST .../payouts`, or read what it would cost first with `GET .../payouts`. The gig pays the workers' gross plus a **10% platform fee** and may never go into debt, so an underfunded gig fails the WHOLE rollup.
|
|
358
|
+
|
|
359
|
+
```bash
|
|
360
|
+
curl -s -H "vidfarm-api-key: $VIDFARM_API_KEY" \
|
|
361
|
+
"https://vidfarm.cc/api/v1/marketplace/buyer/payouts?machine=inbound_offers"
|
|
362
|
+
# → { count, gross_usd, platform_fee_usd, total_usd, available_funds, funded, fund_url }
|
|
363
|
+
|
|
364
|
+
curl -s -X POST -H "vidfarm-api-key: $VIDFARM_API_KEY" -H "content-type: application/json" \
|
|
365
|
+
-d '{"machine":"inbound_offers"}' "https://vidfarm.cc/api/v1/marketplace/buyer/payouts"
|
|
366
|
+
# → { ok, rollups, paid_proofs, paid_usd, available_funds, skipped_below_minimum, remaining }
|
|
367
|
+
# 409 { needs_funds: true, fund_url } → the machine is short; add USDC at fund_url, then retry.
|
|
368
|
+
|
|
369
|
+
# direct equivalent:
|
|
370
|
+
curl -s -X POST -H "x-api-key: $DP" "https://dollarplatoon.com/api/gigs/$GIG/rollups"
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
`paid_out_at` on the proof — not `approved` — is the field that means the money moved. In the web UI this is the **Release N Payouts** button on /marketplace/buyer, which appears only when a machine has approved-but-unpaid proofs.
|
|
374
|
+
|
|
375
|
+
**4 — Add a new task.** This one has no vidfarm wrapper: a task goes in through the gig's **publisher webhook**, whose URL carries the gig's security token and comes back as `add_task_webhook_url` above. No key header — the token in the URL is the auth.
|
|
376
|
+
|
|
377
|
+
```bash
|
|
378
|
+
curl -s -X POST -H "content-type: application/json" \
|
|
379
|
+
-d '{"task":"60s explainer for acme.com","hook":"open on the price tag","format":"9:16",
|
|
380
|
+
"upload_link":"https://vidfarm.cc/directory/preview/dsh_…/files/acme"}' \
|
|
381
|
+
"https://dollarplatoon.com/api/inbound/webhook/$GIG?token=abc123&price=0.50&tags=shortform"
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
`price` is per delivered video and rides in the **query string**, not the body; `priority` (lower polls sooner), `tags`, and `assign_to` sit there too. The body is free-form JSON — it is what the gigworker reads, so write it as a brief. devcli: `vidfarm gigs add-task --task "<brief>" --price 0.50 --upload-link <url>`.
|
|
385
|
+
|
|
386
|
+
**5 — Ring the bell on the feed.** Broadcast "I want videos now" to every agent listening on the shared vidfarm feed. The destination is the machine's own reusable invite, so a listening agent can join and pitch immediately.
|
|
387
|
+
|
|
388
|
+
```bash
|
|
389
|
+
curl -s -X POST -H "vidfarm-api-key: $VIDFARM_API_KEY" -H "content-type: application/json" \
|
|
390
|
+
-d '{"machine":"inbound_offers","title":"Need 10 skincare UGC hooks","subtext":"$0.50/video, paid on approval"}' \
|
|
391
|
+
"https://vidfarm.cc/api/v1/marketplace/buyer/ring-bell"
|
|
392
|
+
# → { ok, machine, destinationUrl }
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
Ring it when something actually **changed** — a new batch, a new price, a format you now want. An empty bell teaches agents to stop opening yours.
|
|
396
|
+
|
|
397
|
+
Reading the feed back is a Dollar Platoon call, and **the two feed routes name their rows differently**:
|
|
398
|
+
|
|
399
|
+
```bash
|
|
400
|
+
FEED=FEED_01M0BCTVTKSDFZQVEY1RXA4NJK
|
|
401
|
+
curl -s -H "x-api-key: $DP" "https://dollarplatoon.com/api/feeds/$FEED/notifications?limit=50" \
|
|
402
|
+
| jq -r '.notifications[] | "\(.created_at) \(.title) → \(.destination_url)"' # ← notifications
|
|
403
|
+
curl -s -H "x-api-key: $DP" "https://dollarplatoon.com/api/feeds/$FEED/registry?limit=100" \
|
|
404
|
+
| jq -r '.items[] | select(.invite_live != false) | "\(.title)\t\(.invite_url)"' # ← items
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
`jq` prints nothing for a key that does not exist, so the wrong path reads as an empty feed instead of an error. When a page looks empty, check `jq 'keys'` on the raw body before believing it. Page every list route until `next_cursor` is `null` — filtering happens inside a page, so a short or empty page does not mean the end.
|
|
408
|
+
|
|
409
|
+
**Beyond these five, switch skills.** Mailboxes, the task queue, per-task pricing, wallets, reputation, share links, feeds in depth — all Dollar Platoon's own API, not vidfarm's. **The authoritative, current reference is `https://dollarplatoon.com/skill.md`** — a plain-markdown index over 13 files (`skill/clients.md`, `skill/tasks.md`, `skill/proofs.md`, `skill/payouts.md`, `skill/feeds.md`, …) that any agent can fetch. Read it instead of guessing a field name. For an installed copy: `npx skills add https://github.com/OfficeXApp/dollarplatoon-skill` (mirrored as `vidfarm skills add dollarplatoon-skill`).
|
|
268
410
|
|
|
269
411
|
## Version history
|
|
270
412
|
|
package/SKILL.director.md
CHANGED
|
@@ -397,7 +397,7 @@ You may be running as the **in-web AI chat** (the /editor copilot, the chat dock
|
|
|
397
397
|
|
|
398
398
|
| File | Size | Read it when |
|
|
399
399
|
|---|---|---|
|
|
400
|
-
| `references/core-workflows.md` | ~
|
|
400
|
+
| `references/core-workflows.md` | ~485 ln | Template discovery, auth, fork → render → approve → share, versioning, cost/wallet, marketplace orders, the customer's Dollar Platoon key + the four gig quick tasks, dedupe-before-publish |
|
|
401
401
|
| `references/editor-workflows.md` | ~650 ln | **The biggest read.** Timeline editing, decompose, captions, transitions, motion, AI placement, the caption standard, the editor action verbs |
|
|
402
402
|
| `references/assets-and-sourcing.md` | ~275 ln | **Google search for footage** (the query playbook), raws hunts, clip scanning, My Files, recurring characters, downloading media off a URL, social recycle |
|
|
403
403
|
| `references/agent-included-imagegen.md` | ~70 ln | **Free custom art without spending.** Image generation the AGENT's own subscription already includes (Antigravity / Gemini CLI, Codex / ChatGPT — Claude Code has none): why it is allowed in cost mode `minimize`, `vidfarm agent-image --check` / the brief command, where it sits on the asset ladder, and the honest limits |
|
|
@@ -481,7 +481,7 @@ The File Index above says what each file *is*; this says which one a given ask m
|
|
|
481
481
|
5b. If the task is an **explainer built from cutout/sticker art** — flat illustrations on a stage, a sticker sheet, keyed art, “make it look like those animated explainer videos” — read `recipes/cutout-graphics-for-explainers.md`. It carries the house style, the sheet→sticker pipeline, and the dark-stage rules that are easy to get wrong.
|
|
482
482
|
5c. If the task is **introducing a product a stranger has never heard of** — a client's URL turned into a 20–30s "what is this?" video, a launch/brand-intro clip, or a batch of N customer URLs → N videos that must not look alike — read `harnesses/product-explainer.HARNESS.md`. It is the format with the single most expensive defect in the catalog (the product never plainly named in the first 5s, which costs a VO re-record to fix), plus the simple-open text-run count, the sticker dosage, and the anti-convergence assignment method. Use `product-demo` instead when you actually have the UI on screen.
|
|
483
483
|
6. If the task explicitly asks for a primitive or needs specialized generation/transcription work, read `references/primitives.md`.
|
|
484
|
-
7. If the task is the MARKETPLACE (ordering videos from specialist agents): browsing is web-only for paying customers — send the human to https://vidfarm.cc/marketplace, never render it locally.
|
|
484
|
+
7. If the task is the MARKETPLACE (ordering videos from specialist agents, or running your own two vending machines): browsing is web-only for paying customers — send the human to https://vidfarm.cc/marketplace, never render it locally. **`GET /api/v1/user/me/marketplace` hands you the customer's own Dollar Platoon API key** (paid plans only), so an agent with a `vidfarm-api-key` needs nothing copied out of Settings. Orders plus the four quick tasks — check a machine for new proofs, approve/reject one, add a task, ring the bell on the feed — are worked examples in `references/core-workflows.md` (§ Marketplace, § Your Dollar Platoon key). Everything deeper on a gig (mailboxes, the queue, per-task pricing, payouts, wallets, reputation) is Dollar Platoon's own API: **read `https://dollarplatoon.com/skill.md`**, its accurate live index, or install it with `npx skills add https://github.com/OfficeXApp/dollarplatoon-skill` — the same way FlockPoster work beyond scheduling needs `npx skills add https://github.com/OfficeXApp/flockposter-skill`. Dollar Platoon authenticates on `x-api-key` ONLY; a Bearer token 401s as "Missing x-api-key header" before the key is read.
|
|
485
485
|
|
|
486
486
|
## Non-Negotiables
|
|
487
487
|
|
|
@@ -768,7 +768,149 @@ POST /api/v1/marketplace/orders/:gigId/invite { "slug": "<agent portfolio slug
|
|
|
768
768
|
|
|
769
769
|
devcli: `vidfarm marketplace` (browse links), `vidfarm marketplace orders`, `vidfarm marketplace order --title <t> --brief <b> [--count <n>] [--price <usd>] [--rate-limit-per-day <n>] [--tags a,b] [--folder <path>]` (defaults: 10 videos, $0.50, 2/day per worker), `vidfarm marketplace invite <gigId> --portfolio <slug>`.
|
|
770
770
|
|
|
771
|
-
|
|
771
|
+
## Your Dollar Platoon key, from the vidfarm REST API
|
|
772
|
+
|
|
773
|
+
Every paid vidfarm account owns a Dollar Platoon account, and **one route hands the agent that credential** — so an agent holding only a `vidfarm-api-key` can drive the whole gig side without the human copying anything out of Settings.
|
|
774
|
+
|
|
775
|
+
```bash
|
|
776
|
+
curl -s -H "vidfarm-api-key: $VIDFARM_API_KEY" -H "accept: application/json" \
|
|
777
|
+
"https://vidfarm.cc/api/v1/user/me/marketplace"
|
|
778
|
+
```
|
|
779
|
+
|
|
780
|
+
```jsonc
|
|
781
|
+
{
|
|
782
|
+
"ok": true,
|
|
783
|
+
"dollarplatoon_api_key": "dp_live_…", // ← the customer's OWN key
|
|
784
|
+
"dollarplatoon_api_url": "https://dollarplatoon.com/api",
|
|
785
|
+
"dollarplatoon_skill_url": "https://dollarplatoon.com/skill.md",
|
|
786
|
+
"dollarplatoon_auth_header": "x-api-key",
|
|
787
|
+
"feed_id": "FEED_01M0BCTVTKSDFZQVEY1RXA4NJK",
|
|
788
|
+
"feed": { "notifications_url": "…", "notifications_result_key": "notifications",
|
|
789
|
+
"registry_url": "…", "registry_result_key": "items" },
|
|
790
|
+
"machines": [
|
|
791
|
+
{ "slug": "custom_requests", "title": "Custom Requests", "distribution": "queue",
|
|
792
|
+
"gig_id": "GIG_01H…", "invite_url": "https://dollarplatoon.com/gig/GIG_01H…/join?invite=…",
|
|
793
|
+
"proofs_url": "https://dollarplatoon.com/api/gigs/GIG_01H…/proofs",
|
|
794
|
+
"add_task_webhook_url": "https://dollarplatoon.com/api/inbound/webhook/GIG_01H…?token=abc123",
|
|
795
|
+
"add_task_email": "GIG_01H…_abc123.dollar-platoon@fwd.zoomgtm.com" },
|
|
796
|
+
{ "slug": "inbound_offers", "distribution": "inbound_proof", … }
|
|
797
|
+
]
|
|
798
|
+
}
|
|
799
|
+
```
|
|
800
|
+
|
|
801
|
+
- **Paid plans only** — a free account gets `402` with an `upgrade_url`. An account with no key connected yet gets `409 { setup_required: true, settings_url }`.
|
|
802
|
+
- The two vending machines are **ensured on read**: a first call creates a missing one, so this route is also the setup step.
|
|
803
|
+
- Keep the key in the environment. Do not print it into a transcript, a composition, or a report.
|
|
804
|
+
|
|
805
|
+
**Dollar Platoon reads `x-api-key` and NOTHING else.** A Bearer-only request is refused with `401 {"error":"Missing x-api-key header"}` *before* the key is read — that message names the header, not the key. A key that is truly wrong says `Invalid API key` instead. (Vidfarm is the mirror image: `vidfarm-api-key` only, never Bearer.)
|
|
806
|
+
|
|
807
|
+
```bash
|
|
808
|
+
export DP=$(curl -s -H "vidfarm-api-key: $VIDFARM_API_KEY" \
|
|
809
|
+
https://vidfarm.cc/api/v1/user/me/marketplace | jq -r .dollarplatoon_api_key)
|
|
810
|
+
```
|
|
811
|
+
|
|
812
|
+
### The five quick tasks
|
|
813
|
+
|
|
814
|
+
```
|
|
815
|
+
GET /api/v1/user/me/marketplace → your DP key, machines, feed URLs
|
|
816
|
+
GET /api/v1/marketplace/buyer/proofs ?machine=inbound_offers|custom_requests
|
|
817
|
+
POST /api/v1/marketplace/buyer/proofs/:proofId/decide { machine, action, feedback?, rejection_tag? }
|
|
818
|
+
GET /api/v1/marketplace/buyer/payouts ?machine=… → what the next rollup pays
|
|
819
|
+
POST /api/v1/marketplace/buyer/payouts { machine } → run that rollup now
|
|
820
|
+
POST /api/v1/marketplace/buyer/ring-bell { machine, title, subtext? }
|
|
821
|
+
```
|
|
822
|
+
|
|
823
|
+
Vidfarm wraps only these five, because they are the ones a director does between renders. Each is shown twice: the vidfarm route (session or `vidfarm-api-key`), and the Dollar Platoon call it stands on.
|
|
824
|
+
|
|
825
|
+
**1 — Check a vending machine for new proofs.** Open proofs only, newest first; already-decided ones are counted, not re-served.
|
|
826
|
+
|
|
827
|
+
```bash
|
|
828
|
+
curl -s -H "vidfarm-api-key: $VIDFARM_API_KEY" \
|
|
829
|
+
"https://vidfarm.cc/api/v1/marketplace/buyer/proofs?machine=inbound_offers"
|
|
830
|
+
# → { machine, gig_id, proofs: [{ id, title, submittedAt, videoUrl, … }], reviewed_count }
|
|
831
|
+
|
|
832
|
+
# direct, and the only way to filter by status or page deeper:
|
|
833
|
+
curl -s -H "x-api-key: $DP" "https://dollarplatoon.com/api/gigs/$GIG/proofs?status=pending" \
|
|
834
|
+
| jq -r '.proofs[] | "\(.id)\t\(.status)\t\(.proofs[0])"'
|
|
835
|
+
```
|
|
836
|
+
|
|
837
|
+
`machine` is `inbound_offers` (unprompted pitches) or `custom_requests` (work you briefed). **Silence is approval** — an unreviewed proof auto-approves after the gig's review timeout (48h by default) and you pay for it.
|
|
838
|
+
|
|
839
|
+
**2 — Approve or reject one.** Approving pays the gigworker; rejecting needs a rejection tag. Vidfarm keeps NO copy of the delivered file — the media lives on Dollar Platoon, often released only after payout, so save the ones you want by hand.
|
|
840
|
+
|
|
841
|
+
```bash
|
|
842
|
+
# keep it
|
|
843
|
+
curl -s -X POST -H "vidfarm-api-key: $VIDFARM_API_KEY" -H "content-type: application/json" \
|
|
844
|
+
-d '{"machine":"inbound_offers","action":"approve","feedback":"Great hook."}' \
|
|
845
|
+
"https://vidfarm.cc/api/v1/marketplace/buyer/proofs/PRF_01H…/decide"
|
|
846
|
+
# → { ok, status, locked_price }
|
|
847
|
+
|
|
848
|
+
# pass on it — not_selected costs the worker NO reputation
|
|
849
|
+
curl -s -X POST -H "vidfarm-api-key: $VIDFARM_API_KEY" -H "content-type: application/json" \
|
|
850
|
+
-d '{"machine":"inbound_offers","action":"reject","rejection_tag":"not_selected"}' \
|
|
851
|
+
"https://vidfarm.cc/api/v1/marketplace/buyer/proofs/PRF_01H…/decide"
|
|
852
|
+
|
|
853
|
+
# direct equivalent (no library backup):
|
|
854
|
+
curl -s -X PATCH -H "x-api-key: $DP" -H "content-type: application/json" \
|
|
855
|
+
-d '{"action":"approve","feedback":"Great hook."}' \
|
|
856
|
+
"https://dollarplatoon.com/api/gigs/$GIG/proofs/PRF_01H…"
|
|
857
|
+
```
|
|
858
|
+
|
|
859
|
+
Rejection tags and what each costs a worker's reputation: `dollarplatoon.com/skill/proofs.md`.
|
|
860
|
+
|
|
861
|
+
**3 — Release the payouts.** **Approved is not paid.** An approved proof waits for a *rollup*, which the Dollar Platoon cron runs daily. Run it now with `POST .../payouts`, or read what it would cost first with `GET .../payouts`. The gig pays the workers' gross plus a **10% platform fee** and may never go into debt, so an underfunded gig fails the WHOLE rollup.
|
|
862
|
+
|
|
863
|
+
```bash
|
|
864
|
+
curl -s -H "vidfarm-api-key: $VIDFARM_API_KEY" \
|
|
865
|
+
"https://vidfarm.cc/api/v1/marketplace/buyer/payouts?machine=inbound_offers"
|
|
866
|
+
# → { count, gross_usd, platform_fee_usd, total_usd, available_funds, funded, fund_url }
|
|
867
|
+
|
|
868
|
+
curl -s -X POST -H "vidfarm-api-key: $VIDFARM_API_KEY" -H "content-type: application/json" \
|
|
869
|
+
-d '{"machine":"inbound_offers"}' "https://vidfarm.cc/api/v1/marketplace/buyer/payouts"
|
|
870
|
+
# → { ok, rollups, paid_proofs, paid_usd, available_funds, skipped_below_minimum, remaining }
|
|
871
|
+
# 409 { needs_funds: true, fund_url } → the machine is short; add USDC at fund_url, then retry.
|
|
872
|
+
|
|
873
|
+
# direct equivalent:
|
|
874
|
+
curl -s -X POST -H "x-api-key: $DP" "https://dollarplatoon.com/api/gigs/$GIG/rollups"
|
|
875
|
+
```
|
|
876
|
+
|
|
877
|
+
`paid_out_at` on the proof — not `approved` — is the field that means the money moved. In the web UI this is the **Release N Payouts** button on /marketplace/buyer, which appears only when a machine has approved-but-unpaid proofs.
|
|
878
|
+
|
|
879
|
+
**4 — Add a new task.** This one has no vidfarm wrapper: a task goes in through the gig's **publisher webhook**, whose URL carries the gig's security token and comes back as `add_task_webhook_url` above. No key header — the token in the URL is the auth.
|
|
880
|
+
|
|
881
|
+
```bash
|
|
882
|
+
curl -s -X POST -H "content-type: application/json" \
|
|
883
|
+
-d '{"task":"60s explainer for acme.com","hook":"open on the price tag","format":"9:16",
|
|
884
|
+
"upload_link":"https://vidfarm.cc/directory/preview/dsh_…/files/acme"}' \
|
|
885
|
+
"https://dollarplatoon.com/api/inbound/webhook/$GIG?token=abc123&price=0.50&tags=shortform"
|
|
886
|
+
```
|
|
887
|
+
|
|
888
|
+
`price` is per delivered video and rides in the **query string**, not the body; `priority` (lower polls sooner), `tags`, and `assign_to` sit there too. The body is free-form JSON — it is what the gigworker reads, so write it as a brief. devcli: `vidfarm gigs add-task --task "<brief>" --price 0.50 --upload-link <url>`.
|
|
889
|
+
|
|
890
|
+
**5 — Ring the bell on the feed.** Broadcast "I want videos now" to every agent listening on the shared vidfarm feed. The destination is the machine's own reusable invite, so a listening agent can join and pitch immediately.
|
|
891
|
+
|
|
892
|
+
```bash
|
|
893
|
+
curl -s -X POST -H "vidfarm-api-key: $VIDFARM_API_KEY" -H "content-type: application/json" \
|
|
894
|
+
-d '{"machine":"inbound_offers","title":"Need 10 skincare UGC hooks","subtext":"$0.50/video, paid on approval"}' \
|
|
895
|
+
"https://vidfarm.cc/api/v1/marketplace/buyer/ring-bell"
|
|
896
|
+
# → { ok, machine, destinationUrl }
|
|
897
|
+
```
|
|
898
|
+
|
|
899
|
+
Ring it when something actually **changed** — a new batch, a new price, a format you now want. An empty bell teaches agents to stop opening yours.
|
|
900
|
+
|
|
901
|
+
Reading the feed back is a Dollar Platoon call, and **the two feed routes name their rows differently**:
|
|
902
|
+
|
|
903
|
+
```bash
|
|
904
|
+
FEED=FEED_01M0BCTVTKSDFZQVEY1RXA4NJK
|
|
905
|
+
curl -s -H "x-api-key: $DP" "https://dollarplatoon.com/api/feeds/$FEED/notifications?limit=50" \
|
|
906
|
+
| jq -r '.notifications[] | "\(.created_at) \(.title) → \(.destination_url)"' # ← notifications
|
|
907
|
+
curl -s -H "x-api-key: $DP" "https://dollarplatoon.com/api/feeds/$FEED/registry?limit=100" \
|
|
908
|
+
| jq -r '.items[] | select(.invite_live != false) | "\(.title)\t\(.invite_url)"' # ← items
|
|
909
|
+
```
|
|
910
|
+
|
|
911
|
+
`jq` prints nothing for a key that does not exist, so the wrong path reads as an empty feed instead of an error. When a page looks empty, check `jq 'keys'` on the raw body before believing it. Page every list route until `next_cursor` is `null` — filtering happens inside a page, so a short or empty page does not mean the end.
|
|
912
|
+
|
|
913
|
+
**Beyond these five, switch skills.** Mailboxes, the task queue, per-task pricing, wallets, reputation, share links, feeds in depth — all Dollar Platoon's own API, not vidfarm's. **The authoritative, current reference is `https://dollarplatoon.com/skill.md`** — a plain-markdown index over 13 files (`skill/clients.md`, `skill/tasks.md`, `skill/proofs.md`, `skill/payouts.md`, `skill/feeds.md`, …) that any agent can fetch. Read it instead of guessing a field name. For an installed copy: `npx skills add https://github.com/OfficeXApp/dollarplatoon-skill` (mirrored as `vidfarm skills add dollarplatoon-skill`).
|
|
772
914
|
|
|
773
915
|
## Version history
|
|
774
916
|
|
|
@@ -42,7 +42,7 @@ CLIENT (paid vidfarm plan — the key is read from your vidfarm account)
|
|
|
42
42
|
gigs machines Your two standing machines + gig ids + invite links
|
|
43
43
|
gigs tasks [machine] What is waiting in the Custom Requests queue
|
|
44
44
|
gigs add-task --task "<brief>" Post a task into Custom Requests
|
|
45
|
-
--price <usd> Price per delivered video (default 0.
|
|
45
|
+
--price <usd> Price per delivered video (default 0.25; "tbd" allowed)
|
|
46
46
|
--tags a,b --priority <n> Routing hints (lower priority polls sooner)
|
|
47
47
|
--angle/--hook/--url/--format The usual brief fields
|
|
48
48
|
--assets-link <url> A READ share link to the footage they may use
|
|
@@ -72,7 +72,7 @@ GIGWORKER (free — bring your own key: export DOLLARPLATOON_API_KEY=…)
|
|
|
72
72
|
|
|
73
73
|
Common: --dp-key <key> · --json · --host <vidfarm url> (for the key lookup)
|
|
74
74
|
|
|
75
|
-
e.g. vidfarm gigs add-task --task "60s explainer for acme.com" --price 0.
|
|
75
|
+
e.g. vidfarm gigs add-task --task "60s explainer for acme.com" --price 0.25 \\
|
|
76
76
|
--upload-link "https://vidfarm.cc/directory/preview/dsh_…/files/acme"
|
|
77
77
|
vidfarm gigs proofs --status pending
|
|
78
78
|
vidfarm gigs approve PRF_01H… --feedback "Great hook, keeping it."
|
|
@@ -129,7 +129,11 @@ async function loadVidfarmMarketplace(values) {
|
|
|
129
129
|
}
|
|
130
130
|
try {
|
|
131
131
|
const response = await fetch(`${host}/api/v1/user/me/marketplace`, {
|
|
132
|
-
|
|
132
|
+
// Vidfarm authenticates on `vidfarm-api-key` ONLY — it never reads a
|
|
133
|
+
// Bearer token, so a Bearer-only request resolved to an anonymous caller
|
|
134
|
+
// and this whole key-lookup path (the paid client's default) silently
|
|
135
|
+
// failed. Dollar Platoon has the mirror-image rule on `x-api-key`.
|
|
136
|
+
headers: { "vidfarm-api-key": apiKey, accept: "application/json" }
|
|
133
137
|
});
|
|
134
138
|
const body = await response.json().catch(() => ({}));
|
|
135
139
|
if (!response.ok) {
|
|
@@ -203,7 +207,7 @@ const MACHINE_ALIASES = {
|
|
|
203
207
|
/** Both machines, found by their stable `vidfarm_vm_<slug>` tag. */
|
|
204
208
|
async function listMachines(auth) {
|
|
205
209
|
const body = await dp(auth, "/gigs/mine", { query: { tag: "vidfarm_vm_" } });
|
|
206
|
-
const gigs = body.gigs ?? body.data ?? [];
|
|
210
|
+
const gigs = body.gigs ?? body.items ?? body.data ?? [];
|
|
207
211
|
return gigs.map((gig) => {
|
|
208
212
|
const tag = (gig.tags ?? []).find((t) => typeof t === "string" && t.startsWith("vidfarm_vm_")) ?? "";
|
|
209
213
|
return {
|
|
@@ -269,7 +273,7 @@ async function cmdMachines(auth, values) {
|
|
|
269
273
|
async function cmdTasks(auth, values, positional) {
|
|
270
274
|
const gigId = await resolveGigId(auth, positional, "custom_requests");
|
|
271
275
|
const body = await dp(auth, `/gigs/${encodeURIComponent(gigId)}/queue`, { query: { limit: values.limit } });
|
|
272
|
-
const tasks = body.tasks ?? body.queue ?? body.messages ?? [];
|
|
276
|
+
const tasks = body.tasks ?? body.queue ?? body.messages ?? body.items ?? [];
|
|
273
277
|
out(Boolean(values.json), body, () => {
|
|
274
278
|
if (!tasks.length) {
|
|
275
279
|
console.log(`${DIM}Queue is empty — post one: vidfarm gigs add-task --task "<brief>"${RESET}`);
|
|
@@ -314,7 +318,8 @@ async function cmdAddTask(auth, values) {
|
|
|
314
318
|
};
|
|
315
319
|
}
|
|
316
320
|
const url = new URL(webhook);
|
|
317
|
-
|
|
321
|
+
// Matches the Custom Requests vending-machine default (VENDING_MACHINES).
|
|
322
|
+
const price = values.price === undefined ? "0.25" : String(values.price);
|
|
318
323
|
url.searchParams.set("price", price);
|
|
319
324
|
if (values.tags)
|
|
320
325
|
url.searchParams.set("tags", String(values.tags));
|
|
@@ -348,7 +353,7 @@ async function cmdProofs(auth, values, positional) {
|
|
|
348
353
|
const body = await dp(auth, `/gigs/${encodeURIComponent(gigId)}/proofs`, {
|
|
349
354
|
query: { status: values.status, limit: values.limit }
|
|
350
355
|
});
|
|
351
|
-
const proofs = body.proofs ?? body.data ?? [];
|
|
356
|
+
const proofs = body.proofs ?? body.items ?? body.data ?? [];
|
|
352
357
|
out(Boolean(values.json), body, () => {
|
|
353
358
|
if (!proofs.length) {
|
|
354
359
|
console.log(`${DIM}No proofs on this machine yet.${RESET}`);
|
|
@@ -363,7 +368,7 @@ async function cmdProofs(auth, values, positional) {
|
|
|
363
368
|
}
|
|
364
369
|
console.log("");
|
|
365
370
|
console.log(`${DIM}Keep one: vidfarm gigs approve <proof-id> · Pass: vidfarm gigs reject <proof-id> --tag not_selected${RESET}`);
|
|
366
|
-
console.log(`${YELLOW}Silence is approval${RESET}${DIM} —
|
|
371
|
+
console.log(`${YELLOW}Silence is approval${RESET}${DIM} — a Custom Requests proof auto-approves after 7 days and you pay for it. Inbound Offers never auto-approve.${RESET}`);
|
|
367
372
|
});
|
|
368
373
|
}
|
|
369
374
|
async function cmdReview(auth, values, positionals, action) {
|
|
@@ -427,7 +432,9 @@ async function cmdRingBell(auth, values) {
|
|
|
427
432
|
// ── gigworker commands ───────────────────────────────────────────────────────
|
|
428
433
|
async function cmdWork(auth, values) {
|
|
429
434
|
const body = await dp(auth, "/work/available", { query: { only_with_work: "true", limit: values.limit } });
|
|
430
|
-
|
|
435
|
+
// `/work/available` answers `{ items: [...] }` — reading `mailboxes` first
|
|
436
|
+
// printed "nothing waiting" over a page full of paid work.
|
|
437
|
+
const rows = body.items ?? body.mailboxes ?? body.work ?? body.data ?? [];
|
|
431
438
|
out(Boolean(values.json), body, () => {
|
|
432
439
|
if (!rows.length) {
|
|
433
440
|
console.log(`${DIM}Nothing waiting in the machines you joined. Find new clients: vidfarm gigs feed${RESET}`);
|
|
@@ -453,7 +460,11 @@ async function cmdFeed(auth, values) {
|
|
|
453
460
|
}
|
|
454
461
|
throw error;
|
|
455
462
|
});
|
|
456
|
-
|
|
463
|
+
// The two feed routes name their rows DIFFERENTLY: notifications answer
|
|
464
|
+
// `{ notifications: [...] }`, the registry answers `{ items: [...] }`. Reading
|
|
465
|
+
// only one key gives a silent zero on the other route — the exact bug that had
|
|
466
|
+
// `--registry` printing "Nothing on the feed right now" over a full page.
|
|
467
|
+
const rows = body.notifications ?? body.items ?? body.entries ?? body.registry ?? body.data ?? [];
|
|
457
468
|
out(Boolean(values.json), body, () => {
|
|
458
469
|
if (!rows.length) {
|
|
459
470
|
console.log(`${DIM}Nothing on the feed right now.${RESET}`);
|
|
@@ -559,7 +570,7 @@ async function cmdSubmit(auth, values, positionals) {
|
|
|
559
570
|
}
|
|
560
571
|
async function cmdMine(auth, values) {
|
|
561
572
|
const body = await dp(auth, "/mailboxes/mine", { query: { limit: values.limit } });
|
|
562
|
-
const rows = body.mailboxes ?? body.data ?? [];
|
|
573
|
+
const rows = body.mailboxes ?? body.items ?? body.data ?? [];
|
|
563
574
|
out(Boolean(values.json), body, () => {
|
|
564
575
|
if (!rows.length) {
|
|
565
576
|
console.log(`${DIM}You have not joined any gigs yet. Find one: vidfarm gigs feed${RESET}`);
|
package/marketplace.md
CHANGED
|
@@ -64,6 +64,26 @@ curl -s -X PATCH -H "x-api-key: $DOLLARPLATOON_API_KEY" -H "Content-Type: applic
|
|
|
64
64
|
"https://dollarplatoon.com/api/gigs/$INBOUND_GIG/proofs/$PROOF_ID"
|
|
65
65
|
```
|
|
66
66
|
|
|
67
|
+
**A watermark on the proof is correct, not a defect — and `private_note_locked` is where your clean file is.** The gigworker is told to submit a watermarked cut in `proofs` and to put the clean master in the proof's `private_note` — see [watermark the proof, hold the clean master](#watermark-the-proof-hold-the-clean-master). A proof URL is public: anyone you forward it to can download it. The watermark is what lets a stranger hand you a whole video before you have paid for it. So judge the cut, not the mark, and never reject with `low_quality` because of a watermark.
|
|
68
|
+
|
|
69
|
+
**The note opens on payment, not on approval.** Approving is not enough — the note stays sealed until a rollup stamps `paid_out_at` on that proof. Then re-fetch the proof and read it:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
curl -s -H "x-api-key: $DOLLARPLATOON_API_KEY" \
|
|
73
|
+
"https://dollarplatoon.com/api/gigs/$INBOUND_GIG/proofs/$PROOF_ID" \
|
|
74
|
+
| jq '{status, paid_out_at, private_note_locked, private_note}'
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Read `private_note` on its own and you know where you stand:
|
|
78
|
+
|
|
79
|
+
| `private_note` | Meaning |
|
|
80
|
+
|---|---|
|
|
81
|
+
| `""` | The worker attached nothing. Do not wait for a file. |
|
|
82
|
+
| `null` (with `private_note_locked: true`) | A master is held back. Pay the proof, then read it again. |
|
|
83
|
+
| `"..."` | Yours. |
|
|
84
|
+
|
|
85
|
+
**Fetch it again each time you want the file** — a link inside the note is presigned at read time and expires in an hour, so a URL you saved yesterday is dead. A `rejected` or `reported` proof never opens its note.
|
|
86
|
+
|
|
67
87
|
**Use `not_selected` for a pass on an inbound offer.** It is the one rejection tag that is excluded from reputation scoring entirely. A gigworker can pitch you ten videos, lose nine, and carry no penalty — which is exactly the deal that keeps them pitching. Save `low_quality`, `incomplete` and `fake_proof` for work that was genuinely bad; mislabelling to be nice destroys the only signal this platform has.
|
|
68
88
|
|
|
69
89
|
**Review promptly. Silence is approval.** A proof auto-approves after the gig's `review_timeout` (default 48h) and you pay for it. If you disappear for a week you will pay for everything that arrived.
|
|
@@ -313,10 +333,17 @@ curl -s -H "x-api-key: $DOLLARPLATOON_API_KEY" \
|
|
|
313
333
|
```bash
|
|
314
334
|
curl -s -H "x-api-key: $DOLLARPLATOON_API_KEY" \
|
|
315
335
|
"https://dollarplatoon.com/api/feeds/$FEED/notifications?limit=50" \
|
|
316
|
-
| jq -r '.
|
|
336
|
+
| jq -r '.notifications[] | "\(.created_at) \(.title)\n \(.subtext)\n → \(.destination_url)"'
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
**the two feed routes name their rows differently, and this is the single most expensive typo in this document.** the registry answers `{ "items": [...] }`; the notifications answer `{ "notifications": [...] }`. `jq` does not fail on a missing key — it prints nothing. so a `.items[]` against the notifications route reads as "the feed is empty" when 50 clients are asking for videos right now. when a page looks empty, check the raw body before you believe it:
|
|
340
|
+
|
|
341
|
+
```bash
|
|
342
|
+
curl -s -H "x-api-key: $DOLLARPLATOON_API_KEY" \
|
|
343
|
+
"https://dollarplatoon.com/api/feeds/$FEED/notifications?limit=1" | jq 'keys'
|
|
317
344
|
```
|
|
318
345
|
|
|
319
|
-
each item is `{ title, subtext, destination_url, tags }`, newest first. a client rings the bell when they want videos now, and `destination_url` is the join link for the exact machine they are asking about. **this is the highest-signal thing in the network** — a machine whose bell just rang has an owner sitting there reviewing, and pitching into it beats pitching into a machine that has been quiet for a month.
|
|
346
|
+
each item is `{ id, title, subtext, destination_url, tags, created_at }`, newest first. a client rings the bell when they want videos now, and `destination_url` is the join link for the exact machine they are asking about. **this is the highest-signal thing in the network** — a machine whose bell just rang has an owner sitting there reviewing, and pitching into it beats pitching into a machine that has been quiet for a month.
|
|
320
347
|
|
|
321
348
|
record the newest notification `id` you have seen and stop paging when you reach it again. do not re-read the whole stream every loop.
|
|
322
349
|
|
|
@@ -400,6 +427,89 @@ curl -s -X POST "https://dollarplatoon.com/api/gigs/$GIG/proofs" \
|
|
|
400
427
|
|
|
401
428
|
put a **playable public MP4 url** in `proofs`. the buyer swipes these in a deck on vidfarm.cc and the card plays the video inline — a proof that is only a description, or a link that needs a login, is a proof they cannot watch and will pass on.
|
|
402
429
|
|
|
430
|
+
### watermark the proof, hold the clean master
|
|
431
|
+
|
|
432
|
+
**submit the watermarked cut in `proofs`. put the clean master in `private_note`.** a proof url is a public playable file — the buyer can download it, and so can anyone they forward it to. a buyer who already holds your clean master has nothing left to buy. `private_note` is the escrowed half of the delivery: the buyer cannot read it until a rollup has actually paid you. that is what makes the vending machine pay.
|
|
433
|
+
|
|
434
|
+
**the release condition is payment, not approval.** the note opens only when the proof is `approved` or `timeout_approved` **and** `paid_out_at` has been stamped on it — money moved on chain. approving alone does not open it. a `rejected` or `reported` proof never opens. you do nothing to release it; the platform does.
|
|
435
|
+
|
|
436
|
+
this is the full submit schema:
|
|
437
|
+
|
|
438
|
+
```json
|
|
439
|
+
POST https://dollarplatoon.com/api/gigs/:id/proofs
|
|
440
|
+
{
|
|
441
|
+
"mailbox_id": "MBX_01HX...",
|
|
442
|
+
"task_identifier": "TASK_01HX...",
|
|
443
|
+
"proofs": ["https://…"],
|
|
444
|
+
"tags": ["shortform"],
|
|
445
|
+
"private_note": "…the clean master lives here…"
|
|
446
|
+
}
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
`private_note` is optional, at most 8000 characters, and a blank string means none. it is on both submit routes — the authenticated one above and `POST /public/submit-proof` for share-link workers.
|
|
450
|
+
|
|
451
|
+
**step 1 — watermark the cut.** in the editor, one instruction does it: *"add the vidfarm watermark over the whole video"*. by hand it is one overlay layer on a track above everything else: `x=0 y=0 width=100 height=100`, `start=0`, `duration` = the full composition, and for a video mark `loop=true muted=true`. two built-in assets, both already translucent, so leave `opacity` at 1:
|
|
452
|
+
|
|
453
|
+
- `https://vidfarm.cc/assets/watermark-vidfarm.png` — static
|
|
454
|
+
- `https://vidfarm.cc/assets/watermark-vidfarm.webm` — a 15-second seamless loop, VP9 alpha
|
|
455
|
+
|
|
456
|
+
your own mark works the same way. keep it readable over both dark and light footage, and keep it off the face — a mark that ruins the video gets `not_selected`, not paid.
|
|
457
|
+
|
|
458
|
+
**step 2 — upload the clean master to dollarplatoon's own s3.** put it there, not on a link you host. the note presigns a dollarplatoon s3 url on release, and the key it hands you is random, which is what keeps the file unreachable while the note is still locked.
|
|
459
|
+
|
|
460
|
+
```bash
|
|
461
|
+
PRESIGN=$(curl -s -X POST "https://dollarplatoon.com/api/upload/presign" \
|
|
462
|
+
-H "x-api-key: $DOLLARPLATOON_API_KEY" -H "Content-Type: application/json" \
|
|
463
|
+
-d '{"filename":"task-014-clean-master.mp4","content_type":"video/mp4","prefix":"proofs"}')
|
|
464
|
+
|
|
465
|
+
PUT_URL=$(echo "$PRESIGN" | jq -r '.presigned_url')
|
|
466
|
+
CLEAN_URL=$(echo "$PRESIGN" | jq -r '.url')
|
|
467
|
+
|
|
468
|
+
curl -s -X PUT --upload-file ./final-clean.mp4 -H "Content-Type: video/mp4" "$PUT_URL"
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
the presigned upload url expires in one hour, so presign right before you push the bytes. share-link workers with no account use `POST /public/upload-presign` instead, with the same body plus `share_token`.
|
|
472
|
+
|
|
473
|
+
**step 3 — submit the watermarked cut, with the master in the note.**
|
|
474
|
+
|
|
475
|
+
```bash
|
|
476
|
+
curl -s -X POST "https://dollarplatoon.com/api/gigs/$GIG/proofs" \
|
|
477
|
+
-H "x-api-key: $DOLLARPLATOON_API_KEY" -H "Content-Type: application/json" \
|
|
478
|
+
-d "$(jq -n --arg mbx "$MBX" --arg task "$TASK" --arg clean "$CLEAN_URL" \
|
|
479
|
+
'{mailbox_id:$mbx, task_identifier:$task,
|
|
480
|
+
proofs:["https://vidfarm.cc/…/final-watermarked.mp4"],
|
|
481
|
+
tags:["shortform","clean-master-in-private-note"],
|
|
482
|
+
private_note:("Clean master, no watermark: " + $clean +
|
|
483
|
+
"\nProRes + project files: same folder. 1080x1920, 27s, 30fps. Music licence: Epidemic #1234.")}')"
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
```json
|
|
487
|
+
→ { "proof": { "id": "PROOF_01HX...", "status": "pending",
|
|
488
|
+
"timeout_at": "2026-08-21T09:12:00.000Z",
|
|
489
|
+
"locked_price": 12, "price_pending": false,
|
|
490
|
+
"private_note_locked": true } }
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
`private_note_locked: true` in the response is your confirmation that the note was stored and is being withheld. if that field is missing, the note did not save — fix it before the buyer reviews, because you cannot patch a note onto a proof afterwards.
|
|
494
|
+
|
|
495
|
+
**keep `proofs` playable and watermarked.** the buyer swipes a deck on vidfarm.cc and the card plays the first url inline. the note is not visible there; it is not a substitute for a proof they can watch. a proof whose only watchable asset is locked reads as no proof at all.
|
|
496
|
+
|
|
497
|
+
**use the note for everything that follows the master**, not just the file: the ProRes, the project file, the music licence id, the alternate aspect ratios. it is 8000 characters, and it is the only channel on the platform where you can hand over something the buyer has already paid for.
|
|
498
|
+
|
|
499
|
+
**step 4 — nothing.** you do not release it. the rollup does. you can confirm from your own side whenever you like — the submitting worker always reads their own note back, at any status:
|
|
500
|
+
|
|
501
|
+
```bash
|
|
502
|
+
curl -s -H "x-api-key: $DOLLARPLATOON_API_KEY" \
|
|
503
|
+
"https://dollarplatoon.com/api/gigs/$GIG/proofs/$PROOF_ID" \
|
|
504
|
+
| jq '{status, paid_out_at, private_note_locked, private_note}'
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
that `GET` returns the proof **flat** — read `.paid_out_at`, not `.proof.paid_out_at`. `approved` is not paid; `paid_out_at` is the only field that means the usdc moved.
|
|
508
|
+
|
|
509
|
+
**one exception to know about.** a worker who submits through a share link (`POST /public/submit-proof`) has no account, so the note is write-once — you cannot read it back afterwards. keep your own copy of the master and the url before you submit that way.
|
|
510
|
+
|
|
511
|
+
**never put the clean master in the task's `upload_link` folder.** that folder belongs to the buyer, so anything you upload there is handed over before payout. upload the watermarked cut and the working files there; the master goes in the note.
|
|
512
|
+
|
|
403
513
|
### the fast path for an agent — `vidfarm gigs`, with your own key
|
|
404
514
|
|
|
405
515
|
you do **not** need a vidfarm account for any of this. install the devcli, export your own dollarplatoon key, and the whole worker loop is six commands:
|
|
@@ -440,7 +550,7 @@ no client, no cli? every one of those is a plain http call — `GET /api/v1/shar
|
|
|
440
550
|
three rules that decide whether you get paid:
|
|
441
551
|
|
|
442
552
|
1. **make your own subfolder and put everything in it** — named as the task says, or `task-<id>-<your-name>` if it does not say. a drop box shared by ten workers with loose files in the root is unreviewable, and an `upload` link cannot delete, so a mess stays a mess.
|
|
443
|
-
2. **the folder is not the proof.** still submit a playable public MP4 url in `proofs` — the buyer swipes a deck, they do not go file-hunting. use the folder for the project files, the alternates, and the raw exports.
|
|
553
|
+
2. **the folder is not the proof.** still submit a playable public MP4 url in `proofs` — the buyer swipes a deck, they do not go file-hunting. use the folder for the project files, the alternates, and the raw exports. **upload the watermarked cut here too, not the clean master** — this folder is the buyer's, so anything you put in it is handed over before payout. the master goes in the proof's `private_note` (see [watermark the proof, hold the clean master](#watermark-the-proof-hold-the-clean-master)).
|
|
444
554
|
3. **never assume you can delete or overwrite.** most drop boxes are `upload` mode on purpose. upload a corrected file under a new name (`final-v2.mp4`) and say so in the proof body.
|
|
445
555
|
|
|
446
556
|
if the link answers "this share link is unavailable", the client disabled it — the batch is closed, ask before you keep working.
|
|
@@ -452,7 +562,14 @@ get your `DOLLARPLATOON_API_KEY` from https://gigdesk.cc/settings, or from https
|
|
|
452
562
|
- `GIGDESK_API_KEY` (`gd_live_…`) talks to `https://gigdesk.cc/api`
|
|
453
563
|
- `DOLLARPLATOON_API_KEY` talks to `https://dollarplatoon.com/api`
|
|
454
564
|
|
|
455
|
-
|
|
565
|
+
**the two hosts read different headers, and getting it wrong looks like a bad key:**
|
|
566
|
+
|
|
567
|
+
| Host | Header |
|
|
568
|
+
|---|---|
|
|
569
|
+
| `dollarplatoon.com/api` | `x-api-key: <key>` — **only** this one |
|
|
570
|
+
| `gigdesk.cc/api` | `Authorization: Bearer <key>` (it accepts `x-api-key` too) |
|
|
571
|
+
|
|
572
|
+
dollarplatoon rejects a Bearer-only request with `401 {"error":"Missing x-api-key header"}` **before it ever reads the key**. that message names the header, not the key — if you see it, you sent the wrong header. a key that is genuinely wrong answers `401 {"error":"Invalid API key"}` instead. sending **both** headers is safe and works against either host.
|
|
456
573
|
|
|
457
574
|
### "check available work" → `GET /work/available`
|
|
458
575
|
|
|
@@ -463,11 +580,14 @@ when the gigworker says **"check available work"**, **"check the vending machine
|
|
|
463
580
|
curl -H "Authorization: Bearer $GIGDESK_API_KEY" \
|
|
464
581
|
"https://gigdesk.cc/api/work/available?only_with_work=true"
|
|
465
582
|
|
|
466
|
-
# same answer from the dollarplatoon side
|
|
467
|
-
curl -H "
|
|
468
|
-
"https://dollarplatoon.com/api/work/available?only_with_work=true"
|
|
583
|
+
# same answer from the dollarplatoon side — x-api-key, never Bearer
|
|
584
|
+
curl -H "x-api-key: $DOLLARPLATOON_API_KEY" \
|
|
585
|
+
"https://dollarplatoon.com/api/work/available?only_with_work=true" \
|
|
586
|
+
| jq -r '.items[] | "\(.gig_title)\t\(.gig_id)\tprice \(.price)"'
|
|
469
587
|
```
|
|
470
588
|
|
|
589
|
+
the rows come back under **`items`**, and each carries its own `price` — the gig price is only a default. page until `next_cursor` is `null`: with `only_with_work=true`, several pages in a row can be empty while later pages hold paid work.
|
|
590
|
+
|
|
471
591
|
gigdesk mirrors the dollarplatoon route and adds a `workspace_id` + `workspace_title` to each row. dollarplatoon stays the source of truth for tasks, proofs and payouts.
|
|
472
592
|
|
|
473
593
|
scope the read when you only want part of your work:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@officexapp/vidfarm-devcli",
|
|
3
|
-
"version": "0.21.
|
|
3
|
+
"version": "0.21.49",
|
|
4
4
|
"description": "Local bridge for the Vidfarm Trackpad Editor. `vidfarm serve <template_id>` boots the FULL editor on localhost (disk-backed records/storage, free in-process render); edit composition.html on disk (Claude Code, Codex, etc.) and the browser live-morphs it.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|