Using moderated.jitsi.net programmatically

So I was trying to find a way to automatically generate moderated Jitsi meetings like at https://moderated.jitsi.net/, but without human intervention.

I took a look at the source code, and found that if I make a GET request to, let’s say, https://moderated.jitsi.net/rest/rooms/myRoomNameHere, I will receive a functioning joinUrl (https://meet.jit.si/moderated/febf8cf92de0a62c25d76d5481d320e5e6690afd1b9d7d35e6403bde2805650c) and moderatorUrl as a response, and I can even use https://moderated.jitsi.net/myRoomNameHere directly.

Looking further, I don’t even need to do an API call at all, I can calculate everything from my end because SHA256(myRoomNameHere) = febf8cf92de0a62c25d76d5481d320e5e6690afd1b9d7d35e6403bde2805650c, the exact same bit as the joinUrl.

So my question is, as this behaviour is undocumented… am I allowed to do this? Is there any problem if I automatically generate moderated links for my app? Are people supposed to be paying 8x8 or mounting their own instance if they want to create unmoderated rooms? Can I rely on this being stable or is it going to break at some point?

Thank you

thank you for figuring out that this was a SHA256 hash. To take this a step further, that hash also works to give you the dial in number and PIN ahead of time here: https://meet.jit.si/moderated/static/dialInInfo.html?room=HASHHERE

You would want to make sure your moderated link name is very unique to prevent unexpected guests who could also calculate the guest link and “Rickroll” your meeting, or worse.

Apparently it’s possible to get the meeting PIN (AKA the meeting id) as JSON programmatically this way: https://api.jitsi.net/conferenceMapper?conference=HASHHERE@conference.moderated.meet.jit.si

You can also get the dial in numbers in JSON this way:
https://api.jitsi.net/phoneNumberList?conference=HASHHERE@conference.moderated.meet.jit.si

Here’s something I hacked together. Feel free to improve upon it and share it back with the community.

 < html >
 < head >
 < script type = "text/javascript" >
    var strModerator;
async function hash(string) {
    const utf8 = new TextEncoder().encode(string);
    return crypto.subtle.digest('SHA-256', utf8).then((hashBuffer) => {
        const hashArray = Array.from(new Uint8Array(hashBuffer));
        const hashHex = hashArray
            .map((bytes) => bytes.toString(16).padStart(2, '0'))
            .join('');
        document.getElementById("moderatorLink").value = "https://moderated.jitsi.net/" + string;
        document.getElementById("guestLink").value = "https://meet.jit.si/moderated/" + hashHex;
        var confid = new XMLHttpRequest();
        var url = 'https://api.jitsi.net/conferenceMapper?conference=' + hashHex + '@conference.moderated.meet.jit.si';
        confid.open("GET", url, true);
        confid.setRequestHeader("Content-Type", "application/json");
        confid.onreadystatechange = function () {
            if (confid.readyState === 4 && confid.status === 200) {
                var json1 = JSON.parse(confid.responseText);
                document.getElementById("mtgPIN").value = json1.id;
            }
        };
        confid.send();

        var confphone = new XMLHttpRequest();
        var url = 'https://api.jitsi.net/phoneNumberList?conference=' + hashHex + '@conference.moderated.meet.jit.si';
        confphone.open("GET", url, true);
        confphone.setRequestHeader("Content-Type", "application/json");
        confphone.onreadystatechange = function () {
            if (confphone.readyState === 4 && confid.status === 200) {
                var json2 = JSON.parse(confphone.responseText);
                document.getElementById("usPhone").value = json2.numbers.US[0];
            }
        };

        confphone.send();

    });
}

 <  / script >
 <  / head >
 < body >
 < label for  = 'strModerator' > Moderator string:  <  / label >  < br >
     < input type = 'text' id = 'strModerator' name = 'strModerator' size = '40' /  >  < br >
     < input type = 'button' value = 'Submit' onClick = 'hash(document.getElementById("strModerator").value)' /  >
     < br >  < br >  < br >
     < label for  = 'moderatorLink' > Moderator link:  <  / label >  < br >
         < input type = 'text' id = 'moderatorLink' name = 'moderatorLink' readonly size = '50' value = 'ID will appear here' /  >  < br >  < br >
         < label for  = 'guestLink' > Guest link:  <  / label >  < br >
             < input type = 'text' id = 'guestLink' name = 'guestLink' readonly size = '100' value = 'ID will appear here' /  >  < br >  < br >
             < label for  = 'mtgPIN' > Meeting PIN:  <  / label >  < br >
                 < input type = 'text' id = 'mtgPIN' name = 'mtgPIN' readonly value = 'PIN' /  >  < br >  < br >
                 < label for  = 'usPhone' > US Dial - In Number:  <  / label >  < br >
                     < input type = 'text' id = 'usPhone' name = 'usPhone' readonly value = 'Number' /  >  < br >  < br >
                    <  / body >
1 Like