---
title: >-
  The Black Friday voice playbook: order confirmation and delivery coordination
  calls for Kenyan e-commerce
description: >-
  A November playbook for Kenyan e-commerce: COD confirmation calls,
  delivery-day DTMF coordination, failed-delivery rescues, and worked cost math
  at scale.
summary: >-
  Black Friday in Kenya runs all of November. This playbook covers COD
  order-confirmation calls, delivery-day coordination with DTMF, failed-delivery
  rescue flows, and capacity math for 10,000 orders.
date: 2026-11-03T00:00:00.000Z
type: blog
---


## Summary

Black Friday in Kenya is not a Friday; it is the whole of November. Jumia's 2025 campaign ran October 31 to November 30; the group moved 5.1 million orders across October–November, 3.2 million of them during the Black Friday event itself — 61% from outside the main cities, which is exactly where a delivery gets coordinated by phone call. This playbook covers the three calls that protect a cash-on-delivery operation through the peak: the checkout confirmation call that kills fake orders before dispatch, the delivery-day coordination call with a press-1/press-2 keypad prompt, and the failed-delivery rescue call — plus the pacing, retry, and cost math to run all three for 10,000 orders.

## Black November: a month-long peak, mostly upcountry

The dates first, framed on the record rather than on hope. Jumia's Black Friday 2025 ran October 31 through November 30 with discounts up to 70%, per its Nairobi launch coverage (as of 2025-10-31). Carrefour and Naivas ran parallel "Black November" campaigns the same month. Campaign dates for 2026 had not been announced when this guide was prepared, but the month-long pattern has held for years, and Black Friday proper falls on **November 27, 2026**. If the pattern holds, campaigns are already live as you read this.

The volume is real and it skews away from Nairobi. Per Jumia's preliminary KPI release for October–November 2025 (published December 2025): 5.1 million orders group-wide, up 30% year on year, with the Black Friday event itself driving 3.2 million of them — and **61% of orders came from outside capital and main urban centres**. For Kenya specifically, Business Daily reported (early 2026) that Jumia Kenya's Q4 2025 sales grew 48% year on year to KES 5.4 billion. The festive quarter is the growth engine, and the hangover is visible in regulator data: Communications Authority sector statistics show domestic parcel volumes fell 25.5% quarter-on-quarter to 2.76 million in January–March 2025, straight off the festive-quarter peak (CA Sector Statistics, published 2025).

Now connect the two numbers. If most orders come from outside the main cities, most deliveries end in Kenya's upcountry last mile — a parcel rides a shuttle or courier to the destination town, lands at a booking office or pickup station, and someone phones the recipient: *your parcel is at the office, come collect*. The last mile of logistics is already digital; the last mile of communication is a clerk or a rider with a call list and paid airtime. Cash on delivery makes it worse: an unreachable customer is not a re-delivery, it is a failed sale plus a wasted rider trip.

Three failure points, three automated calls.

## Call one: confirm the COD order within minutes of checkout

COD checkout has no payment gate, which is the point — and the problem. Nothing stops a mistyped phone number, a second thought, or an outright fake order from entering your dispatch queue. Every one of those you ship costs a real rider trip, and during Black November your riders have no trips to waste.

The fix is a confirmation call fired from your order-created webhook, within minutes of checkout, while intent is fresh: a 20–25 second script that reads back the order and total in KES, then asks for a keypress to confirm. Orders that are not confirmed by call (or a follow-up attempt) never reach dispatch.

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

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

const client = new SautikitClient({ apiKey: process.env.SAUTIKIT_API_KEY });

