Second approval (the checker screen)
Second approval is the single most-used screen for checkers. It is designed to minimise mistakes — the payload is read-only, the context block surfaces the seven most important risk signals, and the OTP gate makes deniability impossible.

Anatomy of the form
The form is split into a header, three side-by-side panels, and a footer action bar.
- Header — request ID, rule code, maker name with photo, submitted timestamp, amount in KES
- Panel 1 (Payload) — exact data that will be written to the database if approved
- Panel 2 (Context) — KYC status, blacklist hits, related records, member history
- Panel 3 (Maker's note) — mandatory free-text from the maker explaining the request
- Footer — Approve, Reject, Re-assign, Save for later (parks it without action)
Payload panel in detail

What the panel shows depends on the rule. Examples:
| Rule | Payload fields shown |
|---|---|
| LOAN_DISBURSE | Member, Loan product, Principal, Tenor, Disbursement channel, M-Pesa number / bank account, Disbursement date |
| LOAN_WRITEOFF | Loan reference, Outstanding principal, Interest, Penalties, Recovery attempts log, Board resolution attachment |
| SAV_ADJUST | Savings account, Current balance, Adjustment amount (+/-), New balance, GL account to charge |
| MBR_BLACKLIST | Member, Reason code (FRAUD/AML/OFAC/INTERNAL), Evidence attachment, Linked exposures KES |
| GL_MANUAL | Journal, Debit account, Credit account, Amount, Memo, Period |
Context panel — seven signals
The context panel always shows seven signals regardless of rule:
- KYC status badge (Green / Amber / Red) with last-verified date
- Blacklist hits — internal + Metropol + TransUnion + OFAC/UN sanctions
- Open loans count and total exposure
- Savings balance (locked vs available)
- Last 30 days transaction count
- PAR status (Performing / Watch / Substandard / Doubtful / Loss)
- STR/CTR flags from the AML engine in the last 90 days
OTP flow
- Click Approve
- A confirmation modal shows a one-line summary: "Disburse KES 75,000 to Mary Mutua (M-Pesa +254723XXXXXX)?"
- Click Confirm — OTP is dispatched via Africa's Talking
- Enter the 6-digit OTP
- Click Verify — the approval and the underlying action commit in a single PostgreSQL transaction
- Success toast: "Approved — DCR-2026-01102 executed"
- Request disappears from Pending and appears in Audit
Reject flow
Rejection is OTP-free because it does not move money. But the reason field is mandatory and is shown to the maker verbatim — be specific and professional. Bad: "No". Good: "M-Pesa number does not match member record; please verify and resubmit".
Rejection reasons are categorised and reported on the Audit page — repeated rejection reasons surface training needs for makers.
Re-assigning to another checker
If you are on lunch / leaving / not the right person, click Re-assign. Pick another checker from the dropdown. The request moves to their queue and you drop off the audit trail (a small entry is logged that you re-assigned). Re-assignment is limited to two hops per request — after that, escalation kicks in to the CRO.
Save for later
Use this when you've started reviewing but need more info ("let me phone the maker"). The request stays in your queue and shows a "Held by Aisha" badge to other checkers so they don't double-handle it. The hold expires after 30 minutes.
Worked scenarios
Scenario — CFO signing off a KES 850,000 write-off
| Character | Role |
|---|---|
| Peter Otieno | Collections Officer (maker) |
| Florence Achieng | CFO (checker 1) |
| Samuel Kibet | CEO (checker 2 — three-person rule) |
| Mary Mutua | Defaulted borrower |
Timeline
- Day 1, 14:00: Peter submits write-off for KES 850,000 outstanding on Mary's loan (Two rules fire: LOAN_WRITEOFF + LOAN_WRITEOFF_BIG (>KES 500k → 2 checkers))
- Day 1, 14:01: Florence receives notification (Pending DCR-2026-02144)
- Day 1, 14:10: Florence opens form — verifies recovery log shows 14 follow-ups, 3 site visits, 1 demand letter from advocates (Maker's note: "Board resolution Q1-2026/17 attached. Member relocated to Tanzania, no traceable address.")
- Day 1, 14:14: Florence clicks Approve — OTP 738241 (First signature captured)
- Day 1, 14:15: Request now needs Samuel's signature (min approvers = 2) (Notified Samuel)
- Day 1, 16:30: Samuel reviews, confirms board resolution, Approves — OTP 092374 (Both OTPs verified)
- Day 1, 16:31: Write-off posts: Dr Loan loss provision KES 850,000 / Cr Loan principal KES 850,000 (GL entry JV-2026-04412 created)
- Day 1, 16:31: Audit entry records both signatures + board resolution attachment (Immutable)
Outcome — Write-off properly authorised with three-person rule. External auditors confirm at year-end.
Reference
Footer action buttons
| Button | What it does | Requires OTP? | Notifies maker? |
|---|---|---|---|
| Approve | Commits the underlying action | Yes | Yes (e-mail + inbox) |
| Reject | Discards with reason | No | Yes (with reason verbatim) |
| Re-assign | Hands to another checker | No | No (silent) |
| Save for later | Holds for 30 min | No | No |
| Print preview | PDF of the request for paper sign-off | No | No |
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Approve clicked but no OTP arrives | Africa's Talking outage or stale phone number. | Wait 60s, click Resend. If still nothing, the user's phone in res.users may be wrong; IT updates it; failing that, IT uses OTP > Bypass (audited). |
| OTP entered but "Invalid OTP" error | OTP expired (>10 min) or typo. | Click Resend OTP for a fresh code. Three wrong attempts locks the OTP for 15 min. |
| Underlying action fails after OTP verified (e.g. M-Pesa down) | Daraja API error or insufficient float. | Approval rolled back automatically. Request returns to pending. Resolve the M-Pesa issue and re-approve (new OTP required). |
| Re-assign dropdown empty | No other user in the rule's checker group / authorised users list. | CRO must add more checkers under Rules > Authorised users. |
| Save for later button missing | Request has already been held by another checker. | Coordinate verbally; once their 30-min hold expires it becomes available again. |

