Cash policy
Cash policy is where you write down — once, centrally — the limits and approval matrices that every till, vault, CIT trip, and EOD reconciliation enforces. Set this before anyone touches cash; revisit quarterly.

What a cash policy contains
A cash policy (`mfi.cash.policy`) groups all the thresholds for one or more branches. Most institutions run two or three policies: one for urban branches, one for rural, optionally one for a flagship/HQ branch with elevated limits. Each branch has exactly one active policy at a time, with a dated history of changes for audit.
| Group | Field | Description |
|---|---|---|
| Till | till_operating_max | Soft-warn threshold (default KES 200k) |
| Till | till_hard_max | Block threshold (default KES 250k) |
| Till | default_float | Opening float drawn from vault (default KES 50k) |
| Vault | vault_overnight_max | Cannot close above (default KES 5M urban / 2M rural) |
| Vault | vault_min_reserve | Below this triggers replenishment (default KES 200k) |
| Single transaction | cash_in_max_per_txn | Above triggers STR/FRC queue (default KES 1M) |
| Single transaction | cash_out_max_per_txn | Above blocks at front-counter, manager approval (default KES 500k) |
| KRA | etims_threshold | Above emits ETIMS payload (default KES 100k) |
| Variance | var_auto_incident | Above this creates incident automatically (default KES 1) |
| Variance | var_supervisor_cap | Supervisor can close incident up to (default KES 5k) |
| Variance | var_manager_cap | Branch manager can close up to (default KES 50k) |
| Variance | var_regional_cap | Regional manager can close up to (default KES 250k) |
| CIT | cit_max_per_bag | Max bag value (default KES 2M) |
| CIT | cit_carrier_liability_cap | Max per carrier per route (default KES 10M) |
Approval matrix

Beyond numeric thresholds, the policy defines who can authorise which override:
| Action | Default authoriser | Default escalation |
|---|---|---|
| Override till hard max | Branch manager | Regional manager |
| Override vault overnight max | Regional manager | Head of treasury |
| Authorise vault → till sweep > KES 500k | Branch manager | Regional manager |
| Authorise emergency vault injection | Head of treasury | CFO |
| Force-close orphan till session | Branch manager | Regional manager |
| Add temporary vault co-holder | Regional manager | Head of treasury |
| Suspend a cash policy | Head of treasury | CFO |
| Edit a published cash policy | Head of treasury | CFO |
Policy lifecycle
- **Draft** — head of treasury or branch operations drafts a new policy or edits a copy of an existing one.
- **Review** — compliance officer reviews and adds comments.
- **Approve** — CFO or head of treasury approves with electronic sign-off.
- **Schedule** — set effective date (typically next branch-open).
- **Active** — policy enforces on every relevant transaction.
- **Superseded** — replaced by a new version. Historical version remains for audit.
- **Suspended** — only for emergency response (e.g. raise overnight max during a holiday surge); requires CFO sign-off.
Publishing and effective date
A policy does not take effect at the moment of approval — it takes effect at the configured effective date, which must be a future branch-open. This prevents mid-day policy switches that would invalidate the morning's transactions. The system holds approved-but-not-yet-effective policies in a queue and swaps at the next branch open.
Branch overrides
Sometimes a single branch needs an exception — a flagship branch with 2× transaction volume, a rural branch with no CIT capability that needs a lower overnight max. Two mechanisms:
- **Branch override field** — for a single threshold, set the override on the branch record itself. Visible in the policy report as 'overridden'.
- **Branch-specific policy** — for multiple thresholds, create a dedicated policy and link only that branch to it. Cleaner for audit.
Policy effectiveness report
Run quarterly from Cash → Reports → Policy Effectiveness. Shows every threshold breach across the institution for the period, by branch, by type, with the resolution (override / declined / escalated). Use this to recalibrate — if 30% of disbursements at one branch are breaching the till hard max, raise that branch's max or change the disbursement channel.
Worked scenarios
Scenario — Raising overnight max for end-of-month surge
| Character | Role |
|---|---|
| Florence Achieng | Branch manager, Karen |
| Aisha Hassan | Head of treasury, HQ |
| Peter Otieno | CFO |
Timeline
- Day 1, 16:00: Florence cannot close branch session — vault at KES 5.4M, KES 400k above overnight max. (Branch session close blocked)
- Day 1, 16:05: Florence emails Aisha to request emergency CIT pickup. Aisha confirms G4S can't dispatch until tomorrow 09:00. (—)
- Day 1, 16:15: Aisha drafts a temporary policy raise for Karen branch from KES 5M to KES 7M, effective from now through Day 14 (peak season). (Policy draft created)
- Day 1, 16:20: Aisha submits to Peter for approval. (Policy in review)
- Day 1, 16:30: Peter signs off electronically with reason: 'Christmas surge, CIT capacity constrained'. Insurance underwriter cc'd. (Policy ACTIVE)
- Day 1, 16:35: Florence closes branch session at KES 5.4M, now within limit. (Branch session CLOSED)
- Day 14, 23:59: Temporary policy expires automatically. Original KES 5M policy resumes. (Policy SUPERSEDED)
Outcome — Branch closed legally and insurance was renotified; surge handled without a panic CIT call-out.
Reference
Policy states
| State | Meaning |
|---|---|
| draft | Being authored |
| review | With compliance for review |
| approved | Signed off, not yet effective |
| active | Currently enforcing |
| superseded | Replaced by a newer version |
| suspended | Temporarily disabled — emergency |
| archived | Historical, no longer reachable |
Shipped defaults — urban vs rural
| Threshold | Urban default | Rural default |
|---|---|---|
| Till operating max | KES 200,000 | KES 100,000 |
| Till hard max | KES 250,000 | KES 150,000 |
| Default float | KES 50,000 | KES 30,000 |
| Vault overnight max | KES 5,000,000 | KES 2,000,000 |
| Vault min reserve | KES 200,000 | KES 100,000 |
| Single cash-in | KES 1,000,000 | KES 500,000 |
| Single cash-out | KES 500,000 | KES 250,000 |
| KRA ETIMS | KES 100,000 | KES 100,000 |
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Policy approved but limits still old at the branch | Effective date not yet reached | Check effective_date on the policy. Limits swap at next branch open after effective date. |
| Compliance officer cannot approve — 'approval matrix change requires CFO' | Approval matrix section was edited, which needs CFO sign-off | Either revert the approval matrix changes and let compliance approve the thresholds only, or route to CFO. |
| Branch override not visible in policy view | Branch override fields are on res.branch, not on the policy form | Open the branch record and check the Cash Override panel. Run the Policy Effectiveness report to see all overrides in one place. |
| Temporary policy did not auto-expire | Expiry cron job paused (multi-company configs sometimes skip) | Run the cron 'mfi.cash.policy.expire_temporary' manually from Settings → Technical → Scheduled Actions, then unpause. |
| Variance incident threshold of KES 1 is annoying — tiny variances flood incidents | Strict-by-default | Do not raise the threshold; tighten teller counting. CBK examiners specifically check that every variance has a case. Instead, set auto-close at KES 50 with supervisor sign-off — the incident still exists for audit but does not consume time. |