// Fired from your order-created webhook, within minutes of checkout.
const { call_id, status } = await client.calls.create({
  from: SHOP_NUMBER,
  to: [order.msisdn],
  idempotencyKey: `bf-confirm:${order.id}`,
});
// status: "ringing" — outcomes arrive on your events_url.
```

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

The idempotency key matters more in November than any other month. Keyed on the order ID, a crashed worker or a replayed queue message can retry the create call as often as it likes — the SDK sends it as the `Idempotency-Key` header, the platform dedupes the request, and your customer is never double-dialed. See [idempotency on the calls API](/developers/api#calls) for the semantics.

When the customer answers, the platform POSTs the call state to the `voice_callback_url` configured on your number's routing, and your handler returns the confirmation script as [JSON voice actions](/developers/concepts/voice-actions). The handler is the same shape as the delivery-day one below — a `say`, a `getDigits`, and a dispatch on the collected digit.

## Call two: the delivery-day coordination call

This is the call that moves the failed-delivery number. On the morning a rider or shuttle is dispatched, every order on the manifest gets a short call: *press 1 if you'll be at this location today, press 2 to reschedule*. Riders then run a manifest of confirmed-reachable customers instead of gambling airtime on silence.

One handler serves the whole flow. `getDigits` collects the keypress, and — this is the part people get wrong — it has **no follow-up URL**. When digits are collected, the platform POSTs back to the same `voice_callback_url` with the `Digits` field populated, and your handler branches on it:

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

const app = express();
app.use(express.json()); // the voice callback delivers JSON

app.post("/voice/delivery", async (req, res) => {
  const digits = req.body.Digits || "";
  const msisdn = req.body.clientDialedNumber; // the customer we dialed (destinationNumber is your own DID)
  const order = await ordersByPhone(msisdn);

  if (!digits) {
    // First hit (call answered): ask the question, collect one digit.
    return res.json(
      sauti
        .say(
          `Habari, hii ni ${SHOP_NAME}. Your order number ${order.shortRef} ` +
            `arrives today at ${order.deliveryArea}. ` +
            `Press 1 if you will be at this location today. ` +
            `Bonyeza mbili kubadilisha siku — press 2 to reschedule.`,
          { language: "en-KE" },
        )
        .getDigits({ numDigits: 1, timeout: 8 }) // timeout is seconds
        .say("Hatukupokea jibu. We will try again later. Kwaheri.", { language: "en-KE" })
        .hangup(),
    );
  }

  if (digits === "1") {
    await markReachableToday(order.id);
    return res.json(
      sauti
        .say("Asante. The rider will call when they are close. Kwaheri.", { language: "en-KE" })
        .hangup(),
    );
  }

  if (digits === "2") {
    await queueReschedule(order.id); // drop off today's manifest, offer new slots
    return res.json(
      sauti
        .say("Sawa. Our team will send you new delivery options shortly. Kwaheri.", { language: "en-KE" })
        .hangup(),
    );
  }

  // Unrecognised input: replay the question.
  return res.json(sauti.redirect("https://shop.example.com/voice/delivery", { method: "POST" }));
});
```

Two operational notes. First, routing lives on the **number**, not the call — set it once with `PUT /v1/numbers/{id}/routing`. If one number runs both confirmation and delivery calls, keep a single handler and let the order's state decide which script plays. Second, keep DTMF prompts short and the digit space small; keypresses on Kenyan mobile networks are lossy enough that a one-digit binary question dramatically outperforms a menu (see [reliable DTMF detection](/blog/dtmf-detection-reliability)).

## Call three: the failed-delivery rescue

Some deliveries fail anyway: phone off at dispatch time, *mteja hapatikani*, a customer who pressed 1 and then went to a funeral. In the standard setup this parcel bounces back to the warehouse or sits at a pickup station accruing storage days until it is returned — a lost COD sale.

The rescue call automates the recovery window. When the rider marks an attempt failed, queue an automated call from the **same branded number** that confirmed the order. This detail carries the flow: a customer who will not answer a rider's unknown personal number will answer the line that has called them twice already about this order. The script offers the two useful exits — press 1 to retry delivery tomorrow, press 2 to collect from the nearest pickup station — using exactly the handler pattern above.

Spread rescue attempts across windows rather than hammering: 15 minutes after the failed attempt, again two hours later, once the next morning, then stop. Sautikit bills per second **after connect** (per the [pricing page](/pricing), as of 2026-06-30), so an attempt that rings unanswered adds nothing to your talk-time spend — the retries cost patience, not money.

## Capacity planning for November

