Running a Startup on AWS? Get Funding With AWS JumpStart. Click Here to Learn More

2021 Fillmore Street #1128

}

24/7 solutions

Issues Bridging Twilio Calls between Customer and Agent

We are seeing issues bridging Twilio calls between the customer and agent with IVR, whisper, wait music, and call recording. Scenario: We have clients calling in. They enter an id to the Twilio based IVR. We play wait music. We call the agent. When the agent picks up the phone, we play the whisper of […]

We are seeing issues bridging Twilio calls between the customer and agent with IVR, whisper, wait music, and call recording.

Scenario:
We have clients calling in. They enter an id to the Twilio based IVR. We play wait music. We call the agent. When the agent picks up the phone, we play the whisper of the ID. We give the agent the opportunity to look up the number. We then bridge the call and launch call recording.
Issue:
In certain situations, we lose the inbound call. We’re not entirely sure why. We don’t see any errors.
Call Flow:
1. Calls the Agent. Enqueue’s the current customer call.
File: CallAgentService.java

    public Verb callAgent(ServletRequest req, Category twilioSettings, JSONObject member) throws Exception {
        String numberCalled = getNumberCalled(req, member);
        String callSid = req.getParameter(Constants.TWILIO_PARAM_CALLSID);
        CallStatus status = callStatusRepository.findByCode(Constants.CALL_STATUS_KNOWN_ID);
        String digits = req.getParameter(Constants.TWILIO_PARAM_DIGITS);
        if(StringUtils.isNotBlank(digits)) {
            TwilioConfigUtils.saveCallStat(callLogStatRepository, callSid, status, digits);
        } else {
            TwilioConfigUtils.saveCallStat(callLogStatRepository, callSid, status);
        }
        CallLog callLog = callLogRepository.findOne(callSid);
        saveCallLog(callLog, member);
        Enqueue enqueue = new Enqueue(callSid);
        enqueue.setWaitUrl(Constants.ENQUEUE_WAIT_URL);
        enqueue.setWaitUrlMethod(HttpMethod.GET.toString());
        Call call = TwilioConfigUtils.callAgent(numberCalled, twilioSettings, env);
        if (call != null) {
            TwilioConfigUtils.saveNewCall(call, callLog, callLogRepository);
            status = callStatusRepository.findByCode(Constants.CALL_STATUS_AGENT_START);
            TwilioConfigUtils.saveCallStat(callLogStatRepository, callSid, status);
            return enqueue;
        } else {
            Say sayError = new Say("We are sorry an error has occurred.");
            sayError.setLanguage(Constants.SPOKEN_LANGUAGE);
            return sayError;
        }
    }

2. Creates the outbound call to the agent with the whisper.
File: TwilioConfigUtils.java

    public static Call callAgent(String called, Category twilioSettings, Environment env) {
        log.debug("Calling agent");
        try {
            String acctSid = getTwilioConfigByCode(twilioSettings, Constants.TWILIO_ACCOUNT_SID);
            String authToken = getTwilioConfigByCode(twilioSettings, Constants.TWILIO_AUTH_TOKEN);
            String phoneNumber = getTwilioConfigByCode(twilioSettings, Constants.TWILIO_AGENT_PHONE);
            TwilioRestClient client = new TwilioRestClient(acctSid, authToken);
            // Build a filter for the CallList
            List params = new ArrayList<>();
            String localhost = env.getProperty("rest.main.url");
            params.add(new BasicNameValuePair("Url", localhost + Constants.WHISPER_AGENT));
            params.add(new BasicNameValuePair("To", phoneNumber));
            params.add(new BasicNameValuePair("From", called));
            params.add(new BasicNameValuePair("Method", HttpMethod.GET.toString()));
            params.add(new BasicNameValuePair("Record", Boolean.TRUE.toString()));
            params.add(new BasicNameValuePair("StatusCallback", localhost + Constants.AUDIO_RECORD_CALLBACK));
            params.add(new BasicNameValuePair("StatusCallbackMethod", HttpMethod.GET.toString()));
            CallFactory callFactory = client.getAccount().getCallFactory();
            Call call = callFactory.create(params);
            return call;
        } catch (Exception e) {
            log.error(e);
        } finally {
            log.debug("Agent called");
        }
        return null;
    }


Agent Call on Twilio Logs

Twilio Log - Outbound Calls to Agent
Twilio Log - Outbound Calls to Agent


