Gather User Input via Keypad (DTMF Tones) in Java

In this guide, we'll show you how to gather user input during a phone call through the phone's keypad (using DTMF tones) in your Java Servlets application. By applying this technique, you can create interactive voice response (IVR) systems and other phone based interfaces for your users. The code snippets in this guide are written using Java and require the Java JDK 7 or higher. They also make use of the Twilio Java SDK.

Let's get started!

Set up your Java Servlets application to receive incoming phone calls

This guide assumes you have already set up your web application to receive incoming phone calls. If you still need to complete this step, check out this guide. It should walk you through the process of buying a Twilio number and configuring your app to receive incoming calls from it.

Collect user input with the <Gather> TwiML verb

The <Gather> TwiML verb allows us to collect input from the user during a phone call. Gathering user input through the keypad is a core mechanism of Interactive Voice Response (IVR) systems where users can press "1" to connect to one menu of options and press "2" to reach another. These prompts can be accompanied by voice prompts to the caller, using the TwiML <Say> and <Play> verbs. In this example, we will prompt the user to enter a number to connect to a certain department within our little IVR system.

Loading Code Samples...
Language
SDK Version:
  • 6.x
  • 7.x
import com.twilio.sdk.verbs.Gather;
import com.twilio.sdk.verbs.Redirect;
import com.twilio.sdk.verbs.Say;
import com.twilio.sdk.verbs.TwiMLResponse;
import com.twilio.sdk.verbs.TwiMLException;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class VoiceServlet extends HttpServlet {

    public void service(HttpServletRequest request, HttpServletResponse response) throws IOException {

        // Create a TwiML response and add our friendly message.
        TwiMLResponse twiml = new TwiMLResponse();

        Gather gather = new Gather();
        gather.setNumDigits(1);
        try {
            gather.append(new Say("For sales, press 1. For support, press 2."));
            twiml.append(gather);
            twiml.append(new Redirect("/voice"));
        } catch (TwiMLException e) {
            throw new RuntimeException(e);
        }

        response.setContentType("application/xml");
        response.getWriter().print(twiml.toXML());
    }
}
import com.twilio.twiml.Gather;
import com.twilio.twiml.Redirect;
import com.twilio.twiml.Say;
import com.twilio.twiml.TwiML;
import com.twilio.twiml.TwiMLException;
import com.twilio.twiml.VoiceResponse;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class VoiceServlet extends HttpServlet {

    public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {

        // Create a TwiML response and add our friendly message.
        TwiML twiml = new VoiceResponse.Builder()
                .gather(
                        new Gather.Builder()
                                .numDigits(1)
                                .say(new Say.Builder("For sales, press 1. For support, press 2.").build())
                                .build()
                )
                .redirect(new Redirect.Builder().url("/voice").build())
                .build();

        response.setContentType("application/xml");
        try {
            response.getWriter().print(twiml.toXml());
        } catch (TwiMLException e) {
            throw new RuntimeException(e);
        }
    }
}
This example returns TwiML containing a <Say> tag nested within a <Gather> tag. The user will be prompted to choose sales or support.
Use <Gather> to collect user input via the keypad (DTMF tones)

This example returns TwiML containing a <Say> tag nested within a <Gather> tag. The user will be prompted to choose sales or support.

If the user doesn't enter any input after a configurable timeout, Twilio will continue processing the TwiML in the document to determine what should happen next in the call. When the end of the document is reached, Twilio will hang up the call. In the above example, we use the <Redirect> verb to have Twilio request the same URL again, repeating the prompt for the user

If a user were to enter input with the example above, the user would hear the same prompt over and over again regardless of what button you pressed. By default, if the user does enter input in the <Gather>, Twilio will send another HTTP request to the current webhook URL with a POST parameter containing the Digits entered by the user. In the sample above, we weren't handling this input at all. Let's update that logic to also process user input if it is present.

Loading Code Samples...
Language
SDK Version:
  • 6.x
  • 7.x
