Working with Twilio Video

Has anyone worked with Twilio’s video product successfully in a Phoenix project? They support several backend languages but Elixir isn’t one of them. As far as I can tell all their docs use their own helper libraries as opposed to offering a general API.

ExTwilio looks interesting but is for their phone API. Anybody know how to get started? I’m psyched to play around with video + some Phoenix channels in an app!

(could not tag this post twilio)

I saw @alchemist_in_training (was that you?) asked this twice too on Slack with no response, I’m hoping that someone with more experience can answer but since there isn’t any I’ll give it a shot :smile:

I’ve looked around their docs and here’s what I got:

Since the Twilio Video API is in public beta and the API is still subject to change (as per their JavaScript client docs), I don’t think you’ll get that much unless you dig down their SDKs’ source code.

And I’m not sure what you mean by several backend languages; the getting started guide lists only Java, Objective-C, Swift, and JS, so I think they only provide client SDKs for Android, iOS, and browser.

That said, there’s one mention of video and webRTC in the ExTwillio issues. I think you can start from there.

Thanks for the response! Yeah, to me four is several languages :smiley:

I did dig into the source code but found it a bit of a rabbit hole with the helper libraries farming out work to sub-helper libraries. The only thing the server does in their example apps is create the json web token, so by “general API”, I meant just a spec of what they required in the JWT payload since that’s more straight forward to work from than reverse engineering their bundle of helper libs.

E.g. if an api were documented in RAML or Swagger, what would it say to send?

That comment from @stavro does look like a good starting point!

Ah, that is of course :smile: I meant to contrast between “backend” and “client”, not the number of languages.

True, I was also looking for a more straightforward HTTP API docs but can’t seem to find it, hence my suggestion to dig the code ^^ Perhaps they’ll make one after they got out of beta? (not sure though, public beta seems to be the perfect time to have a public API spec if they really wanted to open it.)

Working version done!

1 Like

I had this working for the beta version of Twilio Video but now that version 1 is officially released, they’ve changed how things work a little bit. Any idea how I would adapt this if I wanted to create a room with a server-side post request to Twilio as outlined here: https://www.twilio.com/docs/api/video/rooms-resource#post-list-resource

Here’s what I’ve got so far, which works, but I need to create a token containing the room name. Any help greatly appreciated!

 def create_room(name) do
api_key = Application.get_env(:myapp, :TWILIO_API_KEY)
api_secret = Application.get_env(:myapp, :TWILIO_API_SECRET)
encoded = Base.encode64("#{api_key}:#{api_secret}")

body = {:form, [UniqueName: name, Type: "peer-to-peer", EnableTurn: "true", StatusCallback: "https://myapp.com/video/callback"]}

HTTPoison.post("https://video.twilio.com/v1/Rooms", body, ["Authorization": "Basic #{encoded}", "Accept": "Application/json; Charset=utf-8"])
end

Edit:
I’ve got a working solution with Twilio Video version 1 now:

# myapp/web/twilio_token.ex
defmodule MyApp.TwilioToken do
  # Generates token for Twilio Programmable video
  # Assumes Joken is in Mix.exs deps and Twilio creds are in app config
  defp tokenize(username, room) do
sid = Application.get_env(:myapp, :TWILIO_ACCOUNT_SID)
api_key = Application.get_env(:myapp, :TWILIO_API_KEY)
api_secret = Application.get_env(:myapp, :TWILIO_API_SECRET)
now = epoch_ts()
exp = now + 3600

payload = %{
  "jti" => "#{api_key}-#{now}",
  "iss" => api_key,
  "sub" => sid,
  "exp" => exp,
  "grants" => %{
    "identity" => username,
    "video": %{
      "room": room
    }
  }
}

payload
|> Joken.token()
|> Joken.with_header_arg("cty", "twilio-fpa;v=1")
|> Joken.with_signer(Joken.hs256(api_secret))
|> Joken.sign()
|> Joken.get_compact()
  end

  defp epoch_ts() do
epoch = {{1970, 1, 1}, {0, 0, 0}}
epoch_i = :calendar.datetime_to_gregorian_seconds(epoch)
now_i = :calendar.datetime_to_gregorian_seconds(:calendar.universal_time)
now_i - epoch_i
  end
  
  def create_room(room, username) do
api_key = Application.get_env(:myapp, :TWILIO_API_KEY)
api_secret = Application.get_env(:myapp, :TWILIO_API_SECRET)
encoded = Base.encode64("#{api_key}:#{api_secret}")
headers = ["Authorization": "Basic #{encoded}"]

{:ok, response} = HTTPoison.get("https://video.twilio.com/v1/Rooms/#{room}", headers)
#does this room already exist? If not, create it
if response.status_code == 404 do
  body = {:form, [UniqueName: room, Type: "peer-to-peer", MaxParticipants: 2, EnableTurn: "true", StatusCallback: "https://myapp.com/callback-url"]}
  {:ok, post_response} = HTTPoison.post("https://video.twilio.com/v1/Rooms", body, headers)
end

tokenize(username, room)
  end

  def config, do: Application.get_env(:myapp, __MODULE__)
  
  
end

Note: this only does some very basic checking to see if the room exists and creates one if it doesn’t and only really handles the happy path. You’ll want to add some logic to handle various failure scenarios.

2 Likes