GH Waiver Campaign Performance

Mar 17 – Apr 10, 2026  ·  Source: ml.loan_info_tbl, LOAN_ACCOUNT_HISTORY, COLLECTION_DAILY, INFOBIP_SMS

Overall Summary

Total Loans
7,930
Fully Paid (discount applied)
481
6.1% conversion
Total Collected
GHS 4.68M
all 7,930 loans incl. partial
Original Debt Exposed
GHS 44.5M
Due After Discount
GHS 21.4M
min repayment to qualify
Yield vs. Original Debt
10.5%
collected / total_due_amount
Structural problem: 4,934 loans (62% of campaign) had never paid on the loan before the campaign. They generated only GHS 366K on GHS 26.4M original debt — 1.4% yield. The 830 loans that paid within 30 days before the campaign generated GHS 2.05M — 5.6× more from 17% of the volume.

Performance by DPD

DPD BandLoansFully PaidConversion CollectedOriginal DebtYield
30–592,939372 12.7% GHS 2,688KGHS 17,121K15.7%
60–891,72166 3.8% GHS 745KGHS 9,350K8.0%
90–1191,90333 1.7% GHS 831KGHS 10,575K7.9%
120+1,36710 0.7% GHS 421KGHS 7,434K5.7%

DPD 120+: GHS 4.55M discount offered, GHS 421K collected. For every GHS 1 collected, ~GHS 10.8 in discount was ignored.

As expected, the conversion rate for DPD 30–59 was the highest. This aligns with expectations, as this group was exposed to the discount for the first time during this campaign.

Performance by Segment (Prior Payment × DPD)

Payment HistoryDPDLoansPaid ConversionCollectedOriginal DebtYield
Paid on campaign day30–59 824251.2% GHS 257KGHS 538K47.7%
Paid 1–7 days before30–59 3048929.3% GHS 734KGHS 1,992K36.9%
Paid 1–7 days before60–89 591423.7% GHS 134KGHS 345K38.8%
Paid 1–7 days before90–119 35411.4% GHS 56KGHS 187K29.8%
Paid 8–30 days before30–59 52614026.6% GHS 1,061KGHS 3,225K32.9%
Paid 8–30 days before60–89 196157.7% GHS 255KGHS 1,162K22.0%
Paid 8–30 days before90–119 11476.1% GHS 195KGHS 709K27.5%
Paid 31d+ before30–59 385307.8% GHS 390KGHS 2,424K16.1%
Paid 31d+ before60–89 329195.8% GHS 283KGHS 1,914K14.8%
Paid 31d+ before90–119 537101.9% GHS 532KGHS 3,111K17.1%
Never paid30–59 1,642714.3% GHS 247KGHS 8,941K2.8%
Never paid60–89 1,133161.4% GHS 62KGHS 5,900K1.0%
Never paid90–119 1,212121.0% GHS 39KGHS 6,549K0.6%
Never paid120+ 94720.2% GHS 18KGHS 4,992K0.4%
The conversion rate was highest across all DPD groups for those who had paid within the last 1–7 days.
On the other hand, 62% of the campaign (almost 5,000 loans) had never paid before. They generated GHS 366K — we can assume that without the campaign, these loans would not pay.

Additional Signals

By Balance Size (Due After Discount)

Balance Band (GHS)LoansFully PaidConversion
<5003311 33.3%
500–1K34455 16.0%
1K–2K2,848206 7.2%
2K–5K4,067202 5.0%
5K+6387 1.1%
Achievable amount drives action. Loans under GHS 1K converted at 16–33% — 3–6× the campaign average. Loans over GHS 2K (71% of volume) converted at ≤5%. High balance is the primary friction point: borrowers willing but unable to pay lump sum. Strongest case for tiered instalment offers at 2K+.

By LN Group

LNLoansPaidRate
4–631692.8%
7–101,994743.7%
11–152,3811265.3%
16–201,325856.4%
21+1,9141879.8%

By Collection Call Response

SegmentLoansPaidRate
Queue answered2,33325911.1%
IVR only1,146897.8%
Attempted, no answer4,4501323.0%

All IVR-only loans also had agent attempts — no loans received purely IVR.

SMS Response (INFOBIP_SMS — 6 discount batches)

SMS BatchSMS SentLoans Paid Within 1 Day
Mar 18 — "Second chance"7,916100
Mar 20 — "Discount active"7,81432
Mar 27 — "Discount reminder"7,69437
Apr 17,04436
Apr 6 — Easter SMS6,31123
Apr 10 — Last day6,22857
Total unique (any batch)285 of 481 (59%)
59% of fully paid loans paid within 1 day of an SMS. First batch (Mar 18) drove the most — borrowers waiting for the offer. Last-day urgency (Apr 10) was second. The first SMS is the highest-leverage send.

