Group finance runs on one exhausted volunteer: the treasurer who chases contributions on WhatsApp, then calls the stragglers one by one from their personal number. This guide replaces that call list with a voice broadcast from the group's own line — a contribution reminder ahead of meeting day with a press-1 acknowledgement, a table-banking meeting notice, and an employer-level remittance chase for SACCOs. It closes with the arithmetic: a 30-member chama's monthly reminder round costs about the price of two sodas.
Per the FinAccess Household Survey 2024 (CBK, KNBS and FSD Kenya, December 2024), 30.7% of Kenyan adults belong to an informal group — a chama, a merry-go-round, a table-banking circle. That is millions of groups, and every one of them has a treasurer doing the same unpaid job in the days before meeting day: post the reminder in the WhatsApp group, watch who reacts, then start calling the members whose M-Pesa hasn't landed.
The regulated end of group finance has the same problem at industrial scale. SASRA's Sacco Supervision Annual Report 2024 (released September 2025) counts 7.39 million members in regulated SACCOs — and two numbers that are really calling campaigns waiting to happen: non-performing loans at 8.2% of gross loans (up from 7.5% the year before), and KSh 3.49 billion in member contributions that employers deducted from paychecks but never remitted, spread across 85 SACCOs and more than 55,000 members.
In both cases the money side is already digital — M-Pesa for the chama, payroll deduction for the SACCO. It's the communication side that is still a human with a phone and a list. That is exactly the layer a voice API automates.
There's a reason the treasurer's job is dreaded, and it isn't the dialing. When Mary calls Wanjiru about a late contribution, it stops being group business and becomes Mary-versus-Wanjiru. The debtor feels singled out by a friend; the treasurer spends social capital with every call; and next quarter nobody wants the treasurer seat.
A call from the group's own line changes the social physics:
The nudge is depersonalized. "Okoa Savings Group is calling" carries the weight of the whole group, not one member's opinion. Nobody has to be the bad guy.
It's consistent. Every member hears the same words, the same tone, the same paybill number — no accusations of favouritism, no softening for friends.
It's recognizable. Members save the group's number once. A call from it means group business, which is precisely why it gets answered when a random unknown number would not.
It survives handover. When the treasurer role rotates, the number, the script, and the call history stay with the group.
A dedicated local number costs KES 100/month ex. VAT (KES 116 incl. VAT) as of 2026-06-30 — see /pricing for current rates — and is claimed instantly through the API or dashboard, with the first month prorated. For a chama, that's less than most groups spend on meeting-day sodas.
The core flow: three days before meeting day, call every member whose contribution hasn't been recorded, remind them of the amount and deadline, and let them acknowledge with one key press.
Routing lives on the number, so point it at your handler once:
Then place the round with the Node SDK. The idempotency key matters more here than almost anywhere: if your script crashes halfway and you re-run it, members must not get called twice about the same month's contribution. The SDK generates a key for you when you omit it — here you want a business key, so pass it explicitly.
npm install @sautikit/node@0.2.0
import { SautikitClient } from "@sautikit/node";const client = new SautikitClient({ apiKey: process.env.SAUTIKIT_API_KEY });const members = await getUnpaidMembers("2026-10"); // your ledger, Chamasoft export, or a spreadsheetfor (const m of members) { const { call_id, status } = await client.calls.create({ from: "+254709000123", // the group's own number to: [m.msisdn], idempotencyKey: `okoa-reminder:2026-10:${m.msisdn}`, }); // status 202: dialing starts}
When each member answers, Sautikit POSTs to your voice_callback_url, and your response — a voice actions document — runs the script. Use getDigits to collect the acknowledgement; when the member presses a key, the platform POSTs back to the same URL with the Digits field populated, and your handler dispatches on it:
import express from "express";import { sauti } from "@sautikit/node";const app = express();app.use(express.json()); // voice callbacks arrive as JSONapp.post("/voice/reminder", (req, res) => { const digits = req.body.Digits || ""; if (!digits) { // Call answered: play the reminder, collect one digit. // timeout is in SECONDS; the nested prompt plays while the platform listens. return res.json( sauti .getDigits({ timeout: 8, numDigits: 1 }, [ { say: { text: "Habari! This is Okoa Savings Group. This month's contribution of " + "two thousand shillings is due before Saturday's meeting. " + "Kama umeshatuma, bonyeza moja. If you will pay by Saturday, press 2. " + "To hear the paybill number, press 9.", language: "en-KE", }, }, ]) .say("Asante. See you Saturday.", { language: "en-KE" }) .hangup(), ); } if (digits === "1") { markAcknowledged(req.body.clientDialedNumber, "already_paid"); return res.json( sauti.say("Asante sana. Tutaonana Jumamosi.", { language: "en-KE" }).hangup(), ); } if (digits === "2") { markAcknowledged(req.body.clientDialedNumber, "will_pay_by_meeting"); return res.json( sauti .say("Thank you. We will see the contribution by Saturday.", { language: "en-KE" }) .hangup(), ); } if (digits === "9") { // Paybill readback: digits spaced so they can be written down; loop repeats it. return res.json( sauti .say("Paybill number: 5 2 2 5 3 3. Account name: OKOA, then your member number.", { language: "en-KE", loop: 2, }) .say("Asante. Tutaonana Jumamosi.", { language: "en-KE" }) .hangup(), ); } // Unrecognised key: replay the menu. return res.json( sauti.redirect("https://chama.example.com/voice/reminder", { method: "POST" }), );});
Two details worth copying. First, the paybill readback: say the digits spaced ("5 2 2 5 3 3", not "522533") so the text-to-speech reads them one at a time, and use loop: 2 so nobody has to fumble for a pen on the first pass. Second, the acknowledgement isn't decoration — "pressed 1" versus "pressed 2" versus "didn't answer" is the treasurer's meeting-day briefing, generated automatically. The same press-1 pattern drives full promise-to-pay flows in commercial collections; here it stays deliberately lighter.
If the treasurer would rather not run code at all, this whole round — check the wallet, place the calls, review who acknowledged — can also be driven from Claude through the Sautikit MCP server.
Table banking adds a second call type: the meeting notice. Contributions are placed on the table and lent out the same day, so a member who misses the meeting misses the money — attendance is the product. Many groups also skew toward members who answer calls far more reliably than they read texts.
The flow is the reminder broadcast minus the collection ask: one call the evening before, stating venue, time, and what to carry. No getDigits needed — a 20-second say and a hangup. Send it to the full member list rather than just the unpaid, and key it per meeting (okoa-meeting:2026-10-17:{msisdn}) so a re-run never double-calls. At 20 seconds, each call costs 20 × KES 0.05 = KES 1 — the arithmetic barely registers.
Here's the mistake to avoid: treating SASRA's KSh 3.49 billion in unremitted contributions as a member-delinquency problem. It isn't. That money was already deducted from 55,000+ members' paychecks — the members paid. It's the employers who failed to forward the deductions to the SACCO. Calling members about it would be both useless and unfair.
The right campaign is employer-level and looks more like accounts receivable than collections:
The list is delinquent employer payroll offices — dozens or hundreds of entries, not tens of thousands.
The script is administrative, not emotional: "This is Ukulima SACCO. Our records show September member deductions have not been received. Press 1 to hear the remittance account details, press 2 to request a statement by email."
The cadence is a monthly cycle: a courtesy call a few days after the remittance deadline, a firmer follow-up two weeks later, then escalation to a human officer — with every call logged and recordable as evidence that the SACCO pursued the arrears, which matters when SASRA asks.
The same broadcast code above runs this campaign with a different list and script. For loan-arrears calling on the 8.2% NPL book — which is member-level — reuse the contribution-reminder flow with a promise-to-pay option, and read the compliance section below twice.
A chama reminder is not a debt-collection call, and it must not sound like one. The person on the line is a co-member, probably a neighbour, and the group's cohesion is worth more than any single month's contribution. But Kenya's rules for digital lenders — CBK's Digital Credit Providers Regulations 2022, Regulation 20 — are the best cadence template available, even where they don't formally bind a chama or SACCO:
Call only the member. Reg 20 bans lenders from calling a borrower's phone contacts and referees, and the ODPC's biggest penalties were for exactly that — see the promise-to-pay compliance section for the case detail. For a group: never call a member's spouse or workmate about their contribution.
Never shame. No mentioning arrears to whoever answers a shared phone; state group business only after confirming who's listening, or keep the opening line neutral.
Don't harass by frequency. Reg 20's catch-all covers "conduct whose consequence is to harass". Two touches per cycle — three days before and the morning of the deadline — is plenty. If a member pressed 1 or 2, stop; they've acknowledged.
Keep hours sane. The usual 7 a.m.–7 p.m. window is best practice, not statute (the promise-to-pay compliance section explains why). For community money, 6 p.m. beats 7 a.m. anyway — that's when M-Pesa balances and goodwill are both at their daily peak.
Identify the caller and honour a stop. Under the Data Protection Act 2019, calling a member about their own contribution to a group they joined rests comfortably on the group relationship — but the call should say who's calling in the first sentence, and a member who asks the group to stop calling should be moved back to the WhatsApp-only list.
Sautikit pricing as of 2026-06-30: outbound calls at KES 3/min — KES 0.05 per second, billed per second after connect; inbound free; a local Nairobi number at KES 100/month ex. VAT (KES 116 incl. VAT). Current rates at /pricing.
A 30-member chama running the full monthly flow:
Item
Arithmetic
Cost
Reminder, 3 days out (30 members × ~35 s)
30 × 35 s × KES 0.05
KES 52.50
Deadline-morning nudge (say 12 still unpaid, ~35 s)
12 × 35 s × KES 0.05
KES 21.00
Meeting notice, evening before (30 × ~20 s)
30 × 20 s × KES 0.05
KES 30.00
Group's own number, monthly
—
KES 116.00
Total per month
≈ KES 220
About KES 7 per member per month — less than the M-Pesa transaction fee on the contribution itself, and it replaces the treasurer's entire evening of airtime and awkwardness.
The SACCO version scales the same way. Chasing 120 delinquent employers with a 45-second remittance call costs 120 × 45 × KES 0.05 = KES 270 per round; a monthly cycle of two rounds runs KES 540 — pursuing arrears measured in millions. The wallet is prepaid and KES-denominated, topped up over M-Pesa, so a group can load KES 500 and run for months; there's no card, no invoice, and no way to overspend past the balance.