import com.twilio.sdk.verbs.Gather;
import com.twilio.sdk.verbs.Redirect;
import com.twilio.sdk.verbs.Say;
import com.twilio.sdk.verbs.TwiMLResponse;
import com.twilio.sdk.verbs.TwiMLException;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class VoiceServlet extends HttpServlet {

    public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {

        // Create a TwiML response and add our friendly message.
        TwiMLResponse twiml = new TwiMLResponse();

        String digits = request.getParameter("Digits");
        if (digits != null) {
            switch (digits) {
                case "1":
                    try {
                        twiml.append(new Say("You selected sales. Good for you!"));
                    } catch (TwiMLException e) {
                        throw new RuntimeException(e);
                    }
                    break;
                case "2":
                    try {
                        twiml.append(new Say("You need support. We will help!"));
                    } catch (TwiMLException e) {
                        throw new RuntimeException(e);
                    }
                    break;
                default:
                    try {
                        twiml.append(new Say("Sorry, I don\'t understand that choice."));
                    } catch (TwiMLException e) {
                        throw new RuntimeException(e);
                    }

                    appendGather(twiml);
                    break;
            }
        } else {
            appendGather(twiml);
        }

        response.setContentType("application/xml");
        response.getWriter().print(twiml.toXML());
    }

    private static TwiMLResponse appendGather(TwiMLResponse twiMLResponse) {
        Gather gather = new Gather();
        gather.setNumDigits(1);
        try {
            gather.append(new Say("For sales, press 1. For support, press 2."));
            twiMLResponse.append(gather);
            twiMLResponse.append(new Redirect("/voice"));
        } catch (TwiMLException e) {
            throw new RuntimeException(e);
        }

        return twiMLResponse;
    }
}
import com.twilio.twiml.*;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class VoiceServlet extends HttpServlet {

    public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {

        // Create a TwiML response and add our friendly message.
        VoiceResponse.Builder builder = new VoiceResponse.Builder();

        String digits = request.getParameter("Digits");
        if (digits != null) {
            switch (digits) {
                case "1":
                    builder.say(new Say.Builder("You selected sales. Good for you!").build());
                    break;
                case "2":
                    builder.say(new Say.Builder("You need support. We will help!").build());
                    break;
                default:
                    builder.say(new Say.Builder("Sorry, I don\'t understand that choice.").build());
                    appendGather(builder);
                    break;
            }
        } else {
            appendGather(builder);
        }

        response.setContentType("application/xml");
        try {
            response.getWriter().print(builder.build().toXml());
        } catch (TwiMLException e) {
            throw new RuntimeException(e);
        }
    }

    private static void appendGather(VoiceResponse.Builder builder) {
        builder.gather(new Gather.Builder()
                        .numDigits(1)
                        .say(new Say.Builder("For sales, press 1. For support, press 2.").build())
                        .build()
                )
                .redirect(new Redirect.Builder().url("/voice").build());
    }
}
By default, <Gather> will "loop" on the current TwiML URL, requesting the same URL every time input is entered.
Branch your call logic based on the digits sent by the user

By default, <Gather> will "loop" on the current TwiML URL, requesting the same URL every time input is entered.

Specify an action to take after user input is collected

You may want to have an entirely different endpoint in your application handle the processing of user input. This is possible using the "action" attribute of the <Gather> verb. Let's update our example to add a second endpoint that will be responsible for handling user input.

Loading Code Samples...
Language
SDK Version:
  • 6.x
  • 7.x
import com.twilio.sdk.verbs.Gather;
import com.twilio.sdk.verbs.Redirect;
import com.twilio.sdk.verbs.Say;
import com.twilio.sdk.verbs.TwiMLResponse;
import com.twilio.sdk.verbs.TwiMLException;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class VoiceServlet extends HttpServlet {

    public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {

        // Create a TwiML response and add our friendly message.
        TwiMLResponse twiml = new TwiMLResponse();

        Gather gather = new Gather();
        gather.setNumDigits(1);
        try {
            gather.append(new Say("For sales, press 1. For support, press 2."));
            twiml.append(gather);
            twiml.append(new Redirect("/voice"));
        } catch (TwiMLException e) {
            throw new RuntimeException(e);
        }

        response.setContentType("application/xml");
        response.getWriter().print(twiml.toXML());
    }
}
import com.twilio.twiml.*;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class VoiceServlet extends HttpServlet {

    public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {

        // Create a TwiML response and add our friendly message.
        TwiML twiml = new VoiceResponse.Builder()
                .gather(new Gather.Builder()
                        .numDigits(1)
                        .action("/gather")
                        .say(new Say.Builder("For sales, press 1. For support, press 2.").build())
                        .build()
                )
                .redirect(new Redirect.Builder().url("/voice").build())
                .build();

        response.setContentType("application/xml");
        try {
            response.getWriter().print(twiml.toXml());
        } catch (TwiMLException e) {
            throw new RuntimeException(e);
        }
    }
}
This technique creates a separate route to handle user input.
Add another route to handle the input from the user

