---
title: >-
  Stop silent lapses: insurance renewal reminder calls that get answered in
  Kenya
description: >-
  Insurance renewals in Kenya lapse silently. Build a 30/14/3-day voice reminder
  flow with press-1 paybill, agent transfer, and callbacks, priced in KES.
summary: >-
  Why renewal SMS fails silently, and how insurers run 30/14/3-day reminder
  calls with a renew-now keypress, agent transfer, callbacks, lapse-save math in
  KES, and consent-safe QA recording.
date: 2026-09-08T00:00:00.000Z
type: blog
---


## Summary

Kenyan insurance has a retention problem hiding inside a growth story: gross premium grew 13.4% year on year to KES 241.3 billion in the first half of 2025, yet penetration fell to 2.2% of GDP (per IRA industry data, H1 2025). Policies lapse silently — the renewal SMS goes unread, the agent's call list never gets finished, and a client quietly becomes uninsured. This guide builds a 30/14/3-day renewal reminder call flow on Sautikit: an automated voice call with "press 1 to renew now, press 2 to talk to your agent, press 3 to request a callback", the agent-transfer pattern behind option 2, the lapse-save arithmetic in KES, and the consent notes you need when calls are recorded for quality assurance.

## Renewals die silently

The paradox in the IRA's numbers is worth sitting with. Premium income is growing at double digits, but insurance penetration dropped to 2.2% of GDP in H1 2025, down from 2.4% in 2024 and far below the 7.4% global average (per IRA data as summarized by Cytonn, September 2025). Kenyans buy cover when they must — motor third-party is mandatory, and motor plus medical made up 67.6% of general-business premium in the same period — and drop it the moment money is tight or nobody reminds them.

Distribution makes the problem personal. Renewals in Kenya belong to tied agents and brokers, and the renewal "system" at most agencies is one person, a 500-client book, and a phone. Hand-calling that book at three minutes a client is 25 hours of dialing — per reminder wave. Nobody finishes the list, so the list gets triaged: big fleet accounts get a call, the KES 30,000 private comprehensive policy gets an SMS, and the boda rider's third-party cover gets nothing. The industry knows how this ends — the Association of Kenyan Insurers and the Insurance Fraud Investigation Unit run joint road spot checks precisely because expired and fake motor certificates are common enough to need policing. A meaningful share of "renewals" simply never happen.

And a lapse is rarely a deferral. Research on Kenyan lapsed policyholders finds that low product understanding drives lapse in the first place, and that lapsed customers rarely come back. Losing the renewal usually means losing the client — and paying acquisition costs all over again for their replacement.

## Why the renewal SMS fails, silently

Every insurer already sends renewal SMS. The problem is not that SMS is bad; it is that SMS fails without telling you.

**A delivery receipt measures the handset, not the human.** "Delivered" means the message reached a phone. It says nothing about whether it was read, understood, or buried under fifty promotional texts. An ignored renewal notice produces exactly the same data trail as one that worked — silence — right up until the certificate expires.

**Renewal is a decision, not a notification.** An SMS asks for nothing in the moment. A voice call puts a decision in front of the policyholder while you have their attention: renew now, talk to my agent, or ask for a callback. Each keypress is a signal your renewal team can act on; silence is not.

**Voice reaches the customers SMS structurally misses.** A spoken reminder in English and Swahili works for the policyholder who won't parse a dense English text about "policy no. MTR/2026/08841 due for renewal."

And there is the calendar. Ask any agency about January and you will hear about the crunch: corporate and fleet covers commonly run on the calendar year, and January's post-festive cash squeeze is when private owners downgrade from comprehensive to third-party or lapse entirely. That is how the market talks about it — a pattern practitioners plan around, not a statistic any regulator publishes — and it is exactly when a reminder machine that does not get tired earns its keep.

## The SHA precedent: re-enrollment comms at national scale

If mass re-enrollment communication sounds theoretical, Kenya just ran the largest one in its history. When NHIF was wound down on 1 October 2024 and replaced by the Social Health Authority, every member had to register afresh — nothing carried over. Registration went from roughly 8 million members under NHIF to 18.99 million by early February 2025 (per the Ministry of Health) and past 27 million by late October 2025 (per press reports of SHA figures). That took a sustained, multi-channel push: national campaigns, USSD, county drives, and a lot of phones ringing.

The lesson: re-enrollment communication at scale happens in this market, and it works when treated as an outbound-contact problem, not a "we sent the notice" problem. Your renewal book is the same problem at smaller scale, with a clearer payoff per contact.