What Worked & What Didn't

✓ Worked

  • DPD 30–59 + paid recently: 27–51% conversion — discount caught active repayers and closed loans
  • LN 21+: 9.8% overall, 18% at DPD 30-59 — loyal borrowers respond to relationship + offer
  • First SMS batch: 100 loans paid within 1 day — strongest single channel action
  • Agent calls: queue-answered loans converted at 11.1% — 3.7× unanswered
  • Small balances (<GHS 1K due): 16–33% conversion (<500 = 33.3%, 500–1K = 16.0%) — achievable amount drives action; 3–6× campaign average

✗ Didn't Work

  • Never paid + DPD 60+: 0.4–1.0% yield — discount wasted on 3,292 loans
  • DPD 120+: 0.7% conversion — GHS 10.8 discount ignored per GHS 1 collected
  • LN 4–6 + DPD 60+: near-zero — no relationship equity, no response
  • 4,450 unanswered loans: 56% of campaign never got agent contact — massive unrealised upside
  • Flat 50% discount for high-intent borrowers: recent payers (27–51% conversion) don't need the full 50% to close — Tiers 1+2 can be served at 15–35% with similar conversion, recovering GHS 400–500K more per campaign

Strategy for Next Campaign

Tier 1 — Active repayers
Paid ≤7 days before campaign — all DPD bands
20–35%
Recent payment signal dominates DPD — strong conversion across all bands:
DPD 30–59: 29.3% → 20% discount  ·  DPD 60–89: 23.7% → 25% discount  ·  DPD 90–119: 11.4% → 35% discount
Current rule misses 94 high-intent loans (DPD 60–119) who receive 50% unnecessarily.
Tier 2 — Recent payers
Paid 8–30 days before — DPD 30–59 and DPD 60–89 only
30–40%
Recency signal holds through DPD 60–89 but fades at 90+:
DPD 30–59: 26.6% → 30–35% discount  ·  DPD 60–89: 7.7% → 40% discount
LN 21+ already captured here via recency. LN 21+ with last payment 31d+ ago = 8.5% / 7.0% — only marginally above campaign average, not enough to pull out of Tier 3.
Tier 3 — Everyone else
Never paid · paid 8–30d + DPD 90–119 · paid 31d+ · DPD 120+
50%
Stay in campaign with current 50% discount. Covers cold starters (4–8% conversion), deep delinquent (1–2%), and segments where payment recency signal is lost. Low conversion expected — 50% is the floor offer needed to extract any recovery.
Financial impact estimate: Tiers 1+2 cover ~1,120 loans (Tier 1: 304+59+35=398 · Tier 2: 526+196=722). Reducing average discount from 50% to ~30% (Tier 1 weighted ~22%, Tier 2 weighted ~34%) with conservative 15% conversion loss → +GHS 400–500K incremental recovery per campaign. Tier 3 (6,810 loans, majority of portfolio) unchanged at 50%. No exclusions — everyone stays in campaign.

Decision logic for next campaign

-- Tier 1: paid within 7 days (all DPD)
IF last_pmt ≤ 7d AND dpd 30–59 → 20% discount
IF last_pmt ≤ 7d AND dpd 60–89 → 25% discount
IF last_pmt ≤ 7d AND dpd 90–119 → 35% discount

-- Tier 2: recent payers (DPD ≤89) OR loyal borrowers (DPD ≤89)
IF last_pmt 8–30d AND dpd 30–59 → 30–35% discount
IF last_pmt 8–30d AND dpd 60–89 → 40% discount

-- Tier 3: everyone else
ELSE → 50% discount (current campaign standard)

Database: GHANA_PROD  ·  Campaign cohort filter: loan_discount_start_date::date = '2026-03-17'  ·  Fully paid: loan_discount_amount IS NOT NULL

