Wes Hager
Wes Hager

Reputation: 201

Receiving a Call Using Twilio Programmable Voice SDK

I'm following the Quickstart Guide using a python deployment on Heroku and am able to place calls just fine. It's the receiving that I'm having problems with. I've not modified any of the init or delegate logic for either the TVOIncomingCall instance or the PKPushRegistry. And indeed, when I compile, the log says "pushRegistry:didUpdatePushCredentials:forType" and "Successfully registered for VoIP push notifications." This makes me think my incoming handler on my server.py is the problem.

My question is this: What's the proper way, in python, to fire off the VoIP push notification to notify the user of an incoming call? I've enabled VoIP Services in my app and generated a VoIP services certificate (.p12) and received a valid PUSH_CREDENTIAL_SID for my efforts, but what to do with it?

The Quickstart says to hit my server's 'placeCall' endpoint, so I update my Twilio number's incoming path and point it accordingly in the dashboard, but this results in "An Application Error has occurred" response whenever anyone calls my Twilio number.

Here is the relevant code from my server.py:

import os
from flask import Flask, request
from twilio.jwt.access_token import AccessToken, VoiceGrant
from twilio.rest import Client
import twilio.twiml

ACCOUNT_SID = 'ACxxxxxxxx'
API_KEY = 'SKxxxxxxxxxxxxxxxx'
API_KEY_SECRET = 'xxxxxxxxxxxxxxxx'
PUSH_CREDENTIAL_SID = 'CRxxxxxxxxxxxxxxxx'
APP_SID = 'APxxxxxxxxxxxxxxxx'
ACCOUNT_AUTH = 'xxxxxxxxxxxxxxxx'

IDENTITY = 'MyApp' #not literally

app = Flask(__name__)

@app.route('/accessToken')
def token():
  account_sid = os.environ.get("ACCOUNT_SID", ACCOUNT_SID)
  api_key = os.environ.get("API_KEY", API_KEY)
  api_key_secret = os.environ.get("API_KEY_SECRET", API_KEY_SECRET)
  push_credential_sid = os.environ.get("PUSH_CREDENTIAL_SID", PUSH_CREDENTIAL_SID)
  app_sid = os.environ.get("APP_SID", APP_SID)

  grant = VoiceGrant(
    push_credential_sid = push_credential_sid,
    outgoing_application_sid = app_sid
  )

  token = AccessToken(account_sid, api_key, api_key_secret, IDENTITY)
  token.add_grant(grant)

  return str(token)


@app.route('/placeCall', methods=['GET', 'POST'])
def placeCall():
    account_sid = os.environ.get("ACCOUNT_SID", ACCOUNT_SID)
    api_key = os.environ.get("API_KEY", API_KEY)
    api_key_secret = os.environ.get("API_KEY_SECRET", API_KEY_SECRET)
    push_credential_sid = os.environ.get("PUSH_CREDENTIAL_SID", PUSH_CREDENTIAL_SID)
    app_sid = os.environ.get("APP_SID", APP_SID)

    CALLER_ID = request.values.get('From')
    IDENTITY = request.values.get('To')

    #client = Client(api_key, api_key_secret, account_sid)
    client = Client(api_key, api_key_secret, account_sid, app_sid, push_credential_sid)
    call = client.calls.create(to='client:' + IDENTITY, from_= 'client:' + CALLER_ID)
    return str(call.sid)

My Identity value is clearly wrong. I would think it would need to equal a reference to an app instance somehow. In the quickstart python example, IDENTITY = 'voice_test' Also, where does the PUSH_CREDENTIAL_SID come into play?

Thanks in advance.

UPDATE
______

As per @philnash's comment, I've nested the client noun inside the dial verb. Here's my new /incoming endpoint:

@app.route('/incoming', methods=['GET', 'POST'])
def incoming():
    CALLER_ID = request.values.get('From')
    resp = twilio.twiml.Response()
    resp.dial(client = IDENTITY, callerId = CALLER_ID, record = "record-from-answer-dual")
    return str(resp)

This now results in a 'Schema validation warning' in the debugger stating 'unknown or unexpected nested elements' as a possible cause. Am I registering the client incorrectly? The old SDK allowed you to explicitly pass in your clientName as a parameter.

The IDENTITY string in my server.py is:

  1. Defined outside of any method scope.
  2. Used in the generation of an accessToken
  3. Nested in the client noun of the dial verb.

Upvotes: 2

Views: 728

Answers (2)

philnash
philnash

Reputation: 73029

Twilio developer evangelist here.

The /placeCall endpoint is not for your Twilio number's incoming call webhook. In the example it is used to simply generate a call to your client.

Instead, your incoming call webhook should point at an endpoint that will return some TwiML instructing Twilio to dial your client. TwiML is just a subset of XML and the elements you will need are <Dial> with a nested <Client>. Your endpoint should return something like:

<Response>
  <Dial>
    <Client>YOUR_CLIENT_NAME</Client>
  </Dial>
</Response>

Let me know if this helps at all.

Update

Based on the update in the question, we now need to generate this TwiML in Python. You can nest TwiML with the Twilio Python helper library using Python's with syntax. I believe this should work:

@app.route('/incoming', methods=['GET', 'POST'])
def incoming():
    CALLER_ID = request.values.get('From')
    resp = twilio.twiml.Response()
    with resp.dial(callerId = CALLER_ID, record = "record-from-answer-dual") as dial:
        dial.client(IDENTITY)
    return str(resp)

Upvotes: 1

Megan Speir
Megan Speir

Reputation: 3811

Update the PUSH_CREDENTIAL_SID value in the application server's server.py file with your new push credential SID. It goes with the following constants at the top of the file:

ACCOUNT_SID = 'AC***'
API_KEY = 'SK***'
API_KEY_SECRET = '***'
PUSH_CREDENTIAL_SID = 'CR***'
APP_SID = 'AP***'

And in your placeCall function:

def placeCall():
  account_sid = os.environ.get("ACCOUNT_SID", ACCOUNT_SID)
  api_key = os.environ.get("API_KEY", API_KEY)
  api_key_secret = os.environ.get("API_KEY_SECRET", API_KEY_SECRET)
  push_credential_sid = os.environ.get("PUSH_CREDENTIAL_SID", PUSH_CREDENTIAL_SID)
  app_sid = os.environ.get("APP_SID", APP_SID)

It seems like you're also missing the APP_SID from above which is the reference to TwiML app that contains some call instructions.

Upvotes: 0

Related Questions