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 |
|
|
4096 |
|
3 |
Tools |
none — |
Structured output |
|
|
|
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 |
|---|---|
|
|
|
|
|
The marketplace string; multiple platforms are comma-separated |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
|---|---|---|
|
0 |
Nothing meaningful locked up |
|
8 |
Small; low absolute pressure |
|
20 |
Sweet spot — meaningful cash, limited access to credit |
|
15 |
Larger; more likely to have financing options |
|
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 |
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 |
|
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 |
|
|
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 |
2 |
Too new, unproven |
6–12 months |
6 |
|
1–3 years |
10 |
Sweet spot |
3–7 years |
7 |
|
|
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 NULLandpayoutPainScore IS NULL. -
Runs only the scoring pass, using
seller.noteas 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.