This technique creates a separate route to handle user input.

Loading Code Samples...
Language
SDK Version:
  • 6.x
  • 7.x
import com.twilio.sdk.verbs.*;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class GatherServlet extends HttpServlet {

    public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {

        // Create a TwiML response and add our friendly message.
        TwiMLResponse twiml = new TwiMLResponse();

        String digits = request.getParameter("Digits");
        if (digits != null) {
            switch (digits) {
                case "1":
                    try {
                        twiml.append(new Say("You selected sales. Good for you!"));
                    } catch (TwiMLException e) {
                        throw new RuntimeException(e);
                    }
                    break;
                case "2":
                    try {
                        twiml.append(new Say("You need support. We will help!"));
                    } catch (TwiMLException e) {
                        throw new RuntimeException(e);
                    }
                    break;
                default:
                    try {
                        twiml.append(new Say("Sorry, I don\'t understand that choice."));
                        twiml.append(new Redirect("/voice"));
                    } catch (TwiMLException e) {
                        throw new RuntimeException(e);
                    }
                    break;
            }
        } else {
            try {
                twiml.append(new Redirect("/voice"));
            } catch (TwiMLException e) {
                throw new RuntimeException(e);
            }
        }

        response.setContentType("application/xml");
        response.getWriter().print(twiml.toXML());
    }
}
import com.twilio.twiml.Redirect;
import com.twilio.twiml.Say;
import com.twilio.twiml.TwiMLException;
import com.twilio.twiml.VoiceResponse;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class GatherServlet extends HttpServlet {

    public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {

        // Create a TwiML response and add our friendly message.
        VoiceResponse.Builder builder = new VoiceResponse.Builder();

        String digits = request.getParameter("Digits");
        if (digits != null) {
            switch (digits) {
                case "1":
                    builder.say(new Say.Builder("You selected sales. Good for you!").build());
                    break;
                case "2":
                    builder.say(new Say.Builder("You need support. We will help!").build());
                    break;
                default:
                    builder.say(new Say.Builder("Sorry, I don\'t understand that choice.").build());
                    builder.redirect(new Redirect.Builder().url("/voice").build());
                    break;
            }
        } else {
            builder.redirect(new Redirect.Builder().url("/voice").build());
        }

        response.setContentType("application/xml");
        try {
            response.getWriter().print(builder.build().toXml());
        } catch (TwiMLException e) {
            throw new RuntimeException(e);
        }
    }
}
This technique creates a separate route to handle user input.
Implement an 'action' URL to handle user input

This technique creates a separate route to handle user input.

The action attribute takes a relative URL which would point to another route your server is capable of handling. Now, instead of conditional logic in a single route, we use actions and redirects to handle our call logic with separate code paths.

Where to next?

If you're building call center type applications in Java Servlets, you might enjoy stepping through full sample applications that implement a full IVR system.

Agustin Camino
Andrew Baker
Kevin Whinnery
David Prothero
Rob Spectre
Ricky Robinett

Need some help?

We all do sometimes; code is hard. Get help now from our support team, or lean on the wisdom of the crowd browsing the Twilio tag on Stack Overflow.

1 / 1
Loading Code Samples...
SDK Version:
  • 6.x
  • 7.x