3. Whispers to the Agent
File: WhisperAgentServlet.java

 @Override
    public void service(ServletRequest req, ServletResponse response) throws ServletException, IOException {
        log.debug("Entering WhisperAgentServlet");
        String callSid = req.getParameter(Constants.TWILIO_PARAM_CALLSID);
        CallLog callLog = callLogRepository.findOne(callSid);
        CallStatus status = callStatusRepository.findByCode(Constants.CALL_STATUS_AGENT_CONNECT);
        TwilioConfigUtils.saveCallStat(callLogStatRepository, callLog.getParentCallSid(), status);
        TwiMLResponse twiml = new TwiMLResponse();
        Long clientId = callLog.getClientId();
        try {
            if (callLog == null) {
                log.debug(twiml.toXML());
                response.setContentType(Constants.CONTENT_TYPE_XML);
                response.getWriter().print(twiml.toXML());
                return;
            }
            Category twilioSettings = categoryRepository.findCategoryByCodeAndClientId(Constants.TWILIO_SETTINGS, clientId);
            String msg;
            if (StringUtils.isNotBlank(callLog.getMemberName()) && StringUtils.isNotBlank(callLog.getMemberLastName())) {
                msg = TwilioConfigUtils.getTwilioConfigByCode(twilioSettings, Constants.TWILIO_WHISPER_AGENT_MENU_MEMBER_INFO);
                Map<String, String> map = new HashMap<>();
                map.put(Constants.TWILIO_MSG_VAR_FIRSTNAME, callLog.getMemberName());
                map.put(Constants.TWILIO_MSG_VAR_LASTNAME, callLog.getMemberLastName());
                String memberId = callLog.getCsMemberId();
                if (StringUtils.isBlank(memberId)) {
                    memberId = callLog.getClientMemberId();
                } else {
                    memberId = "1111" + StringUtils.leftPad(memberId, 8, '0');
                }
                StringBuilder sbMemberId = new StringBuilder();
                for (char c : memberId.toCharArray()) {
                    sbMemberId.append(c).append(", ");
                }
                map.put(Constants.TWILIO_MSG_VAR_MEMBER_ID, sbMemberId.toString().trim());
                msg = StrSubstitutor.replace(msg, map);
            } else {
                msg = TwilioConfigUtils.getTwilioConfigByCode(twilioSettings, Constants.TWILIO_WHISPER_AGENT_MENU_NO_MEMBER_INFO);
            }
            Say incomingCall = new Say(msg);
            incomingCall.setLanguage(Constants.SPOKEN_LANGUAGE);
            Dial dial = new Dial();
            Queue queue = new Queue(callLog.getParentCallSid());
            dial.append(queue);
            twiml.append(incomingCall);
            twiml.append(dial);
        } catch (TwiMLException e) {
            log.error(e);
            Say error = new Say("We are sorry an error has occurred while connecting.");
            error.setLanguage(Constants.SPOKEN_LANGUAGE);
            try {
                twiml = new TwiMLResponse();
                twiml.append(error);
            } catch (TwiMLException e1) {
                log.error(e1);
            }
            callLog = callLogRepository.findOne(callLog.getParentCallSid());
            Category twilioSettings = categoryRepository.findCategoryByCodeAndClientId(Constants.TWILIO_SETTINGS, clientId);
            TwilioConfigUtils.releaseQueueWithError(callLog, twilioSettings, env);
        }
        log.debug(twiml.toXML());
        response.setContentType(Constants.CONTENT_TYPE_XML);
        response.getWriter().print(twiml.toXML());
    }

Customer Call on Twilio Logs
Where does the Hangup event come from? When we do our tests we are still on the call?!??!

Twilio Log of Inbound Client Call with IVR
Twilio Log of Inbound Client Call with IVR
Joel Garcia
Joel Garcia

Joel Garcia has been building AllCode since 2015. He’s an innovative, hands-on executive with a proven record of designing, developing, and operating Software-as-a-Service (SaaS), mobile, and desktop solutions. Joel has expertise in HealthTech, VoIP, and cloud-based solutions. Joel has experience scaling multiple start-ups for successful exits to IMS Health and Golden Gate Capital, as well as working at mature, industry-leading software companies. He’s held executive engineering positions in San Francisco at TidalWave, LittleCast, Self Health Network, LiveVox acquired by Golden Gate Capital, and Med-Vantage acquired by IMS Health.

Related Articles

Amazon Web Services – CodeCatalyst

Amazon Web Services – CodeCatalyst

When a development team is building out an application, it helps to have access to the same resources, have the tools for planning and testing, and to have access to the application all in one place. CodeCatalyst comes with a slew of continuous integration/continuous development (CI/CD) tools and can leverage other AWS services and be connected to other AWS projects on an account. As a collaborative tool, it is easy to introduce new members into the project and to log all activity or all tests from a single dashboard. It’s a complete package of all the tools needed to securely work on every step of an application’s lifecycle.

The Definitive Guide to AWS Pricing

The Definitive Guide to AWS Pricing

Perhaps the biggest issue with AWS that its competitors edge out on is the confusing pricing model. It does promise the capacity to help users save significantly on funds that otherwise by avoiding spending on unnecessary resources, but getting to that point isn’t always clear. We will be covering in greater detail how this works.

Using Stable-Diffusion and Dreambooth to create personalized AI art – Part 2 of 3

Using Stable-Diffusion and Dreambooth to create personalized AI art – Part 2 of 3

DreamBooth is a deep learning generation model used to fine-tune existing text-to-image models, developed by researchers from Google Research and Boston University in 2022. Originally developed using Google's own Imagen text-to-image model, DreamBooth implementations can be applied to other text-to-image models, where it can allow the model to generate more fine-tuned and personalised outputs after training on three to five images of a subject.

Download our 10-Step Cloud Migration ChecklistYou'll get direct access to our full-length guide on Google Docs. From here, you will be able to make a copy, download the content, and share it with your team.