## The 30/14/3 flow

Three calls, keyed to the lapse date: 30 days out (the decision window opens — quotes and no-claim discounts can be discussed), 14 days out (urgency, paybill in hand), and 3 days out (last call before cover ends). Policies that renew after the first wave drop out of the later ones.

Place the calls from a daily job. Two Sautikit details do the heavy lifting: the [`Idempotency-Key` header](/developers/concepts/calls) makes re-runs safe (same policy, same wave, one call — even if your cron fires twice), and per-second billing keeps a 40-second reminder cheap.

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

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

```js
// renewal-dialer.js — run daily. Dials every policy 30, 14, or 3 days from lapse.
import { SautikitClient, SautikitError } from "@sautikit/node";

const client = new SautikitClient({ apiKey: process.env.SAUTIKIT_API_KEY });
const WAVES = [30, 14, 3];

for (const days of WAVES) {
  const policies = await policiesLapsingInDays(days); // your book: policyNo, msisdn, lapseDate, agentMsisdn

  for (const p of policies) {
    try {
      await client.calls.create({
        from: process.env.SAUTIKIT_NUMBER, // your workspace-owned number
        to: [p.msisdn],
        // Same policy + same wave = one call, even if this job re-runs.
        idempotencyKey: `renewal:${p.policyNo}:${p.lapseDate}:d${days}`,
      });
    } catch (err) {
      // Typed error — no status-code sniffing on the raw response.
      if (err instanceof SautikitError && err.status === 402) break; // wallet.insufficient_balance — top up, then resume
      throw err;
    }
  }
}
```

The call's behaviour lives on the number, not the request: point your number's `voice_callback_url` at the handler below (via `PUT /v1/numbers/{id}/routing` or the dashboard), and Sautikit will POST there when the policyholder answers.

## One handler, three keys

`getDigits` collects the keypress; when digits arrive, the platform POSTs back to the same `voice_callback_url` with the `Digits` field populated. One handler therefore serves the whole flow — it plays the menu when there are no digits, and dispatches when there are. (`timeout` is in seconds, and there is no follow-up URL field on `getDigits` — dispatch on `Digits`.)

```js
// voice.js — the number's voice_callback_url
import express from "express";
import { sauti } from "@sautikit/node";

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

app.post("/voice/renewal", async (req, res) => {
  const digits = req.body.Digits || "";
  const policy = await policyByPhone(req.body.destinationNumber); // look up by the number we dialed

  if (!digits) {
    // First hit: call answered. Play the menu, collect one digit.
    return res.json(
      sauti
        .getDigits({ timeout: 7, numDigits: 1 }, [
          // seconds
          {
            say: {
              text:
                `Habari. This is Acme Insurance. Your motor cover ends on ${policy.lapseDateSpoken}. ` +
                "To renew now and get the paybill, press 1. Kulipia sasa, bonyeza moja. " +
                "To talk to your agent, press 2. Kuzungumza na wakala wako, bonyeza mbili. " +
                "To request a callback, press 3.",
              language: "en-KE",
            },
          },
        ])
        // No input: close politely; the next wave will try again.
        .say("Tutapiga tena kabla ya tarehe. We will call again before your cover ends. Kwaheri.", {
          language: "en-KE",
        })
        .hangup(),
    );
  }

  if (digits === "1") {
    await markIntentToRenew(policy);
    return res.json(
      sauti
        .say(
          `To renew, pay ${policy.premiumSpoken} shillings to paybill 8 8 4 4 0 0, ` +
            `account number ${policy.policyNoSpoken}.`,
          {
            language: "en-KE",
            loop: 2, // say it twice — nobody catches a paybill on the first pass
          },
        )
        .hangup(),
    );
  }

  if (digits === "2") {
    return res.json(
      sauti
        .say(
          "Connecting you to your agent. This call may be recorded for quality assurance. Subiri kidogo.",
          { language: "en-KE" },
        )
        .dial({
          number: policy.agentMsisdn,       // the assigned agent, not a pool
          callerId: process.env.SAUTIKIT_NUMBER,
          timeout: 25,
          record: "record-from-answer",     // QA recording — see the consent section
        })
        // Runs when the dial leg ends — unanswered OR after the conversation, if the caller is still on the line.
        .say("If we could not connect you just now, your agent will call you back shortly. Kwaheri.", {
          language: "en-KE",
        })
        .hangup(),
    );
  }

  if (digits === "3") {
    await queueCallback(policy); // lands on the agent's follow-up list in your CRM
    return res.json(
      sauti
        .say("Sawa. Your agent will call you back within one working day. Kwaheri.", {
          language: "en-KE",
        })
        .hangup(),
    );
  }

  // Unrecognised digit: replay the menu.
  return res.json(sauti.redirect("https://yourapp.example/voice/renewal", { method: "POST" }));
});
```

