Running distributed team calls across Nairobi, Kampala, and Lagos from a US-based conference bridge adds ~168 ms of unnecessary one-way latency for every Safaricom subscriber on the line. This guide sets up a Sautikit-hosted conference room with a local Nairobi dial-in number, DTMF PIN entry, moderator controls, and conference recording, all of it within East Africa's routing fabric and priced in KES.
When a Safaricom subscriber dials a US-based conference bridge, the call routes through Safaricom's core network, exits Kenya via the TEAMS or EASSy submarine cable, transits a US carrier's network, and finally terminates on the conference server. Each leg adds latency. The measured one-way delay on this path is typically 170–190 ms, comfortably above ITU-T G.114's 150 ms recommendation for natural conversation.
A Sautikit Nairobi number (KES 100/month ex. VAT, KES 116 incl. VAT) routes through Safaricom's Nairobi PoP directly to Sautikit's media infrastructure, also in Nairobi. Safaricom-to-Safaricom calls on Sautikit never leave Safaricom's core network. The measured one-way latency for this path is approximately 12 ms.
You can measure this yourself before committing. Send a SIP OPTIONS packet from your server to sip.sautikit.com and compare the round-trip time with the RTT to a US SIP endpoint:
# Measure SIP round-trip to Sautikit (Nairobi)sngrep -I sip.sautikit.com -p 5060 --count 5# For a quick UDP probe, sipsak works:sipsak -s sip:ping@sip.sautikit.com -v
The difference in RTT directly translates to perceived audio delay. At 12 ms vs 180 ms, every participant on a Safaricom line notices.
Every Sautikit number costs KES 100/month (ex. VAT; KES 116 incl. 16% VAT), whether it's a Nairobi 020 landline or a mobile 07xx number. For a conference bridge, a landline number is still the better choice: it signals a dedicated service line and is easier to distribute in meeting invites without ambiguity about the carrier.
Claim the number and set a voice_callback_url pointing to your webhook handler:
All participants join the same named room. startOnEnter controls when audio begins. Supervisors can join muted for silent monitoring.
When a caller dials in, Sautikit posts the call state to your voice_callback_url. Your handler returns a JSON action sequence: greet the caller, collect their PIN with GetDigits, validate it, and place them into the conference room.
// conference/join endpoint: Node.js (Express)import express from "express";const app = express();app.use(express.urlencoded({ extended: true }));app.use(express.json());const CONFERENCE_ROOMS = { "1234": { name: "engineering-standup", moderatorPin: "9999" }, "5678": { name: "all-hands", moderatorPin: "0001" },};app.post("/conference/join", (req, res) => { // The webhook receives form fields when a call connects; digit // submissions arrive on the same URL with a `Digits` field set. const digits = req.body.Digits || ""; if (!digits) { // First contact: ask for PIN return res.json({ actions: [ { say: { text: "Welcome. Please enter your conference PIN followed by the hash key.", language: "en-KE" } }, { getDigits: { numDigits: 4, timeout: 10000, finishOnKey: "#", action: "https://your-server.example.com/conference/join" } }, ] }); } const room = CONFERENCE_ROOMS[digits]; if (!room) { return res.json({ actions: [ { say: { text: "Invalid PIN. Please try again.", language: "en-KE" } }, { redirect: { url: "https://your-server.example.com/conference/join" } }, ] }); } // Valid PIN: place caller into conference return res.json({ actions: [ { say: { text: `Joining ${room.name}. You are being connected.`, language: "en-KE" } }, { conference: { name: room.name, muted: false, record: true, waitUrl: "https://your-server.example.com/conference/hold-music", } }, ] });});
The conference verb places the caller into a named room. Participants who dial in with the same name value share the same audio bridge. The room is created on first join and torn down when the last participant leaves.
Enable recording by setting record: true on the conference verb. Conference recording bills at KES 0.50/minute, but note that Sautikit bills per participant-minute, not per wall-clock minute.
For a 60-minute call with 10 participants: 60 min × 10 participants × KES 0.50 = KES 300 in recording costs. The first 5 GB of recording storage is free; the tiers above that start at KES 10/month for 10 GB.
The recording is attached to the call record for the conference leg. When the conference ends, Sautikit emits a call.recording.ready webhook event containing the call ID. Retrieve the presigned URL via GET /v1/calls/{id}/recording.
A participant can be designated moderator by setting moderator: true in the conference verb. Moderators can mute a specific participant, kick them, or end the conference via the commands endpoint at POST /v1/conferences/{room_name}/commands.
Rather than requiring remote participants to dial in, you can add them to the conference by dialling out from the bridge. This is useful for Ugandan or Tanzanian team members who would otherwise pay international rates.
Use the Dial verb with a conference destination to add a participant:
Dialling out to a Ugandan +256 number from Sautikit costs KES 3/min per outbound leg, the same rate as any outbound call. When the participant picks up, Sautikit emits a call.answered webhook event for that outbound leg.
At 10 participants with Opus at 16 kbps (the default for WebRTC SIP clients), the conference mixer processes approximately 160 kbps of inbound audio simultaneously. For standard PSTN callers on Safaricom, the codec at the interconnect is G.711 PCMU (64 kbps); Sautikit transcodes to the internal mixing format at the media gateway. This transcoding happens transparently; you do not need to negotiate codecs in your webhook response.
The practical implication: participants calling from a Nairobi landline or Safaricom mobile will sound slightly different from participants on WebRTC clients. The G.711-to-Opus transcoding introduces approximately 5 ms of additional processing latency and a minor MOS drop from ~4.2 to ~3.9. For a team call this is imperceptible.
Dial-out to Uganda (1 participant × 60 min × KES 3/min)
60 min
180
Total
483
For comparison, a subscription-based conferencing service at US$25/month at a KES 135/USD rate costs KES 3,375/month regardless of usage. The Sautikit model is pay-as-you-go: a team that holds two such calls per month pays approximately KES 966: well under that fixed cost, and in KES with a wallet you top up via M-Pesa.
The waitUrl is polled while the participant waits for the first other participant to join. Once a second participant joins, the normal conference audio starts. The play loop terminates automatically when the conference begins.
Need to notify participants over SMS or WhatsApp before the call, or run a staffed support desk alongside the bridge? Helloduty adds those channels on top of Sautikit's voice.