A 10,000-order November is not a steady drip. It is a baseline with cliffs on flash-sale days and a hard spike around November 27. Plan four things.

**Pace the queue.** Confirmation calls are event-driven and naturally smooth. Delivery-day calls are a batch: 500 orders on one morning manifest, dispatched between 08:30 and 11:00, is 500 calls over 150 minutes — a little over 3 call starts per minute. Feed your dialer from a queue with a concurrency cap rather than a for-loop, and it stays boring even on flash days.

**Define retry windows before the month starts.**

| Call | On no answer | Stop after |
|---|---|---|
| Checkout confirmation | +15 min, +2 h | 3 attempts, then hold order for manual review |
| Delivery-day coordination | +30 min (before rider departs) | 2 attempts, then rider skips or calls direct |
| Failed-delivery rescue | +15 min, +2 h, next morning | 4 attempts, then route parcel to pickup station |

Keep all attempts inside daytime hours — roughly 8am to 8pm. That is courtesy and campaign hygiene rather than a statutory requirement, and it protects your answer rates for the rest of the month.

**Pre-fund the wallet.** Sautikit is prepaid: at zero balance, `POST /v1/calls` returns `402 wallet.insufficient_balance` and your confirmation pipeline silently stalls — the worst possible failure mode mid-flash-sale. Set a low-balance threshold with an alert, and top up over M-Pesa STK push before the big days rather than during them.

**Watch outcomes, not dispatches.** Point the number's `events_url` at a handler that records `call.completed` outcomes per order, and dedupe on the event ID. Mid-campaign, you can also pull call stats and wallet balance straight from Claude through the [Sautikit MCP server](/mcp) — a daily ops standup without opening a dashboard.

## The math for 10,000 orders

Sautikit pricing (as of 2026-06-30): KES 3/min outbound — KES 0.05 per second, billed per second after connect. See [/pricing](/pricing) for current rates. Assume one order in ten dies at confirmation (fake, mistyped, changed mind), so 9,000 orders reach delivery day, and assume 1,200 rescue calls across the month including retries that connect.

| Call | Answered calls | Avg length | Total seconds | Cost at KES 0.05/s |
|---|---|---|---|---|
| Checkout confirmation | 10,000 | 25 s | 250,000 | KES 12,500 |
| Delivery-day coordination | 9,000 | 30 s | 270,000 | KES 13,500 |
| Failed-delivery rescue | 1,200 | 35 s | 42,000 | KES 2,100 |
| **Total** | | | **562,000** | **KES 28,100** |

The arithmetic per call: 25 seconds × KES 0.05 = KES 1.25 per answered confirmation; the whole three-call program lands at KES 28,100 ÷ 10,000 ≈ **KES 2.81 per order**. Set that against what it buys. The 1,000 fake orders killed at confirmation are 1,000 rider trips never dispatched. And if the coordination and rescue calls convert even 2% of orders — 200 sales — that would otherwise have failed on reachability, then at an assumed average basket of KES 2,500 that is KES 500,000 of rescued COD revenue against KES 28,100 of call spend. The assumptions are yours to tune; the shape of the trade rarely changes.

## The December handoff

November's peak does not end, it hands over. December brings the travel exodus, matatu and bus fares that roughly triple (per Nairobi press reporting, December 2025), and parcels that increasingly move through shuttle desks and pickup stations to recipients who are themselves on the road. The three-call pattern carries straight over — arrival notifications replace confirmation calls, and the rescue flow works even harder when recipients are travelling. If you are wiring this up for Black November, you are also building your December stack; the broader delivery patterns are in [cut failed deliveries in Kenya](/blog/logistics-voice-patterns-kenya). And when the campaign needs SMS receipts or WhatsApp alongside the calls, that is [Helloduty](https://helloduty.com), the multi-channel CX platform Sautikit is part of.

## Get started

1. [Create a Sautikit workspace](/) and claim a phone number.
2. Top up over **M-Pesa**: KES billing, no card.
3. Wire your order-created webhook to `POST /v1/calls` and point the number's routing at the delivery handler above.

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