Payout-pain score

sellers.payoutPainScore is an integer 0–100 estimating how acutely a marketplace seller needs faster access to their payouts. Higher means stronger cash-flow pressure, which means a better prospect for payout financing.

It is produced by the second AI pass in the enrichment pipeline — a pure reasoning call with no tools, reading only structured seller data plus the notes the first pass gathered.

Implementation: lambdas/enrichment_scorer/agents/seller_scoring_agent.py, prompts in agents/prompts.py, result model in models/enrichment.py.

Agent configuration

Setting Value

Model

AgentModelIds.CLAUDE_HAIKU_3

max_tokens

4096

max_turns

3

Tools

none — tools=lambda: []

Structured output

PayoutPainScoreResult

max_tokens is 4096, not 1024. The model writes a per-signal reasoning block of roughly 1,000–2,000 tokens before calling the structured-output tool; a tighter budget produced MaxTokensReachedException.

Result:

class PayoutPainScoreResult(BaseModel):
    payout_pain_score: int   # 0–100
    reasoning: str           # one paragraph explaining each signal's contribution

Only payout_pain_score is persisted, to sellers.payoutPainScore. The reasoning is logged at DEBUG and then discarded.

Inputs

Field Source

seller_id

sellers.sellerId

business_name

businessName, falling back to name

platforms

The marketplace string; multiple platforms are comma-separated

primary_category

primaryCategory

estimated_monthly_revenue

estimateSales, formatted as $N/month

mom_growth

moMGrowth, formatted to one decimal place with %

asin_count

numberAsins

started_selling_date

startedSellingDate as an ISO date

today_date

date.today().isoformat() — needed for the seasonal signal

enrichment_notes

SellerEnrichmentResult.notes from the first pass, or sellers.note when scoring runs without a fresh enrichment

Any missing value is rendered as "Unknown" and the prompt instructs the model to treat it as the lowest-scoring tier for that signal.

The eight signals

Total weight is 100. Each signal is evaluated independently and the weighted scores are summed.

1. Estimated monthly revenue — weight 20

The primary driver: how much cash is locked in transit.

Band Points Rationale

< $1k or unknown

0

Nothing meaningful locked up

$1k–$10k

8

Small; low absolute pressure

$10k–$80k

20

Sweet spot — meaningful cash, limited access to credit

$80k–$500k

15

Larger; more likely to have financing options

> $500k

10

Enterprise scale; institutional credit available

The curve is deliberately non-monotonic. Very large sellers have banks.

2. Revenue growth trend — weight 15

Growth outruns working capital faster than anything else.

MoM growth Points

Unknown or negative

0

0–5%

5

5–15%

10

> 15%

15

3. Seasonal timing — weight 15

Cash-flow pressure peaks 60–90 days before a high-volume season, when inventory must be bought. Windows: Q4 (Oct–Dec), Prime Day (typically July), back-to-school (August). Scored relative to today_date.

Distance to peak Points Rationale

Outside all windows

0

90–120 days

8

Early build-up

60–90 days

15

Maximum pressure window

30–60 days

10

Late but still relevant

< 30 days

5

Too close — inventory is already ordered

This is the only signal whose value changes without the seller changing, so the same seller re-scored in a different month can legitimately land on a different total.

4. ASIN / SKU count — weight 10

More product lines means more inventory to fund at once.

Count Points Rationale

Unknown or 0

0

1–10

3

11–50

7

51–200

10

> 200

8

Likely dropship or wholesale; lower pressure per ASIN

5. Primary category — weight 10

High cost-of-goods, long replenishment cycles.

Category Points

Unknown or services

0

Books, media, toys

4

Beauty, grocery, pet

6

Home goods, outdoors, auto

8

Electronics, supplements, industrial, health

10

6. Seller tenure — weight 10

Established enough to have real revenue, too small for institutional credit.

Tenure Points Rationale

Unknown or < 6 months

2

Too new, unproven

6–12 months

6

1–3 years

10

Sweet spot

3–7 years

7

> 7 years

4

Likely has established credit lines

7. Multi-marketplace presence — weight 10

Each marketplace holds funds independently, so selling on two doubles the delay problem.

Presence Points

Single marketplace

0

Two or more

10

8. Trigger event — weight 10

Active stress signals found in the enrichment notes or inferable from context.

Signal Points

None detected

0

One weak signal (for example a new product launch)

5

Strong signal (payment hold, rapid scaling, suspension lifted, large purchase order)

10

This is the only signal that depends on the first AI pass. A seller scored without enrichment notes can score at most 90.

Interpreting a score

Range Meaning

70–100

Almost certainly an acute cash-flow problem: revenue large enough to lock meaningful capital, growth outpacing working capital, external signals confirming pressure

30–70

The expected range for most sellers

0–30

Little evidence of cash-flow stress

The prompt states this distribution explicitly, which anchors the model away from clustering everything at one end.

Where the score is used

Written to sellers.payoutPainScore and consumed by the wider platform for prioritisation — for example the campaign thresholds described in the enrichment agent plan (> 40 enrol in email, > 70 call first).

The score is not an input to any re-engagement segment in this repository. Those segments prioritise on engagement recency and call history, not on payout pain. See Re-engagement segments.

Failure and backfill

Scoring is best-effort. _apply_score() runs in its own short-lived session so a failure cannot roll back the enrichment commit, and every exception is caught and logged at ERROR.

Sellers enriched before scoring existed, or whose scoring pass failed, are recovered by the Seller.ScorePending handler (lambdas/enrichment_scorer/events/score_pending.py):

  • Selects up to 10 sellers via get_pending_scoring(): enrichedAt IS NOT NULL and payoutPainScore IS NULL.

  • Runs only the scoring pass, using seller.note as the enrichment context.

  • Each seller is independent; one failure logs and the loop continues.

Its EventBridge rule (ScorePendingBackfill) is currently commented out in lambdas/template.yaml.

Changing the formula

The formula lives entirely in SELLER_SCORING_PROMPT in lambdas/enrichment_scorer/agents/prompts.py. Changing it changes future scores only — existing rows are not recomputed, and get_pending_scoring() skips any row that already has a score. To rescore a cohort, null the column for those sellers and let the backfill pick them up:

UPDATE sellers SET "payoutPainScore" = NULL
WHERE "enrichedAt" IS NOT NULL AND "primaryCategory" = 'Electronics';

Then invoke Seller.ScorePending repeatedly (10 sellers per run) or raise _BATCH_SIZE in the handler.