import com.twilio.sdk.verbs.Gather;
import com.twilio.sdk.verbs.Redirect;
import com.twilio.sdk.verbs.Say;
import com.twilio.sdk.verbs.TwiMLResponse;
import com.twilio.sdk.verbs.TwiMLException;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class VoiceServlet extends HttpServlet {

    public void service(HttpServletRequest request, HttpServletResponse response) throws IOException {

        // Create a TwiML response and add our friendly message.
        TwiMLResponse twiml = new TwiMLResponse();

        Gather gather = new Gather();
        gather.setNumDigits(1);
        try {
            gather.append(new Say("For sales, press 1. For support, press 2."));
            twiml.append(gather);
            twiml.append(new Redirect("/voice"));
        } catch (TwiMLException e) {
            throw new RuntimeException(e);
        }

        response.setContentType("application/xml");
        response.getWriter().print(twiml.toXML());
    }
}
import com.twilio.twiml.Gather;
import com.twilio.twiml.Redirect;
import com.twilio.twiml.Say;
import com.twilio.twiml.TwiML;
import com.twilio.twiml.TwiMLException;
import com.twilio.twiml.VoiceResponse;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class VoiceServlet extends HttpServlet {

    public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {

        // Create a TwiML response and add our friendly message.
        TwiML twiml = new VoiceResponse.Builder()
                .gather(
                        new Gather.Builder()
                                .numDigits(1)
                                .say(new Say.Builder("For sales, press 1. For support, press 2.").build())
                                .build()
                )
                .redirect(new Redirect.Builder().url("/voice").build())
                .build();

        response.setContentType("application/xml");
        try {
            response.getWriter().print(twiml.toXml());
        } catch (TwiMLException e) {
            throw new RuntimeException(e);
        }
    }
}
SDK Version:
  • 6.x
  • 7.x
import com.twilio.sdk.verbs.Gather;
import com.twilio.sdk.verbs.Redirect;
import com.twilio.sdk.verbs.Say;
import com.twilio.sdk.verbs.TwiMLResponse;
import com.twilio.sdk.verbs.TwiMLException;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class VoiceServlet extends HttpServlet {

    public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {

        // Create a TwiML response and add our friendly message.
        TwiMLResponse twiml = new TwiMLResponse();

        String digits = request.getParameter("Digits");
        if (digits != null) {
            switch (digits) {
                case "1":
                    try {
                        twiml.append(new Say("You selected sales. Good for you!"));
                    } catch (TwiMLException e) {
                        throw new RuntimeException(e);
                    }
                    break;
                case "2":
                    try {
                        twiml.append(new Say("You need support. We will help!"));
                    } catch (TwiMLException e) {
                        throw new RuntimeException(e);
                    }
                    break;
                default:
                    try {
                        twiml.append(new Say("Sorry, I don\'t understand that choice."));
                    } catch (TwiMLException e) {
                        throw new RuntimeException(e);
                    }

                    appendGather(twiml);
                    break;
            }
        } else {
            appendGather(twiml);
        }

        response.setContentType("application/xml");
        response.getWriter().print(twiml.toXML());
    }

    private static TwiMLResponse appendGather(TwiMLResponse twiMLResponse) {
        Gather gather = new Gather();
        gather.setNumDigits(1);
        try {
            gather.append(new Say("For sales, press 1. For support, press 2."));
            twiMLResponse.append(gather);
            twiMLResponse.append(new Redirect("/voice"));
        } catch (TwiMLException e) {
            throw new RuntimeException(e);
        }

        return twiMLResponse;
    }
}
import com.twilio.twiml.*;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class VoiceServlet extends HttpServlet {

    public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {

        // Create a TwiML response and add our friendly message.
        VoiceResponse.Builder builder = new VoiceResponse.Builder();

        String digits = request.getParameter("Digits");
        if (digits != null) {
            switch (digits) {
                case "1":
                    builder.say(new Say.Builder("You selected sales. Good for you!").build());
                    break;
                case "2":
                    builder.say(new Say.Builder("You need support. We will help!").build());
                    break;
                default:
                    builder.say(new Say.Builder("Sorry, I don\'t understand that choice.").build());
                    appendGather(builder);
                    break;
            }
        } else {
            appendGather(builder);
        }

        response.setContentType("application/xml");
        try {
            response.getWriter().print(builder.build().toXml());
        } catch (TwiMLException e) {
            throw new RuntimeException(e);
        }
    }

    private static void appendGather(VoiceResponse.Builder builder) {
        builder.gather(new Gather.Builder()
                        .numDigits(1)
                        .say(new Say.Builder("For sales, press 1. For support, press 2.").build())
                        .build()
                )
                .redirect(new Redirect.Builder().url("/voice").build());
    }
}
SDK Version:
  • 6.x
  • 7.x