Pair option 1 with a follow-up text carrying the paybill and account number in writing — SMS and WhatsApp follow-ups (and the agent desk to work the responses) are a job for [Helloduty](https://helloduty.com), the multi-channel CX platform Sautikit is part of.

## The agent-transfer pattern

Option 2 is where the economics of a 500-client book change. The `dial` verb bridges the policyholder to their assigned agent's phone — `number` is the agent's MSISDN pulled from your CRM, not a shared hotline. Three details matter:

- **`callerId` set to your business number** keeps the brand consistent: the client sees the same number that called them, and the agent's personal line stays private.
- **`timeout: 25`** bounds how long the agent's phone rings before Sautikit gives up. Voice actions run strictly top to bottom, so the next action fires whenever the `dial` leg ends — whether the agent never answered or the conversation finished and the policyholder is still on the line — falling through to a polite promise of a callback rather than only on no-answer.
- **The transfer is warm by construction.** The only people who reach an agent are the ones who pressed 2 on a call about their own expiring policy. The machine does the 25 hours of dialing; humans get a queue of interested clients.

For deeper routing — agent busy, route to a supervisor, office-hours logic — the same [Digits-dispatch pattern](/developers/concepts/voice-actions) extends with `redirect` to per-branch handlers, exactly as in our [KES IVR menu guide](/blog/ivr-menu-kes-payments).

## What a save is worth

Sautikit pricing (as of 2026-06-30): KES 3/min outbound (KES 0.05/sec), billed per second after connect; inbound free. See [/pricing](/pricing) for current rates. The reminder script above runs about 40 seconds when the policyholder listens through the menu:

- One answered reminder: 40 s × KES 0.05 ≈ **KES 2.00**
- Full 30/14/3 cadence: 3 × KES 2.00 = **KES 6.00 per policy** — an upper bound, since clients who renew at day 30 drop out of later waves
- A 500-client book: 500 × KES 6.00 = **KES 3,000 per renewal cycle**

Now put that against what a lapse costs. A mid-range private comprehensive motor premium runs on the order of KES 30,000. If the entire season of calling — every client, every wave — saves **one** such policy, the retained premium is ten times what the campaign cost. And because Kenyan lapse research says lapsed clients rarely re-buy, the realistic alternative to that save is not "they renew late"; it is paying acquisition costs to replace the client. The arithmetic is lopsided enough that the real question is not whether to call, but why any renewal book still relies on an unread text.

Once the number and handler are wired, you can run the campaign itself from Claude — place a wave, pull call stats, check the wallet — through the hosted [Sautikit MCP server](https://sautikit.com/mcp).

## Recording for QA: consent and retention

If you enable `record: "record-from-answer"` on the agent leg, you are processing personal data: under Kenya's Data Protection Act, 2019, a voice recording of an identifiable person is personal data, full stop.

- **Notify before recording starts.** The handler above says "this call may be recorded for quality assurance" *before* the `dial` action — put the notice ahead of the recording, not after it.
- **Have a documented lawful basis and purpose** (quality assurance and dispute resolution are the usual ones for renewal lines), and honour a caller who objects — route them to an unrecorded line or skip the `record` parameter for that call.
- **Keep a retention schedule and stick to it.** Recording costs KES 0.50 per recorded minute (as of 2026-06-30), and storage on Sautikit is tiered — the free tier keeps recordings for 24 hours, paid tiers extend retention; see [/pricing](/pricing). Retention that expires by default is a feature here: recordings you no longer hold are recordings you cannot mishandle.

> 
> 
> This is engineering guidance, not legal advice. For the fuller regulatory picture — CAK licensing, the DPA, and what Sautikit handles for you — see [Ship compliant voice in Kenya](/blog/voice-regulation-kenya).
> 
> 

## Get started

1. [Create a Sautikit workspace](/) and claim a phone number.
2. Top up over **M-Pesa**: KES billing, no card.
3. Point your number's `voice_callback_url` at the renewal handler above, load your lapse dates, and schedule the 30/14/3 dialer.

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