recoupable/skills

recoup-internal-sales

>- INTERNAL — Recoup staff tooling, gated by the recoup-internal keyword. Invoke ONLY when the request explicitly includes "recoup-internal" (e.g. "recoup-internal run the sales sweep"). Never use for customer-facing or artist requests. Run Recoup's daily sales sweep — walk the whole activity surface (Stripe payments, Privy logins, new catalog valuations, BOTH Attio funnels (Valuation Leads for subscriptions and Agency Leads for custom-build/advisory work), Supabase credits, scheduled tasks, an…

First seen Jul 28, 2026

Installation

$ npx skills add recoupable/skills --skill recoup-internal-sales

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from recoupable/skills · top by installs.

npx skills add recoupable/skills

Browse all from recoupable/skills

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Repository health

Stars 3
License LICENSE
Default branch main
Open issues 0
Status Active

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 39,888 B
  • docs SUMMARY.md 1,273 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 23 installs

SKILL.md

Recoup Sales Sweep

The repeatable prospecting + follow-up motion for Recoup staff. Eight data pulls plus one synthesis: read every place a sales signal shows up, collapse it into one ranked list of people to follow up with, then act — enrich/advance the Attio pipeline and draft the outreach. First drafted 2026-07-23; refine the steps below against each real run.

Define the window up front as UTC [FROM, TO) — usually since the last sweep (default last 24h). Run the eight pulls in parallel (subagents work well; each is self-contained), then synthesize.

Run the acquisition circuit-breaker check before anything else. If no new paid customer has landed in 14 days, the default priority order will send you into retention work again and the sweep will not produce a sale.

What this is (and what it is not)

This skill is forward-looking: it produces actions (who to contact, why, how). It deliberately reuses the data plumbing of two sibling skills instead of re-implementing it — load them for the ready-made queries/scripts:

  • recoup-internal-weekly-usage-review — the retrospective numbers (Stripe,

Privy logins, the credits/chats/tasks/valuations SQL pack). This sweep turns those same pulls into follow-ups; that skill reports the totals.

  • recoup-internal-funnel-valuation-pipeline — the deep motion for one

valuation lead: Attio funnel recipes, qualification rubric, the valuation PDF, and the outreach template. Step 3 hands qualified new valuations to it.