import com.twilio.sdk.verbs.Gather;
import com.twilio.sdk.verbs.Redirect;
import com.twilio.sdk.verbs.Say;
import com.twilio.sdk.verbs.TwiMLResponse;
import com.twilio.sdk.verbs.TwiMLException;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class VoiceServlet extends HttpServlet {

    public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {

        // Create a TwiML response and add our friendly message.
        TwiMLResponse twiml = new TwiMLResponse();

        Gather gather = new Gather();
        gather.setNumDigits(1);
        try {
            gather.append(new Say("For sales, press 1. For support, press 2."));
            twiml.append(gather);
            twiml.append(new Redirect("/voice"));
        } catch (TwiMLException e) {
            throw new RuntimeException(e);
        }

        response.setContentType("application/xml");
        response.getWriter().print(twiml.toXML());
    }
}
import com.twilio.twiml.*;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class VoiceServlet extends HttpServlet {

    public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {

        // Create a TwiML response and add our friendly message.
        TwiML twiml = new VoiceResponse.Builder()
                .gather(new Gather.Builder()
                        .numDigits(1)
                        .action("/gather")
                        .say(new Say.Builder("For sales, press 1. For support, press 2.").build())
                        .build()
                )
                .redirect(new Redirect.Builder().url("/voice").build())
                .build();

        response.setContentType("application/xml");
        try {
            response.getWriter().print(twiml.toXml());
        } catch (TwiMLException e) {
            throw new RuntimeException(e);
        }
    }
}
SDK Version:
  • 6.x
  • 7.x
import com.twilio.sdk.verbs.*;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class GatherServlet extends HttpServlet {

    public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {

        // Create a TwiML response and add our friendly message.
        TwiMLResponse twiml = new TwiMLResponse();

        String digits = request.getParameter("Digits");
        if (digits != null) {
            switch (digits) {
                case "1":
                    try {
                        twiml.append(new Say("You selected sales. Good for you!"));
                    } catch (TwiMLException e) {
                        throw new RuntimeException(e);
                    }
                    break;
                case "2":
                    try {
                        twiml.append(new Say("You need support. We will help!"));
                    } catch (TwiMLException e) {
                        throw new RuntimeException(e);
                    }
                    break;
                default:
                    try {
                        twiml.append(new Say("Sorry, I don\'t understand that choice."));
                        twiml.append(new Redirect("/voice"));
                    } catch (TwiMLException e) {
                        throw new RuntimeException(e);
                    }
                    break;
            }
        } else {
            try {
                twiml.append(new Redirect("/voice"));
            } catch (TwiMLException e) {
                throw new RuntimeException(e);
            }
        }

        response.setContentType("application/xml");
        response.getWriter().print(twiml.toXML());
    }
}
import com.twilio.twiml.Redirect;
import com.twilio.twiml.Say;
import com.twilio.twiml.TwiMLException;
import com.twilio.twiml.VoiceResponse;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class GatherServlet extends HttpServlet {

    public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {

        // Create a TwiML response and add our friendly message.
        VoiceResponse.Builder builder = new VoiceResponse.Builder();

        String digits = request.getParameter("Digits");
        if (digits != null) {
            switch (digits) {
                case "1":
                    builder.say(new Say.Builder("You selected sales. Good for you!").build());
                    break;
                case "2":
                    builder.say(new Say.Builder("You need support. We will help!").build());
                    break;
                default:
                    builder.say(new Say.Builder("Sorry, I don\'t understand that choice.").build());
                    builder.redirect(new Redirect.Builder().url("/voice").build());
                    break;
            }
        } else {
            builder.redirect(new Redirect.Builder().url("/voice").build());
        }

        response.setContentType("application/xml");
        try {
            response.getWriter().print(builder.build().toXml());
        } catch (TwiMLException e) {
            throw new RuntimeException(e);
        }
    }
}