---
title: >-
  Chama and SACCO reminders: collect group contributions without the awkward
  treasurer call
description: >-
  Automate chama contribution reminders and SACCO remittance chasing with voice
  calls in KES: gentle cadences, press-1 acknowledgements, paybill readback.
summary: >-
  How chamas and SACCOs replace the treasurer's one-by-one calls with a
  group-line voice broadcast: reminder flows, DTMF acknowledgements,
  employer-level remittance chasing, and cost math in shillings.
date: 2026-10-13T00:00:00.000Z
type: blog
---


## Summary

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.

## Every treasurer is a human reminder engine

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.

## Why the treasurer's personal number is the wrong caller

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](/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 contribution-reminder broadcast

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:

```bash
curl -X PUT https://api.sautikit.com/v1/numbers/$NUMBER_ID/routing \
  -H "Authorization: Bearer $SAUTIKIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "voice_callback_url": "https://chama.example.com/voice/reminder" }'
```

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.

```bash
npm install @sautikit/node@0.2.0
```

> 
> 
> `@sautikit/node` is in preview — pin the version; the API may change before 1.0.
> 
> 

```js
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 spreadsheet

for (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](/developers/concepts/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:

```js
import express from "express";
import { sauti } from "@sautikit/node";

const app = express();
app.use(express.json()); // voice callbacks arrive as JSON

app.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](/blog/debt-reminder-robocall-mpesa-kenya); 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](/mcp).

## The table-banking meeting reminder

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.

## SACCO remittance chasing: call the employer, not the member

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.

## Cadence design: community money is softer than commercial collections

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](/blog/promise-to-pay-dtmf-debt-collection) 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](/blog/promise-to-pay-dtmf-debt-collection) 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.

> 
> 
> This is compliance-informed practice, not legal advice. The DCP Regulations bind licensed digital credit providers; chamas and most SACCO reminder calls sit outside them. Treating Reg 20 as your floor anyway costs nothing and keeps the group's calls welcome.
> 
> 

## What a reminder round costs

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](/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.

## Get started

1. [Create a Sautikit workspace](/) and claim the group's own phone number.
2. Top up over **M-Pesa**: KES billing, no card.
3. Point the number's `voice_callback_url` at the reminder handler above and run your first round before the next meeting day.

**[Start with Sautikit →](/)** &nbsp;·&nbsp; **[See pricing →](/pricing)** &nbsp;·&nbsp; **[Need SMS, WhatsApp & an agent desk? Helloduty →](https://helloduty.com)**
