Twilio Meeting Notification API

Twilio is a cloud based communication platform, which allows, among others, developers to use their apis to host video calls. In this blog post, we will use the Twilio API, to create a meeting request and also get notified when User joins the meeting.

The first step involves logging into to your Twilio account and creating an API Key for your application. You can login to your Twilio Console and navigate to Accounts-> API Keys & Tokens & and select Create API Key. Please ensure you save your API Key SID and Secret. The secret is shown only once, so copy and store it immediately.

You would also need to store your AccountSid and AuthToken.

Okay, now you have defined all that is required to start programming. Let us define a record to store the configurable details and read it from appsettings.json.

public record TwilioOptions
{
    public string AccountSid { get; set; } = string.Empty;
    public string AuthToken { get; set; } = string.Empty;
    public string ApiKeySid { get; set; } = string.Empty;
    public string ApiSecret { get; set; } = string.Empty;
    public string RoomType { get; set; } = "group"; // Default if not set
    public string Identity { get; set; } = string.Empty;
}

  "TwilioOptions": {
    "AccountSid": "your-account-sid",
    "AuthToken": "your-auth-token",
    "ApiKeySid": "your-api-sid",
    "ApiSecret": "your-api-secret",
    "Identity": "username"
  }

Having defined the configuration, the first step obviously is to read the configuration.

var twilioConfig = builder.Configuration.GetSection(nameof(TwilioOptions)).Get<TwilioOptions>();
if (twilioConfig is null)
    throw new InvalidOperationException("Twilio configuration is missing");
builder.Services.AddSingleton(twilioConfig);

Before we proceed further, let us install the Twilio Rest API Library nuget.

Install-Package Twilio

We are now ready to create our endpoints for creating the meeting and allowing to monitor the meeting.

Create Meeting

To create the meeting, we need to a create a Room and allocate video grants to it. The rooms can be created with the RoomResource.Create.

app.MapGet("/create", (TwilioOptions settings, ILogger<Program> logger, string roomName) =>
    {
        logger.LogInformation($"Attempting to create new room {roomName}");

        TwilioClient.Init(settings.AccountSid, settings.AuthToken);

        var roomResource = RoomResource.Read(uniqueName: roomName, limit: 1).FirstOrDefault();

        if (roomResource is not null)
        {
            logger.LogInformation($"Room {roomName} already exists with status {roomResource.Status}");
        }
        else
        {
            // with postback url
            roomResource = RoomResource.Create(uniqueName: roomName, 
                type: RoomResource.RoomTypeEnum.Group,
                recordParticipantsOnConnect: true,
                statusCallback: new Uri("your-events-end-point-link"), // ๐Ÿ‘ˆ This is key!
    statusCallbackMethod: Twilio.Http.HttpMethod.Post);
        }

        logger.LogInformation(
            @$"Room SID {roomResource!.Sid}, Status {roomResource!.Status}, Type : {roomResource.Type}");

        var videoGrant = new VideoGrant
        {
            Room = roomName
        };

        var token = new Token(settings.AccountSid, settings.ApiKeySid, settings.ApiSecret, settings.Identity,
            grants: [videoGrant]);

        logger.LogInformation($"Token ID : {token.ToJwt()}");
        return new RoomDetails(token.ToJwt(), roomName);
    })
    .WithName("Create")
    .WithOpenApi()
    .Produces(StatusCodes.Status404NotFound)
    .Produces<RoomDetails>();

As you can observe, we have initialized the TwilioClient with the AccountSid and AuthToken from your account. We then check if the room already exists, otherwise we create one using the RoomResource.Create method from the Twilio API library.

roomResource = RoomResource.Create(uniqueName: roomName, 
                type: RoomResource.RoomTypeEnum.Group,
                recordParticipantsOnConnect: true,
                statusCallback: new Uri("your-events-end-point-link"), // ๐Ÿ‘ˆ This is key!
    statusCallbackMethod: Twilio.Http.HttpMethod.Post);

Note the statusCallBack Uri. This is very important for recieving callbacks or notifications about our meeting. We will create this in a shortwhile, in a gist, this is the endpoint, Twilio would be posting updates or sending notifications for every activity in your room.

The next step is to create the video grant for the room and generate a token, which can be passed to the attendee.

var videoGrant = new VideoGrant
        {
            Room = roomName
        };

