feat(socials): credit-gate both scrape endpoints (5 + posts credits) + auth on bulk
Summary
Implements the credit-gate item on recoupable/chat#1836, priced per the Apify cost research. Contract: recoupable/docs#259 — merge order: docs → api.
Pricing: 5 credits + 1 per requested post, per profile scraped (1 credit = $0.01). Worst-case Apify cost is YouTube at $0.003 × 2×posts items ($0.60 at posts=100) → 105 credits keeps ≥1.75× margin; default scrape (5 credits) has 3–20× margin on every platform.
| Layer | Change |
|---|---|
lib/socials/getSocialScrapeCreditCost.ts | 5 + (posts ?? 0) |
lib/socials/ensureSocialScrapeCredits.ts | gate via ensureCreditsOrShortCircuit → 402 with checkoutUrl (auto-recharge path included) |
lib/socials/deductSocialScrapeCredits.ts | deduct-on-success via recordCreditDeduction; never throws (mirrors research) |
POST /api/socials/{id}/scrape | validator gates after auth + artist-access; handler deducts only when the run started (no charge on unsupported platform / failed start) |
POST /api/artist/socials/scrape | gains auth (validateAuthContext → 401) + artist access (checkAccountArtistAccess → 403) — previously fully open (audit); gates on (5+posts) × linked profiles, deducts per profile whose scrape started |
⚠️ Behavior change: the bulk endpoint now requires auth, and both endpoints charge credits. No internal callers exist (grep across chat/api/tasks/admin found only the route itself); external consumers get documented 401/402/403s per docs#259.
Tests (TDD, red → green)
All new behavior written test-first and confirmed failing (10 failures across 6 files) before implementation:
getSocialScrapeCreditCost: 5 / 6 / 25 / 105ensureSocialScrapeCredits/deductSocialScrapeCredits: gate passthrough, 402 short-circuit, never-throws deduction- single endpoint: payload carries
account_id; ensure called with 5 (default) / 25 (posts=20); 402 short-circuit; deduct on success only (not on unsupported platform or failed start) - bulk endpoint: 401 propagation, 403 no-access, ensure (5+20)×2=50, deduct per started profile ((5+20)×1=25 when one of two starts),
[]with zero charge when no socials
Affected domains (lib/socials, lib/artist, lib/apify, lib/credits): 324/324 pass. Full suite passes; tsc --noEmit unchanged at 200 pre-existing errors (none new); ESLint clean.
Preview verification to follow as a PR comment.
🤖 Generated with Claude Code
Summary by cubic
Adds credit gating to both social scrape endpoints at 5 credits + 1 per requested post per profile, and adds auth + artist access checks to the bulk artist scrape. Aligns the API with the cost model and docs in recoupable/docs#259 and closes the credit-gate requirement in recoupable/chat#1836.
-
New Features
- Upfront credit check with 402 short-circuit and
checkoutUrl; cost = 5 + posts per profile. POST /api/socials/{id}/scrape: validates auth and artist access, forwardsposts, deducts only if the Apify run starts.POST /api/artist/socials/scrape: now requires auth and artist access; gates on (5 + posts) × linked profiles; deducts per profile that starts; returns [] with no charge when no socials.
- Upfront credit check with 402 short-circuit and
-
Migration
- Merge order: docs → api (
recoupable/docs#259). - External callers must include auth and handle 401/402/403 responses.
- Merge order: docs → api (