sautikit
PricingDevelopersBlogAbout
Sign inStart building

Route callers automatically: build an IVR menu with JSON, no telephony SDK

Build a DTMF-driven IVR menu with Sautikit voice actions (Say, GetDigits, and Redirect).

use-caseivrvoice-actionsdtmf

Next Steps

  • SaySayAction speaks synthesised text to the caller.
  • GetDigitsGetDigitsAction collects DTMF from the caller, optionally with a nested `<Say>` / `<Play>` prompt. Mirrors the PBX `<GetDigits>` verb.
  • RedirectRedirectAction transfers the call flow to another Sautikit URL. The URL should point to a Sautikit-owned endpoint that returns a new VoiceAction response; the PBX rejects unsupported URL targets.
  • Voice Actions DSLVoice Actions are the JSON DSL Sautikit uses to control call flow. Your voice_callback_url returns a JSON array of verbs; the platform executes them in order against the live call.
sautikit

Programmable voice infrastructure for Africa. Buy numbers, place calls, and bill per second, all in KES, via API.

Product

NumbersCalls & routingRecordingsWallet & billingPricing

Developers

DocumentationAPI referenceQuickstartAI promptChangelog

Company

AboutBlogCareersConsole

© 2026 Sautikit. All rights reserved.

Sautikit provides voice API services for application developers. Numbers provisioned on this platform are not configured for emergency calling (e.g. 999 / 112). Do not use Sautikit numbers as a replacement for a primary phone line.

Summary

An IVR (Interactive Voice Response) system answers an inbound call, plays a spoken menu, collects a key-press from the caller, and routes the call to the correct destination or triggers a follow-up action. With Sautikit you implement this entirely through voice-action JSON returned from your webhook endpoint: no proprietary markup language, no telephony SDK to install.

Your webhook URL is attached to a phone number you claim. When a call arrives, Sautikit POSTs to that URL and executes whatever actions your server returns.

Who this is for

  • Teams building self-service telephone menus (press 1 for sales, press 2 for support).
  • Developers adding voice automation to an existing web backend.
  • Product managers who need a recorded phone tree with measurable drop-off rates.
  • Any application that needs to collect a single digit or short PIN before routing a caller.

How it works

IVR menu flow
IVR menu flowCaller dials. Sautikit posts to webhook. Server returns Say + GetDigits. Digit 1 routes to sales. Digit 2 routes to support. Digit 0 replays menu. No input loops back to menu.Caller dials +254700000001Say + GetDigits"Press 1 Sales · 2 Support · 0 Repeat"digit=1Dial +254722111111sales teamdigit=0Redirect /voicereplay menudigit=2Dial +254722222222support team? input
Each digit press posts back to your voice_callback_url. Your server reads the Digits field and returns the next action set.

The loop is pure HTTP: each step is a POST from Sautikit to your server, and your server replies with JSON. You can have as many levels in the menu as you need by chaining Redirect and additional GetDigits steps.

State management

Your server owns the menu state. The POST body from Sautikit includes the call SID, the caller's number (From), and the collected digits. Use these to look up session context in your database or cache if you need to track where the caller is in a multi-level tree.

API surface

Endpoints you call:

  • POST /v1/numbers: claim a phone number to attach your webhook.
  • PATCH /v1/numbers/{number_id}: set or update the routing_url (your webhook).
  • GET /v1/calls/{call_sid}: fetch call detail records after the call ends.

Voice actions used:

  • Say: text-to-speech prompt for the menu.
  • GetDigits: collect one or more DTMF digits from the caller.
  • Play: play an audio file instead of synthesised speech (optional).
  • Redirect: send the in-progress call to a different webhook URL.
  • Dial: connect the caller to an agent or external number.
  • Hangup: end the call cleanly.

Example

1. Webhook handler (Express / Node.js)

import express from "express";
const app = express();
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
 
// Step 1: Sautikit calls this when the number is dialled
app.post("/ivr/welcome", (req, res) => {
  res.json({
    actions: [
      {
        getDigits: {
          numDigits: 1,
          timeout: 5,
          finishOnKey: "",
          action: "https://yourapp.example.com/ivr/route",
          nested: [
            {
              say: {
                text: "Welcome to Acme Corp. Press 1 for sales. Press 2 for support. Press 3 to hear this menu again.",
                language: "en-US",
              },
            },
          ],
        },
      },
    ],
  });
});
 
// Step 2: digit collected
app.post("/ivr/route", (req, res) => {
  const digit = req.body.Digits ?? req.body.digits ?? "";
 
  if (digit === "1") {
    return res.json({
      actions: [
        { say: { text: "Connecting you to sales." } },
        { dial: { number: "+254720000001" } },
      ],
    });
  }
 
  if (digit === "2") {
    return res.json({
      actions: [
        { say: { text: "Connecting you to support." } },
        { dial: { number: "+254720000002" } },
      ],
    });
  }
 
  // Fallback: replay menu
  return res.json({
    actions: [{ redirect: { url: "https://yourapp.example.com/ivr/welcome" } }],
  });
});
 
app.listen(3000);

2. Attach your webhook to a number

curl -X PATCH "https://api.sautikit.com/v1/numbers/{number_id}" \
  -H "Authorization: Bearer $SAUTIKIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"routing_url": "https://yourapp.example.com/ivr/welcome"}'

Sautikit POSTs the call details to routing_url whenever this number receives an inbound call.

Pricing notes

IVR usage is billed per minute for the inbound call leg. A caller who navigates a two-level menu and then connects to an agent for five minutes uses:

  • IVR handling time (menu prompts + digit collection): billed at the standard per-minute inbound rate for the duration the call is active on the Sautikit platform.
  • Connected leg: once Dial connects the caller to an agent, the per-minute rate continues for both the inbound and outbound legs.

There is no separate fee for running voice actions or for the number of HTTP round-trips between Sautikit and your webhook.

Keep GetDigits timeouts short (3–5 seconds) to avoid unnecessary billed silence time. If no digit is received before the timeout, Sautikit will proceed to the next action or post to the action URL with an empty Digits field, so you can loop the menu rather than holding the call open.

Next steps

  • GetDigits voice action reference: full parameter list, nested verb support.
  • Say voice action reference: TTS voice and language options.
  • Voice actions concept: how the action-response loop works end to end.
  • Webhooks concept: POST body schema and security headers.
  • Redirect voice action: multi-level menu chaining.