Also useful: recoup-internal-account-health-report (deep dive on one account you're about to contact), recoup-internal-task-email-audit (task-run health for step 6), recoup-platform-api-access (raw Recoup API).

Prerequisites

  • RECOUPAPIKEY / RECOUPACCESSTOKENthe primary source. A recoupsk

key, or an admin Privy JWT for cross-account work (see Source order). Load recoup-platform-api-access for the endpoint map.

  • ATTIOAPIKEY — read + write (this sweep writes follow-ups back).
  • Supabase read access to production (MCP execute_sql or psql) — **augmentation

only**, for the fleet-wide aggregates the API has no endpoint for.

  • Stripe: authenticated CLI or a live restricted key (pass --api-key — the

keychain is often unreadable in sandboxed shells).

  • PRIVYAPPID + PRIVYPROJECTSECRET — Privy Management API.

Everything is a GET or SELECT except the two sanctioned writes named in Guardrails (Attio follow-ups; disabling a confirmed-dead task). Outreach is always drafted for a human to send, never sent from this skill.

Source order — dogfood the API first

1. Recoup API → 2. Supabase. Reach for api.recoupable.dev first and drop to SQL only for what it cannot answer. This is not a style preference: the sweep is run by the people who own the product, and every pull is a free QA pass. The 2026-07-26 run found two product bugs purely by looking at API responses that no SQL query would have surfaced.

Use the API for anything scoped to one account, artist, or task:

Need Call Why not SQL
A customer's tasks GET /api/tasks?account_id= Returns owneremail, model, nextrun, upcoming, recentruns, triggerscheduleidnone of which exist as columns on scheduledactions. A schedule that will never fire is only visible here.
An artist's socials GET /api/artists?account_id= Socials are embedded as accountsocials; the hand-rolled accounts → accountsocials → socials join is where mistakes happen.
Remove an artist DELETE /api/artists/{id} Encodes the last-owner check and the fail-closed song-dependency guard. Raw SQL skips both.
Email → account POST /api/accounts One call vs. a account_emails lookup.
Refresh a profile POST /api/socials/{id}/scrape Real scrape; SQL only shows you stale rows.

Admin cross-account access. An admin Privy JWT plus an accountid override lets you read/write in a customer's context without their key — accountid as a query param on GETs, in the JSON body on PATCH/DELETE. Authorized by validateAuthContext / checkIsAdmin. Confirm with GET /api/admins{"isAdmin":true}. These JWTs expire in ~1 hour — re-request rather than debugging a sudden 401.

Drop to SQL only for the fleet-wide sweep — the cross-account aggregates that have no endpoint: per-account credit burn, negative balances, went-silent accounts, zombie tasks, valuation runs in a window, chat-title themes. That is most of the eight pulls, and SQL is genuinely the right tool there. The rule is about account-scoped reads, not about banning SQL.

When the two disagree, the API is the customer's truth — it is what the product actually serves them. A DB row the API doesn't surface is not something the customer can see.

Check this first — the acquisition circuit-breaker

Before running the sweep, ask one question: how many new paid customers have we added in the last 14 days? If the answer is zero, the sweep's normal priority order is actively wrong and you must switch modes.

Why this exists: seven of the eight pulls below read our own database. Every one of them requires the person to already be a user. That makes this an excellent farming motion with almost no hunting, and the default ranking (failed payment > new customer > warm valuation lead) puts retention ahead of acquisition every single time. On a small base with noisy retention signals, you never reach pull 3.

The one exception is the Agency Leads funnel (pull 4), which is the only surface in this sweep where a live lead need never have touched the product. Agency revenue is a separate line from subscriptions and it converts on a different clock: a build or advisory engagement can close from a warm personal contact in a week, with no signup, no credits and no activation. When the product funnel is cold, the agency board is often the fastest path to revenue in the whole sweep — read it first, not last.

That failure mode is not hypothetical. Measured 2026-08-04: two months with zero new sales, while 93 distinct accounts ran a catalog valuation in 60 days (241 runs in June, 198 in July) and 56 of them ran exactly once and never returned. The demand was there the whole time. Nobody worked it, because the sweep kept routing the day into retention on existing accounts.

In acquisition mode, do this instead — in this order:

  1. Work the valuation backlog before anything else. Pull 3, but over the *whole

backlog*, not just the sweep window. Every account that ran a valuation and has no Attio entry is an unworked, hands-raised lead. Prioritize one-and-done runs: they got their number and left, which is the sharpest unanswered-question signal we have.

  1. Work the Agency Leads board. Pull 4. Anything at In Conversation with no time

on the calendar is a warm lead one message away from an advancement, and it does not require the person to be a user at all.

  1. Look outside the database. No other pull does this, so it will not happen

unless you make it happen: named-account research, inbound-adjacent communities, and the enterprise/custom-dev angle for any label or catalog-holder domain already in Attio. Hunting is not a data pull; it is a decision to spend the day differently. New agency leads land on agency_leads at New.

  1. Cap retention work. Timebox existing-account work so it cannot consume the day.

A zombie task or a stale follow-up is real, but it will never produce a new sale.

Return to the normal ordering once a new paid customer lands.

The eight pulls

Each pull is: what to read → the sales signal → the action. Always drop test rows (sweetmantech*, sidney@, @example.com, [TEST], preview-auth-probe).

These eight are the fleet-wide layer, so most of them are legitimately SQL (or Stripe/Privy/Attio). The API-first rule bites at the next step: the moment the ranked list names a person, every drill-down on that account — their tasks, artists, socials, scrape freshness — goes through api.recoupable.dev, not another SELECT. Ranking is a SQL job; qualifying a named lead is an API job.

1. Stripe — new money to protect

  • Read: subscriptions created / trials converted / first invoices in the

window; failed payments + disputes; credits_topup sessions (weekly-usage-review §3 has the exact calls).

  • Signal: a new paid customer or trial-converter → welcome + make-sure-they-

activate follow-up (the highest-value moment to reach out). A failed payment / dispute → save-the-account follow-up, today. A top-up loop (many identical small sessions) → a "let's right-size your plan" conversation.

  • Action: log the person in Attio; draft the welcome / recovery note.

⚠️ A raw checkout-session count measures bots, not demand. autoRechargeOrFail
mints a Checkout session server-side whenever a scheduled run is short of credits,
so the session list is dominated by cron artefacts with no human anywhere near them.
Measured 2026-08-04: **100 live sessions over two days, 0 paid, 0 with a
customer_email, 49% created inside four distinct minutes of the hour, across 22
accounts.** Reading that as "people are trying to sign up" is backwards — and reading
its absence as "nobody wants us" is equally wrong.

Always filter before you quote a number: keep only sessions with a non-null
customer_email, then drop any minute-cluster that lines up with an account's cron
schedule. What survives is human intent. If nothing survives, say **"we cannot see
human purchase intent"** — not "there is none."

The same artefact appears at the customer level: a Stripe customer with
email: null, no payment methods, and a created timestamp inside the account's own
cron window was minted by the same code path. Never write that up as an "abandoned
checkout" — check created against the account's task schedule first.

2. Privy — who is signing in

  • Read: the Privy-logins script from recoup-internal-weekly-usage-review — it

paginates the Privy Management API into new signups vs returning for the window.

  • Signal: a new signup with no chats yet → onboarding-stall follow-up. A

returning power user → expansion. An enterprise/label domain email → high- value human outreach. Cross-check session-riders (long sessions never bump latestverifiedat) against the interactive-chats query in step 7.

  • Action: route new signups through the activation-funnel lens; flag warm

enterprise leads for a personal note.

3. Valuations — new catalog leads

  • Read: playcount_snapshots created in the window (weekly-usage-review SQL

query 13). A row with a NULL catalog is a measured-but-unclaimed run — a brand-new warm lead nobody is working yet.

  • Signal: every new valuation is a lead; unclaimed ones are the freshest.
  • Action: hand each qualified lead to

recoup-internal-funnel-valuation-pipeline (research → qualify → enrich Attio → valuation PDF → outreach). Don't re-implement that motion here.

4. Attio — the follow-up backlog (the heart of this sweep)

The one pull that is only about follow-through. Read BOTH people funnels every sweep — they are separate revenue lines and a sweep that reads one is blind to half the pipeline:

List api_slug What lives there
Valuation Leads valuation_leads Product/subscription funnel. Someone ran a catalog valuation and could become a Pro subscriber.
Agency Leads agency_leads Services funnel. Custom build, advisory, or retainer work. Usually arrives as a person emailing us, often from the personal network, and frequently has nothing to do with music.

Load recoup-internal-funnel-valuation-pipeline for the Valuation Leads funnel recipes (list entries, stages, field slugs, enrich/advance calls). The same Attio API shapes work for agency_leads.

Never file a lead on the wrong list. An agency lead parked in Valuation Leads inflates the valuation funnel with someone who never ran a valuation, exactly the way a wrong lead_source does. If the person has no catalog interest, they are an agency lead. A person can legitimately appear on both, but only once each side is real.

Valuation Leads — stages and SLA

New → Report Delivered → Qualified → Pro Offer Sent → Pro Active (Won) → Lost

  • Owed a nudge: entries at Report Delivered or Pro Offer Sent whose

last activity is older than the follow-up SLA (default 3 business days) with no reply/advance — someone we reached out to who went quiet.

Agency Leads — stages and SLA

New → In Conversation → Call Booked → Scoping → Proposal Sent → Won → Nurture → Lost

Fields: stage, buyerorreferrer (Buyer / Referrer / Unknown), projecttype (Build / Advisory / Retainer / Unknown), owner, estprojectvalue, lostreason.

  • The stage that matters most is In Conversation. It means they replied but **no

time is on the calendar. Call Booked is deliberately separate, because "agreed to a call" and "a time exists" are different states and the gap between them is where this pipeline has historically died — two threads went cold for four years and three and a half years respectively, both immediately after the lead said yes to a call. Both lapses were ours. Flag any entry sitting at In Conversation for more than 3 days as the most urgent item on the agency side**, ahead of anything further down the board.

  • Nurture is where good contacts go to be forgotten unless policed. Any Nurture

entry with no dated follow-up task is a bug: create the task or move it to Lost.

  • Resolve buyerorreferrer before scoping. Personal-network leads are frequently

introductions, not buyers, and treating a referrer as a buyer wastes a cycle.

  • Leave estprojectvalue empty until the work is scoped. A guessed number makes an

unqualified lead look qualified.

Both lists

  • Flagged for follow-up: any entry whose note / next-step says to circle back.
  • Signal → action:

- Non-responder, < 3 touches → draft the next nudge (new angle, not a resend). - Non-responder, ≥ 3 touches over ~2 weeks → move to Lost with lost_reason = No response so the board stays honest. - Replied / booked → advance the stage and set the next step.

  • Always set the owner so every live lead is accountable to a person.
  • A personal contact is not a sequence. Much of the agency list comes from the

founder's own network. One light nudge from a personal inbox with a new angle, never an automated cadence.

5. Credits — engagement and billing risk

  • Read: per-account burn, current balances, and prior-week-active-now-silent

(weekly-usage-review SQL queries 2, 4, 8).

  • Signal: heavy burners → healthy; candidate for expansion / a testimonial.

Negative balance still burning → billing risk and a top-up conversation (nothing stops them at zero). Went silent (active last week, zero this week) → churn-save follow-up while it's still warm.

  • ⚠️ Burn reads wrong in BOTH directions. High burn is not engagement — a daily

scheduled task produces usage events and looks like an active account. And near-zero burn is not disengagement: it is just as often an account we never funded. Confirm against step 7 before calling anyone active or dead.

  • Read the balance and the plan together (see Credits mechanics below). An account

that has been at the same balance for months is not frugal, it is unrefilled.

  • Action: log the follow-up; for a billing-risk account, pair the outreach with

the plan-fit conversation from step 1.

Before quoting anyone's balance, read references/credits-mechanics.md. The refill is lazy and reading a balance mutates it; it SETS rather than adds, so on a free-tier account above 333 a read silently reduces it; and isPro is derived from Stripe by account id, so a customer whose Stripe record is missing metadata.accountId reads as free tier while paying.

6. Tasks — zombie schedules on inactive accounts

Scheduled tasks keep firing (and burning AI tokens) long after an account goes cold. Find enabled schedules whose owner has no recent usage and no recent login — reach out to confirm the task still adds value, else turn it off to save spend. scheduledactions.lastrun is dead (not written by the Workflows runner), so judge activity by usage_events, not that column.

-- Zombie tasks: enabled schedules whose account had NO usage in the last 30 days.
-- (Cross-check task-run health with recoup-internal-task-email-audit.)
SELECT sa.account_id, ae.email, sa.title, sa.schedule, a2.name AS artist,
       (SELECT MAX(created_at) FROM usage_events ue
          WHERE ue.account_id = sa.account_id) AS last_usage
FROM scheduled_actions sa
LEFT JOIN LATERAL (SELECT email FROM account_emails
  WHERE account_id = sa.account_id LIMIT 1) ae ON true
LEFT JOIN accounts a2 ON a2.id = sa.artist_account_id
WHERE sa.enabled = true
  AND NOT EXISTS (SELECT 1 FROM usage_events ue
    WHERE ue.account_id = sa.account_id
      AND ue.created_at >= now() - interval '30 days')
ORDER BY ae.email NULLS LAST, sa.title;
  • Signal: a still-firing task for a dormant / non-paying account = wasted

tokens and a re-engagement opening ("this report still runs — still useful?").

  • Action: draft the check-in. Disable the schedule only after no-response or

an explicit "turn it off" — see Guardrails; disabling is a mutation.

7. Chats — what customers actually use Recoup for

  • Read: chats created in the window with titles (weekly-usage-review SQL queries

5a/5b). Titles are admin-readable; bodies are owner-scoped (see account-health caveat).

  • ⚠️ **Do not split on the 'Scheduled generation' topic — it no longer identifies

autopilot. Scheduled rooms now commonly carry a NULL topic, and titled rooms include cron output (a real one: "Weekly Performance Dashboard 2026-07-20 to 2026-07-26"). Two tests that do work: - Clock alignment. A cron fires at the same minute every day; a human is off-clock. Group an account's rooms by extract(minute from updatedat) — one dominant minute is the schedule, everything else is a person. - User messages per room (the sharper one). A scheduled run writes exactly one user-role message per room**, at the cron minute. A human session is multi-turn and off-clock. count(usermsgs) == count(rooms) for a month means nobody typed anything that month.

  • Signal: theme the titles (strategy / analytics / content / release) to see

what the product is used for — that shapes both the follow-up hook and product feedback. A new interactive user is engaged and worth a check-in; a recurring pain theme is a feature request plus a helpful-outreach opening.

  • Action: note the usage themes; follow up with high-intent interactive users;

feed recurring friction back to product.

8. Outbound email — what we have already said to them

  • Read: the Resend sent log (paginates well past a 14-day window) plus

emailsendlog, with per-message delivery status.

  • Signal: a lead with zero human outreach ever (only product email) → an

untouched first-contact opportunity. Bounces on a task email → a customer's own report silently failing (fix before anyone sells them anything). Duplicate sends of the same report → a runner bug hitting a real inbox — an owned-and-fixed apology is the warmest opener there is.

  • Action: never-contacted feeds the ranked list; bounces and duplicates become

fix-then-tell hooks (see The send loop).

Synthesis — one ranked follow-up list

  1. Collapse to people. Key every signal by account/email and merge across the

eight pulls — one person, all their signals, deduped.

  1. Rank by opportunity × urgency. Roughly: failed payment / churn-in-progress

(save today) > new paid customer (welcome now) > agency lead stalled at In Conversation (a booked call is the cheapest advancement available) > warm valuation or enterprise lead (work this week) > expansion / testimonial > zombie-task cleanup. This ordering assumes a funnel that is converting. If no new paid customer has landed in 14 days, invert it — agency leads, warm valuation leads and enterprise leads come first. See the acquisition circuit-breaker above.

  1. Correlate across sources — the cross-checks are where the real finds are:

- paid in Stripe but no Privy login → onboarding never landed; reach out now. - valuation run with no Attio entry → an unworked lead; create it (step 3). - negative credits and an enterprise domain → expansion priced as risk. - zombie task on a went-silent account → one message does double duty (re-engage + stop the token drain). - agency lead at In Conversation with no dated follow-up task → the exact shape of every thread this pipeline has lost. Create the task before moving on. - the same person on both funnels → decide which is real now and work that one; pitching a subscription to someone who is talking to us about a build reads as a sales sequence and costs the warmer thread.

  1. Act: write the follow-ups into Attio (create/advance entries, set owner +

next step) and draft each outreach. Present the ranked list + drafts to the operator to send. Before drafting for any selected contact, build their four-source dossier (next section).

The contact dossier — before any outreach is drafted

The moment the operator selects a specific contact (from the ranked list or on their own), research that person into one concise four-row table and present it alongside the draft. It is the pre-flight that catches "already paying", "payment-blocked, not disinterested", "this IS the artist", and "we emailed them yesterday". Build it fresh even if a sweep pull already touched the account — the fleet aggregates hide the per-person timeline.

Source What goes in the row The lookup that works
Privy first + last login events POST https://auth.privy.io/api/v1/users/email/address with {"address": "<email>"} (basic auth PRIVYAPPID:PRIVYPROJECTSECRET + privy-app-id header) → createdat and each linked account's firstverifiedat / latestverified_at. One call — no need to re-run the pagination script for one person.
Stripe customer / card / subscription status Find the customer by metadata.accountId (email search misses — see Tooling gotchas), then GET /v1/customers/{id}/payment_methods and GET /v1/subscriptions?customer=…&status=all. A customer with 0 payment methods and 0 subscriptions = they reached checkout and never paid — blocked, not disinterested.
Supabase credits_usage, sessions, chats All by accountid — except chats, which has no accountid column: join sessions.accountid → chats.sessionid, then count chat_messages rows with role='user' per chat. Report typed chats separately from empty "New chat" rows — an untyped open is still a signal (they visited and bounced). Balance via SQL only, never the credits GET (see Credits mechanics).
Resend outbound emails we've sent them emailsendlog by accountid (there are no toemail/subject columns; the subject is inside rawbody) → cross-ref each resendid against the Resend API for subject + delivery status. Zero rows = we have never emailed this person from the product.

Read the table before writing a word of outreach: the first/last-login pair sets the "returning vs. new" frame, the Stripe row sets the ask (rescue vs. upgrade vs. retention), the chats row tells you what they actually tried to do in their own words (titles are admin-readable), and the Resend row is what "we" have already said to them.

Then extend the dossier with four product-side tables — what the contact has actually done with Recoup, in their own timeline:

Table What goes in it The lookup that works
Lifetime activity Key dated events from signup to now, weighted to recent: first login, each typed chat (title verbatim), tasks created, valuation runs, checkout attempts, emails delivered or missed, last event. Merge the four source rows chronologically. A 10–15 row timeline reads faster than any aggregate and is where the story ("built the report Thursday, hit the paywall Monday") becomes visible.
Tasks Every task: title, artist, cadence, model, enabled, last run + status, next runs, does it email — and does the sent email match the intention the customer typed when they created it? GET /api/tasks?accountid= only (model / upcoming / recentruns / owneremail don't exist on scheduledactions). For intention-vs-output: compare the emailsendlog.rawbody headline against the chat/task title that spawned it. A run that says COMPLETED with no matching emailsend_log row delivered nothing — the customer thinks the task ran; it silently didn't reach them.
Artists Roster: each artist + every connected social (platform, handle/URL, followers, scrape freshness). GET /api/artists?accountid= — socials embedded as accountsocials; the social's updated_at is the scrape date. A one-platform roster (e.g. Spotify only) is itself an outreach hook: nothing else is connected.
Valuation / catalog Run count + dates, claimed vs unclaimed, the value on file, what's in the catalog (songs / albums / total streams), and the crown jewels (top songs by plays). playcountsnapshots by account (the column is account, not accountid) → songmeasurements.snapshot = ps.id, join songs ON songs.isrc = songmeasurements.song; value is the play count. The dollar value is not in the DB (never persisted) — read it off the Attio auto-note or the valuation email. Repeat runs of the same catalog in one sitting = they're trying to answer a question; find out which one.

The lead workspace — where every email lives

Each lead gets one folder in the operator's private workspace (workspace/sales/<lead>/, its own git repo, never a product submodule). Two things in it are load-bearing for the send loop; everything else (notes, PDFs, call transcripts, task prompts) sits beside them.

workspace/sales/<lead>/
  EMAILS.md            chronological index: every send + receipt, the read, the reply playbook
  emails/
    README.md          the four lines below, so a cold reader knows the rule
    sent/              verbatim final text of each send, one file, with its reply playbook
    received/          verbatim inbound emails, one file each
    threads/           verbatim multi-message Gmail threads (secrets redacted)
    drafts/            every draft and revision; never the sent version

Files are YYYY-MM-DD-slug.md. Rules that keep the record trustworthy:

  • EMAILS.md is the index, emails/ is the evidence. The index entry links to the

file; the file holds the words. Never let a loose DRAFT-*.md sit at the folder root.

  • The sent file is the operator's copy, not the draft. Step 3 of the send loop

("diff what actually went out") ends by writing the sent text to emails/sent/; a draft promoted to "sent" without the diff is how the record and the inbox drift apart.

  • Every inbound reply gets a received/ file the day it arrives, even a one-liner.

The 2026-08-25 reorg of two live accounts found one sent email that existed only in Attio and two replies that existed only in Gmail; the reorg is what surfaced them.

  • Secrets never enter the files. API tokens that ride inside task prompts get

redacted before the prompt or thread is copied in.

  • Attio mirrors, it does not replace. Each send/receipt still becomes an Attio note

(the CRM's email sync misses bodies), and the note names the local file path.

The send loop — dossier to closed-out record

The per-contact motion that converts. Run it identically for every send; the 2026-07-29 run repeated it seven for seven with same-day replies from cold contacts.

  1. Fix, then tell. The dossier almost always surfaces account defects —

duplicate artists or tasks, unconnected socials, blocked balances, schedules that won't deliver. Repair the unambiguous ones via the API before drafting, and open the email with what you did ("While looking at your account I fixed two things you shouldn't have had to deal with"). The specific observed detail is what makes outreach read personal; a fix already delivered is what makes it worth answering.

  1. Draft in the house voice. Open with the concrete observation, never

meta-framing ("a quick note from a human" reads as automation). Offers are things we will do, not commands to the reader ("I'll send you the link next week, or now if you ask" beats "reply GO"). One plain close: "Let me know how I can help." Valuation numbers always carry their caveats. Every email carries at least one in-body link to a recoupable.dev subdomain — deep-linked to the exact thing you are asking for, never just the signature's home-page link. A reply that never becomes a site visit cannot convert, and with no in-body link every action routes back through a human by default, which is how a warm lead stalls. Match the link to the ask:

The ask Link
Connect / fix streaming + social platforms https://chat.recoupable.dev/setup/socials
Re-run or check a catalog valuation https://chat.recoupable.dev/catalogs
Set up or edit a scheduled report https://chat.recoupable.dev/tasks
Anything else / general return visit https://chat.recoupable.dev

Then run the draft through the unslop skill before presenting it. Every customer-facing draft gets an unslop pass — it strips the AI tells (em dashes, puffery, "not just X but Y", AI vocabulary) that make outreach read machine-written, and its no-em-dash rule enforces this skill's own standing rule for customer email. If unslop is not installed, get it from skills.sh/cursor/plugins/unslop (npx skills add — it installs at user level and applies to all writing). The pass edits the draft in place; meaning and commitments must survive it unchanged.

  1. The operator sends; diff what actually went out. Read the sent copy

(from the CRM email sync, or the operator's paste) against your draft. Any commitment the operator added or changed becomes the follow-up task's content — the record must match the inbox, not the draft. Write the sent text to emails/sent/ (see The lead workspace) before closing out.

  1. Close out every send the same way: complete the open task the send

fulfilled; write a sent-log note stating what was promised and the reply playbook (what to do for each likely reply); do the keep-a-lead-warm trio — set owner, log the note, create a dated follow-up task (a Qualified enterprise lead once sat 21 days untouched because none of the three existed), writing that task as a runbook — exact ids, API calls, decision rules — executable by a cold reader; set the stage to what the email actually did (a Pro pitch → Pro Offer Sent; delivering the number → Report Delivered; a retention touch → no stage change).

  1. On reply, run the playbook same-day. Log the reply; if the sender is

not who the record says, correct the identity immediately (rename, split the people, fix future greetings); pay for substantive product feedback on the spot (grant, verify the balance landed, say so in the reply); reproduce any reported bug in a live browser the same day and file it with evidence; and replace any task the reply mooted — a no-touch rule dies the moment they engage.

Meeting prep — the qualified-customer call

The send loop ends when a lead books a call. What happens next is a different motion — not "who do we contact" but "what do we put in front of someone who has already agreed to spend thirty minutes with us." Run it for any booked call with a qualified customer. It produces exactly three artifacts: a pitch, a meeting plan, and the PDFs to deliver.

Full workflow in references/meeting-prep.md — mining what the customer already told us (read chat message bodies, never the auto-generated titles), finding the gap between what they promise publicly and what their published process delivers, the four document shapes, and the house format.

Two things that are easy to get wrong and expensive to undo:

  • Never bring an account-health / account-status PDF to a prospect. That

format is internal tooling; run it on a quiet account and you hand someone an audit of their colleague's inactivity.

  • Data documents survive the call; argued documents often need re-aiming.

Invest in the measured ones, and argue only from what the customer already did, never from an assumed goal.

Guardrails

  • Read-only except two sanctioned writes: enriching/advancing Attio, and

disabling a confirmed-dead task. Both are reversible; still confirm before moving a lead to Lost or disabling a task.

  • Never mutate customer data with raw SQL — go through the API. An UPDATE on

scheduledactions writes the row but skips everything the endpoint does around it: updateTask re-syncs the Trigger.dev schedule, and deleteArtist runs a last-owner check plus a fail-closed song-dependency guard before hard-deleting. A direct write leaves the row and the scheduler disagreeing, and the drift is invisible in the table you just edited. Use PATCH/DELETE with an accountid override. This applies to every customer-facing table, not just tasks.

  • Draft, never send. Every message touching a real customer is handed to the

operator to send. No auto-send from this skill.

  • PII stays in the CRM. Names, emails, account IDs live in Attio/Supabase —

never paste them into shared docs or external services.

  • Exclude test rows everywhere (sweetmantech*, sidney@, @example.com,

[TEST], preview-auth-probe).

  • Trust Stripe over the DB for paying status — the Supabase subscriptions

mirror can be empty/stale.

Lessons from the first live run (2026-07-23)

Four leads worked end to end: an inbound valuation lead, a distributor/broker, a major-label exec, and a dormant power user. What actually mattered.

Qualification

  • Credit burn is not engagement. Two accounts looked "active today" and were

pure autopilot — the usage was a scheduled task firing. One had been dark 4 months while its daily task quietly burned the balance negative. Check the last interactive chat date, always (step 7 has the two tests that survive a NULL topic).

  • And a quiet account is not a disengaged one. A subscriber of 19 months read as

dormant — 1 active day, 0 chats — because he had 456 credits against a 9,999 entitlement the whole time and nobody had ever read his balance to trigger the refill. He had paid $405.46 for roughly 2% of one month's allowance. Fleet-wide, 1,499 of 1,646 accounts were carrying an unapplied refill and 133 were blocked at or below zero. Before writing anyone off, check whether we ever funded them.

  • Qualify the catalog before spending human time. A lead who ran a valuation

and asked "what can I sell for" held a 10-track public-domain classical catalog worth ~$700 — no publishing to sell, since Bach and Chopin are public domain. Capture works fine; qualification is the gap. Check repertoire type and streams before drafting anything.

Valuation you can put in front of a buyer

  • Say "asset value, not a bid." A catalog buyer asked "is this for the buy?"

The number models a 10-16x multiple on sustainable annual net label share. It is not an offer price and must never anchor an LOI.

  • It mis-prices older catalogs in BOTH directions. Against four independent

market comps (independent catalog buyers and distributors) it was accurate on a 2-year catalog, undervalued an 8-year/90-release catalog ~2x, and overvalued 8-10 year catalogs. Cause: annual run rate is estimated as lifetime streams ÷ catalog age, which averages an old catalog flat — understating one weighted to recent output, overstating a decaying one. The market prices trailing-12-month performance. Older than ~3 years: get statements before quoting.

  • It is Spotify only. One artist's self-reported ~700k/month Amazon track

was worth roughly 10-20x his entire Spotify catalog per year at Amazon's public ~$0.00402/stream rate. Ask about other DSPs before calling a catalog small.

  • Pull real release dates. Catalog age is floored at 1 year and falls back to 5

when unknown, so identical streams can value ~5x apart.

Delivering

  • The sequence that converted: verify the roster → measure → render the PDF →

deliver by email and the channel they replied on → advise honestly on the number → ask for statements. Being the one who says "you may be overpaying" is what turned a valuation into a partnership conversation.

  • Keep outreach short and lead with the concrete thing you did for them. Attach a

report only when their job makes it relevant — a label exec has no use for a catalog valuation of an artist their employer already owns.

Reference files

  • references/meeting-prep.md — prep for a booked call: the pitch, the meeting plan, the PDFs to deliver.
  • references/credits-mechanics.md — how checkAndResetCredits behaves, before you quote a balance.
  • references/tooling-gotchas.md — Privy / Stripe / Supabase / Attio quirks and dead columns.