Methodology notes: Use loan_discount_start_date (not end_date) for cohort — gives 7,930 loans matching LAH exactly. · Prior payment recency must use LAST_REPAYMENT_DATE from LAH at PERIOD = '2026-03-17', not from loan_info_tbl (post-campaign field updated on settlement). · Do not join INFOBIP_SMS + MANUAL_CALLS_PRIORITY_DAILY in same query — triggers MCP false write-detection. Use date-based matching instead.
1 — Overall Summary
SELECT COUNT(DISTINCT loan_id) AS total_loans, COUNT(DISTINCT CASE WHEN loan_discount_amount IS NOT NULL THEN loan_id END) AS fully_paid_loans, ROUND(100.0 * COUNT(DISTINCT CASE WHEN loan_discount_amount IS NOT NULL THEN loan_id END) / COUNT(DISTINCT loan_id), 1) AS conversion_pct, ROUND(SUM(total_due_amount), 0) AS due_before_discount, ROUND(SUM(loan_discount_min_repayment_amount), 0) AS due_after_discount, ROUND(SUM(total_repayment_amount), 0) AS total_collected, ROUND(100.0 * SUM(total_repayment_amount) / NULLIF(SUM(total_due_amount), 0), 1) AS yield_pct FROM GHANA_PROD.ml.loan_info_tbl WHERE loan_discount_start_date::date = '2026-03-17'
2 — Performance by DPD Band
SELECT CASE WHEN lah.DAYS_LATE < 60 THEN '30-59' WHEN lah.DAYS_LATE < 90 THEN '60-89' WHEN lah.DAYS_LATE < 120 THEN '90-119' ELSE '120+' END AS dpd_band, COUNT(*) AS total_loans, COUNT(CASE WHEN lit.loan_discount_amount IS NOT NULL THEN 1 END) AS fully_paid, ROUND(100.0 * COUNT(CASE WHEN lit.loan_discount_amount IS NOT NULL THEN 1 END) / COUNT(*), 1) AS conversion_pct, ROUND(SUM(lit.total_repayment_amount), 0) AS total_collected, ROUND(SUM(lit.total_due_amount), 0) AS original_debt, ROUND(100.0 * SUM(lit.total_repayment_amount) / NULLIF(SUM(lit.total_due_amount), 0), 1) AS yield_pct FROM GHANA_PROD.ml.loan_info_tbl lit LEFT JOIN GHANA_PROD.SEMANTIC_LAYER.LOAN_ACCOUNT_HISTORY lah ON lit.loan_id = lah.LOAN_ID AND lah.PERIOD = '2026-03-17' AND lah.LOAN_DISCOUNT_START_DATE = '2026-03-17' WHERE lit.loan_discount_start_date::date = '2026-03-17' GROUP BY 1 ORDER BY MIN(lah.DAYS_LATE)
3 — Performance by Segment (Prior Payment Recency × DPD)
SELECT CASE WHEN DATEDIFF('day', lah.LAST_REPAYMENT_DATE::DATE, '2026-03-17'::DATE) IS NULL THEN 'D_never_paid' WHEN DATEDIFF('day', lah.LAST_REPAYMENT_DATE::DATE, '2026-03-17'::DATE) <= 0 THEN 'A_campaign_day' WHEN DATEDIFF('day', lah.LAST_REPAYMENT_DATE::DATE, '2026-03-17'::DATE) <= 7 THEN 'B_1-7d_before' WHEN DATEDIFF('day', lah.LAST_REPAYMENT_DATE::DATE, '2026-03-17'::DATE) <= 30 THEN 'C_8-30d_before' ELSE 'E_31d+' END AS pmt_seg, CASE WHEN lah.DAYS_LATE < 60 THEN '30-59' WHEN lah.DAYS_LATE < 90 THEN '60-89' WHEN lah.DAYS_LATE < 120 THEN '90-119' ELSE '120+' END AS dpd_band, COUNT(*) AS total_loans, COUNT(CASE WHEN lit.loan_discount_amount IS NOT NULL THEN 1 END) AS fully_paid, ROUND(100.0 * COUNT(CASE WHEN lit.loan_discount_amount IS NOT NULL THEN 1 END) / COUNT(*), 1) AS conversion_pct, ROUND(SUM(lit.total_repayment_amount), 0) AS total_collected, ROUND(SUM(lit.total_due_amount), 0) AS original_debt, ROUND(SUM(lit.loan_discount_min_repayment_amount), 0) AS due_after_discount, ROUND(100.0 * SUM(lit.total_repayment_amount) / NULLIF(SUM(lit.total_due_amount), 0), 1) AS yield_pct FROM GHANA_PROD.ml.loan_info_tbl lit LEFT JOIN GHANA_PROD.SEMANTIC_LAYER.LOAN_ACCOUNT_HISTORY lah ON lit.loan_id = lah.LOAN_ID AND lah.PERIOD = '2026-03-17' AND lah.LOAN_DISCOUNT_START_DATE = '2026-03-17' WHERE lit.loan_discount_start_date::date = '2026-03-17' GROUP BY 1, 2 ORDER BY 1, 2
4 — Performance by LN Group
SELECT CASE WHEN lit.LN BETWEEN 4 AND 6 THEN 'LN_4-6' WHEN lit.LN BETWEEN 7 AND 10 THEN 'LN_7-10' WHEN lit.LN BETWEEN 11 AND 15 THEN 'LN_11-15' WHEN lit.LN BETWEEN 16 AND 20 THEN 'LN_16-20' ELSE 'LN_21+' END AS ln_band, COUNT(*) AS total_loans, SUM(CASE WHEN lit.loan_discount_amount IS NOT NULL THEN 1 ELSE 0 END) AS fully_paid, ROUND(100.0 * SUM(CASE WHEN lit.loan_discount_amount IS NOT NULL THEN 1 ELSE 0 END) / COUNT(*), 1) AS conversion_pct, ROUND(SUM(lit.total_repayment_amount), 0) AS total_collected, ROUND(SUM(lit.total_due_amount), 0) AS due_before_discount, ROUND(100.0 * SUM(lit.total_repayment_amount) / NULLIF(SUM(lit.total_due_amount), 0), 1) AS yield_pct FROM GHANA_PROD.ml.loan_info_tbl lit WHERE lit.loan_discount_start_date::date = '2026-03-17' GROUP BY 1 ORDER BY MIN(lit.LN)
5 — Performance by Collection Call Response
WITH base AS ( SELECT lit.loan_id, CASE WHEN lit.loan_discount_amount IS NOT NULL THEN 1 ELSE 0 END AS fully_paid, lit.total_repayment_amount AS collected, lit.total_due_amount AS due_before_discount FROM GHANA_PROD.ml.loan_info_tbl lit WHERE lit.loan_discount_start_date::date = '2026-03-17' ), calls AS ( SELECT LOAN_ID, SUM(NUM_QUEUE_ANSWERED) AS queue_ans, SUM(NUM_IVR_ANSWERED) AS ivr_ans, SUM(NUM_QUEUE_ATTEMPTS) AS queue_att, SUM(NUM_IVR_ATTEMPTS) AS ivr_att FROM GHANA_PROD.SEMANTIC_LAYER.COLLECTION_DAILY WHERE "Date" BETWEEN '2026-03-17' AND '2026-04-10' GROUP BY LOAN_ID ) SELECT CASE WHEN COALESCE(c.queue_ans, 0) > 0 THEN 'QUEUE_ANSWERED' WHEN COALESCE(c.ivr_ans, 0) > 0 THEN 'IVR_ONLY' WHEN (COALESCE(c.queue_att, 0) + COALESCE(c.ivr_att, 0)) > 0 THEN 'ATTEMPTED_NO_ANSWER' ELSE 'NO_CONTACT' END AS call_segment, COUNT(*) AS total_loans, SUM(b.fully_paid) AS fully_paid, ROUND(100.0 * SUM(b.fully_paid) / COUNT(*), 1) AS conversion_pct, ROUND(SUM(b.collected), 0) AS total_collected, ROUND(100.0 * SUM(b.collected) / NULLIF(SUM(b.due_before_discount), 0), 1) AS yield_pct FROM base b LEFT JOIN calls c ON b.loan_id = c.LOAN_ID GROUP BY 1 ORDER BY conversion_pct DESC
6 — SMS Batch Dates
SELECT SENT_AT::date AS sms_date, COUNT(*) AS sms_sent FROM GHANA_PROD.DATA.INFOBIP_SMS WHERE SENT_AT::date BETWEEN '2026-03-17' AND '2026-04-10' AND (LOWER(TEXT) LIKE '%discount%' OR LOWER(TEXT) LIKE '%waiver%') GROUP BY 1 ORDER BY 1
7 — SMS Attribution (Paid Within 1 Day of Any Batch)
WITH sms_dates AS ( SELECT SENT_AT::date AS sms_date FROM GHANA_PROD.DATA.INFOBIP_SMS WHERE SENT_AT::date BETWEEN '2026-03-17' AND '2026-04-10' AND (LOWER(TEXT) LIKE '%discount%' OR LOWER(TEXT) LIKE '%waiver%') GROUP BY 1 ), paid_loans AS ( SELECT loan_id, LAST_REPAYMENT_DATE::date AS repaid_dt FROM GHANA_PROD.ml.loan_info_tbl WHERE loan_discount_start_date::date = '2026-03-17' AND loan_discount_amount IS NOT NULL AND LAST_REPAYMENT_DATE IS NOT NULL ) SELECT COUNT(DISTINCT p.loan_id) AS paid_within_1d_any_sms, (SELECT COUNT(*) FROM paid_loans) AS total_fully_paid FROM paid_loans p JOIN sms_dates s ON DATEDIFF('day', s.sms_date, p.repaid_dt) BETWEEN 0 AND 1