var token = new Token(settings.AccountSid, settings.ApiKeySid, settings.ApiSecret, settings.Identity,
    grants: [videoGrant]);

Events/Notification

The events/notification endpoint recieves notifications from the Twilio API for activities in your room. It doesn’t do any magic by itself, other than reading the details send by Twilio and displaying it to user (or in this case, logging it)

app.MapPost("/events", async (ILogger<Program> logger,  HttpContext context) =>
{
    using var reader = new StreamReader(context.Request.Body);
    var body = await reader.ReadToEndAsync();
    var parsed = HttpUtility.ParseQueryString(body);

    var eventType = parsed["StatusCallbackEvent"];
    var room = parsed["RoomName"];
    var participant = parsed["ParticipantIdentity"];

    logger.LogInformation($"Twilio Event: {eventType}, Room: {room}, Participant: {participant}");

    return Results.Ok();
})

That’s all we need to do in the server side, now you can use the Create endpoint to generate the meeting token. For testing you can use a simple html page as following to join meeting.

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Twilio Video Manual Test</title>
  <script src="https://media.twiliocdn.com/sdk/js/video/releases/2.24.0/twilio-video.min.js"></script>
  <style>
    video {
      max-width: 300px;
      border-radius: 12px;
      margin: 10px;
    }
  </style>
</head>
<body>
  <h2>Join Twilio Video Room (Manual Token Test)</h2>
  
  <label>Access Token:</label><br>
  <textarea id="token" rows="5" cols="80" placeholder="Paste your Twilio JWT token here"></textarea><br><br>

  <label>Room Name:</label><br>
  <input id="room" value="MyGroupRoom"><br><br>

  <button onclick="joinRoom()">Join Room</button>

  <h3>Local Video</h3>
  <div id="local-media"></div>

  <h3>Remote Participants</h3>
  <div id="remote-media"></div>

  <script>
    async function joinRoom() {
      const token = document.getElementById("token").value.trim();
      const roomName = document.getElementById("room").value.trim();

      if (!token || !roomName) {
        alert("Token and Room Name are required");
        return;
      }

      try {
        const room = await Twilio.Video.connect(token, {
          name: roomName,
          audio: true,
          video: true
        });

        const localMediaContainer = document.getElementById('local-media');
        room.localParticipant.tracks.forEach(publication => {
          if (publication.track) {
            localMediaContainer.appendChild(publication.track.attach());
          }
        });

        room.on('participantConnected', participant => {
          console.log('Participant connected:', participant.identity);
          participant.on('trackSubscribed', track => {
            document.getElementById('remote-media').appendChild(track.attach());
          });
        });

        alert("Connected to Room: " + room.name);
      } catch (error) {
        console.error("Connection failed:", error);
        alert("Connection failed: " + error.message);
      }
    }
  </script>
</body>
</html>


Once user joins the meeting, you will get notification as following

2025-04-11T16:18:34.5225834Z       Twilio Event: participant-disconnected, Room: demo4, Participant: user1
2025-04-11T16:18:34.5461551Z info: Program[0]
2025-04-11T16:18:34.5461784Z       Twilio Event: track-added, Room: demo4, Participant: user1
2025-04-11T16:18:34.5994428Z info: Program[0]
2025-04-11T16:18:34.5995389Z       Twilio Event: track-added, Room: demo4, Participant: user1
2025-04-11T16:18:34.5995662Z info: Program[0]
2025-04-11T16:18:34.5995699Z       Twilio Event: participant-connected, Room: demo4, Participant: user1
2025-04-11T16:18:34.6789293Z info: Program[0]
2025-04-11T16:18:34.6789888Z       Twilio Event: recording-completed, Room: demo4, Participant: user1
2025-04-11T16:18:34.7474175Z info: Program[0]
2025-04-11T16:18:34.7475354Z       Twilio Event: recording-completed, Room: demo4, Participant: user1
2025-04-11T16:18:37.8985755Z info: Program[0]
2025-04-11T16:18:37.8986254Z       Twilio Event: recording-started, Room: demo4, Participant: user1
2025-04-11T16:18:37.9492369Z info: Program[0]
2025-04-11T16:18:37.9492921Z       Twilio Event: recording-started, Room: demo4, Participant: user1

That’s all for now.

Leave a comment