I landed on this problem when I received a work email with the subject line "Time Sensitive" and that we needed to have "branded" Zoom email notifications next week for a client delivery. In my head this means adding a header banner, footer, and adding active social icons. First thing I did was look at the existing templates that Zoom has and looked at a string that does not look familiar to me, Freemarker!
I am a bit familiar writing in HTML, CSS and a little bit of javascript so you can say I am not a programmer. FreeMarker is something I am not familiar with. Doing a quick google search did not give me any result on a template or a written code, and I ended up with a forum post from another individual who had the same problem.
After, maybe an hour, of trying to understand how to use FreeMarker with HTML, and bit of guidance from my web dev SO, it finally clicked. Working with the FreeMarker directives means making sure I do not accidentally lose/delete the directives provided by Zoom, because I have no clue which directives are allowed by Zoom. I also assumed that the provided directives are necessary to generate Zoom call information so I definitely do not want to mess with those.
How did I know that what I have written worked? Don't trust the "Preview" option on Zoom, instead send yourself a sample email and view it both on desktop and mobile. Here's a desktop view on my email:
After, maybe an hour, of trying to understand how to use FreeMarker with HTML, and bit of guidance from my web dev SO, it finally clicked. Working with the FreeMarker directives means making sure I do not accidentally lose/delete the directives provided by Zoom, because I have no clue which directives are allowed by Zoom. I also assumed that the provided directives are necessary to generate Zoom call information so I definitely do not want to mess with those.
How did I know that what I have written worked? Don't trust the "Preview" option on Zoom, instead send yourself a sample email and view it both on desktop and mobile. Here's a desktop view on my email:
I hope this is helpful to any of you, whether you are a technical or non-technical staff at your company, that's been handed off this same task. Connect with me!
source code: zoom meeting invite using freemarker
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type"/>
</head>
<body style="height: 100% !important; width: 100% !important; background-color: #ffffff; margin: 0; padding: 0;">
<table border="0" cellpadding="0" cellspacing="0" align="center" style="font-size: 13px;color:#39394d;font-family: Arial;background-color: #ffffff; width: 100%; height: 100%;padding-bottom: 10px;" border="0" cellpadding="0" cellspacing="0" align="center">
<tr>
<td style="vertical-align: top">
<table border="0" cellpadding="0" cellspacing="0" align="center" style="width: 592px;">
<tr>
<td style="vertical-align: top;padding: 0px background-color: #f4f5f9">
<table border="0" cellpadding="0" cellspacing="0" align="center"
style="margin-left: 0px;margin-right: 0px;width: 100%;background-color: #ffffff;padding-bottom: 37px;">
</table>
<table border="0" cellpadding="0" cellspacing="0" align="center"
style="margin-left: 0px;margin-right: 0px;width: 100%;background-color: #ffffff;padding-bottom: 37px;">
<tr>
<td style="padding-top: 30px;padding-bottom: 10px;background-color:#ffffff;text-align: center">
<img src="https://us02web.zoom.us/account/branding/p/7dec6bcf-5bd3-4bc7-b0ef-e3d25f6cb1a9.png">
</td>
</tr>
</table>
<tr>
<td style="padding-left: 28px;padding-right: 28px;">
<table border="0" cellpadding="0" cellspacing="0" align="center"
style="width: 100%;font-size: 13px;color: #39394d;font-family: Arial;">
<tr>
<td colspan="2" style="vertical-align: top; padding-top: 10px;">
Join ${siteName} Meeting:
${meetingUrl}
</td>
</tr>
<tr>
<td colspan="2" style="vertical-align: top; padding-top: 10px;">
Meeting ID: ${meetingNumber}
</td>
</tr>
<tr>
<td colspan="2" style="vertical-align: top; padding-top: 10px;">
<#if password??>
Passcode: ${password}
</#if>
</td>
</tr>
<tr>
<td colspan="2" style="vertical-align: top; padding-top: 10px;">
<#if enablePSTN>
<#assign phoneTapPwd = h323Password?? && enablePSTNPasswordProtected?? && !(isUserTSPEnabled??) >
<#if isUserTSPEnabled?? && isUserTSPEnabled>
</td>
</tr>
<tr>
<td colspan="2" style="vertical-align: top; padding-top: 10px;">
Dial
<#if tspNumbersInfo?? && ((tspNumbersInfo?size) > 0)>
<#list tspNumbersInfo as tspNumberInfo>
${tspNumberInfo}
</#list>
</#if>
</td>
</tr>
<tr>
<td colspan="2" style="vertical-align: top; padding-top: 10px;">
${(tspPsdTitle!'')?html}: ${(tspPsd!'')?html}
<#elseif pickedNumbers?? && ((pickedNumbers?size) > 0)>
<#if supportOneTap?? && supportOneTap>
One tap mobile:
</td>
</tr>
<tr>
<td>
${pickedNumbers[0].displayNumber?replace(' ','')?replace('(0)','')},,${number?c}#<#if phoneTapPwd>,,,,,,0#,,${h323Password}#
</#if>${pickedNumbers[0].countryName!'US'}<#if pickedNumbers[0].free> Toll-free</#if>
<#if ((pickedNumbers[0].cityName)?? && (pickedNumbers[0].cityName)?length gt 1)> (${pickedNumbers[0].cityName?html}
<#if (pickedNumbers[0].labels)?? && (pickedNumbers[0].labels)?length gt 1>,${pickedNumbers[0].labels?html})<#else>)</#if>
<#else><#if (pickedNumbers[0].labels)?? && (pickedNumbers[0].labels)?length gt 1> (${pickedNumbers[0].labels?html})</#if></#if>
</td>
</tr>
<tr>
<td colspan="2" style="vertical-align: top;">
<#if ((pickedNumbers?size) > 1 && pickedNumbers[1].country == pickedNumbers[0].country)>${pickedNumbers[1].displayNumber?replace(' ','')?replace('(0)','')},,${number?c}#<#if phoneTapPwd>,,,,,,0#,,${h323Password}#</#if> ${pickedNumbers[0].countryName!'US'}<#if pickedNumbers[1].free> Toll-free</#if><#if ((pickedNumbers[1].cityName)?? && (pickedNumbers[1].cityName)?length gt 1)> (${pickedNumbers[1].cityName?html}<#if (pickedNumbers[1].labels)?? && (pickedNumbers[1].labels)?length gt 1>,${pickedNumbers[1].labels?html})<#else>)</#if><#else><#if (pickedNumbers[1].labels)?? && (pickedNumbers[1].labels)?length gt 1> (${pickedNumbers[1].labels?html})</#if></#if>
</#if>
</td>
</tr>
<tr>
</#if>
<td colspan="2" style="vertical-align: top; padding-top: 10px;">
Dial by your location:
<#list pickedNumbers as pickedNumber>
</br>
${pickedNumber.displayNumber}
<#if pickedNumber.free>${pickedNumber.countryName!'US'} Toll-free<#else>${pickedNumber.countryName!'US'}</#if>
<#if ((pickedNumber.cityName)?? && (pickedNumber.cityName)?length gt 1)> (${pickedNumber.cityName?html}
<#if (pickedNumber.labels)?? && (pickedNumber.labels)?length gt 1>,${pickedNumber.labels?html})<#else>)</#if>
<#else>
<#if (pickedNumber.labels)?? && (pickedNumber.labels)?length gt 1> (${pickedNumber.labels?html})</#if></#if>
</#list>
</td>
</tr>
<tr>
<td colspan="2" style="vertical-align: top; padding-top: 10px;">
Meeting ID: ${meetingNumber}
<#else>
<#if supportOneTap?? && supportOneTap>
One tap mobile
${tollNumbers[0]?replace(' ','')?replace('(0)','')},,${number?c}#<#if phoneTapPwd>,,,,,,0#,,${h323Password}#</#if> ${tollCountry!'US'} Toll
<#if ((tollNumbers?size) > 1)>${tollNumbers[1]?replace(' ','')?replace('(0)','')},,${number?c}#<#if phoneTapPwd>,,,,,,0#,,${h323Password}#</#if> ${tollCountry!'US'} Toll
</#if>
</#if>
</td>
</tr>
<tr>
<td colspan="2" style="vertical-align: top; padding-top: 10px;">
Dial by your location
${tollNumbers[0]} ${tollCountry!'US'} Toll
<#if ((tollNumbers?size) > 1)>${tollNumbers[1]} ${tollCountry!'US'} Toll</#if>
<#if premiumNumbers?? && ((premiumNumbers?size) > 0)>
<#list premiumNumbers as pmNum>
${pmNum.displayNumber} ${pmNum.countryName!'US'} Toll
</#list>
</#if>
</td>
</tr>
<tr>
<td colspan="2" style="vertical-align: top; padding-top: 10px;">
<#if tollFreeNumbers?? && ((tollFreeNumbers?size) > 0)>
<#list tollFreeNumbers as tfreeNum>
${tfreeNum} ${tollFreeCountrys[tfreeNum_index]!'US'} Toll-free
</#list>
</#if>
</td>
</tr>
<tr>
<td colspan="2" style="vertical-align: top; padding-top: 10px;">
Meeting ID: ${meetingNumber}
</#if>
<#if h323Password?? && enablePSTNPasswordProtected?? && !(isUserTSPEnabled??)>
Passcode: ${h323Password}
</#if>
</td>
</tr>
<tr>
<td colspan="2" style="vertical-align: top; padding-top: 10px;">
<#if showInternationalNumbersLink?? && showInternationalNumbersLink>
Find your local number: ${teleConferenceUrl}
</#if>
<#elseif useOtherAudioConference>
Join by phone
${otherAudioConferenceInfo!''}
</#if>
</td>
</tr>
<tr>
<td colspan="2" style="vertical-align: top; padding-top: 10px;">
<#if h323Gateway?? && ((h323Gateway?size) > 0)>
<#if isCRC?? && isCRC>
Join by SIP
${number?c}${(sipDomain!'@zoomcrc.com')?html}
<#else>
Join by SIP
${number?c}@${h323Gateway[0]}
<#if ((h323Gateway?size) > 1)>
${number?c}@${h323Gateway[1]}
</#if>
</#if>
Join by H.323
<#list h323Gateway as rc>
${rc}
</#list>
Meeting ID: ${meetingNumber}
<#if h323Password??>
Passcode: ${h323Password}
</#if>
<#if shortSIPUrl??>
More H.323 IP addresses: ${shortSIPUrl}
</#if>
</#if>
</td>
</tr>
<tr>
<td colspan="2" style="vertical-align: top; padding-top: 10px;">
<#if enableLync??>
Join by Skype for Business
${lyncUrl}
</#if>
</td>
</tr>
<tr>
<td colspan="2" style="vertical-align: top; padding-top: 10px;">
<#if moreCustomizedContent??>
${moreCustomizedContent}
</#if>
</td>
</tr>
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%">
<tbody>
<tr>
<td>
<img src="https://us02web.zoom.us/account/branding/p/8ea99ae5-b914-4976-a41c-f449d0eb7d61.png">
</td>
</tr>
</tbody>
</table>
</table>
</td>
</tr>
<tr>
<td style="padding-top: 30px; padding-bottom: 30px; background-color: #FFFFFF; border-top-color:#F0F2F4; border-top-style: solid; border-top-width: 1px;">
<table align="center" border="0" cellpadding="0" cellspacing="0">
<tbody>
<tr>
<td>
<a href="https://www.instagram.com/integrated.work/" style="text-decoration: none;">
<img alt="" border="0" height="25" src="https://us02web.zoom.us/account/branding/p/32de5379-2c9a-4425-ad66-f86bb6c2d0f8.png"
style="width: 25px; height: 25px; border-width: 0px; border-style: solid;"
/>
</a>
</td>
<td style="padding-left:10px; padding-right: 10px;">
<a href="https://www.linkedin.com/company/integratedwork"
style="text-decoration: none;">
<img alt="" border="0" height="25" src="https://us02web.zoom.us/account/branding/p/5ca521c7-cb34-489e-9a17-02061732ceda.png"
style="width: 25px; height: 25px; border-width: 0px; border-style: solid;"
/>
</a>
</td>
<td>
<a href="https://integratedwork.com/blog" style="text-decoration: none;">
<img alt="" border="0" src="https://us02web.zoom.us/account/branding/p/3c36a454-3b66-480a-b957-62cc785fc74d.png"
style="width: 25px; height: 25px; border-width: 0px; border-style: solid;"
/>
</a>
</td>
</tr>
</tbody>
</table>
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%">
<tbody>
<tr>
<td style="font-family:helvetica,arial; font-size:12px; line-height:18px; color:#999999; text-align:center; padding-top: 10px;">
Copyright ©2020 Integrated Work, Inc. All rights reserved.
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta content="text/html; charset=utf-8" http-equiv="Content-Type"/>
</head>
<body style="height: 100% !important; width: 100% !important; background-color: #ffffff; margin: 0; padding: 0;">
<table border="0" cellpadding="0" cellspacing="0" align="center" style="font-size: 13px;color:#39394d;font-family: Arial;background-color: #ffffff; width: 100%; height: 100%;padding-bottom: 10px;" border="0" cellpadding="0" cellspacing="0" align="center">
<tr>
<td style="vertical-align: top">
<table border="0" cellpadding="0" cellspacing="0" align="center" style="width: 592px;">
<tr>
<td style="vertical-align: top;padding: 0px background-color: #f4f5f9">
<table border="0" cellpadding="0" cellspacing="0" align="center"
style="margin-left: 0px;margin-right: 0px;width: 100%;background-color: #ffffff;padding-bottom: 37px;">
</table>
<table border="0" cellpadding="0" cellspacing="0" align="center"
style="margin-left: 0px;margin-right: 0px;width: 100%;background-color: #ffffff;padding-bottom: 37px;">
<tr>
<td style="padding-top: 30px;padding-bottom: 10px;background-color:#ffffff;text-align: center">
<img src="https://us02web.zoom.us/account/branding/p/7dec6bcf-5bd3-4bc7-b0ef-e3d25f6cb1a9.png">
</td>
</tr>
</table>
<tr>
<td style="padding-left: 28px;padding-right: 28px;">
<table border="0" cellpadding="0" cellspacing="0" align="center"
style="width: 100%;font-size: 13px;color: #39394d;font-family: Arial;">
<tr>
<td colspan="2" style="vertical-align: top; padding-top: 10px;">
Join ${siteName} Meeting:
${meetingUrl}
</td>
</tr>
<tr>
<td colspan="2" style="vertical-align: top; padding-top: 10px;">
Meeting ID: ${meetingNumber}
</td>
</tr>
<tr>
<td colspan="2" style="vertical-align: top; padding-top: 10px;">
<#if password??>
Passcode: ${password}
</#if>
</td>
</tr>
<tr>
<td colspan="2" style="vertical-align: top; padding-top: 10px;">
<#if enablePSTN>
<#assign phoneTapPwd = h323Password?? && enablePSTNPasswordProtected?? && !(isUserTSPEnabled??) >
<#if isUserTSPEnabled?? && isUserTSPEnabled>
</td>
</tr>
<tr>
<td colspan="2" style="vertical-align: top; padding-top: 10px;">
Dial
<#if tspNumbersInfo?? && ((tspNumbersInfo?size) > 0)>
<#list tspNumbersInfo as tspNumberInfo>
${tspNumberInfo}
</#list>
</#if>
</td>
</tr>
<tr>
<td colspan="2" style="vertical-align: top; padding-top: 10px;">
${(tspPsdTitle!'')?html}: ${(tspPsd!'')?html}
<#elseif pickedNumbers?? && ((pickedNumbers?size) > 0)>
<#if supportOneTap?? && supportOneTap>
One tap mobile:
</td>
</tr>
<tr>
<td>
${pickedNumbers[0].displayNumber?replace(' ','')?replace('(0)','')},,${number?c}#<#if phoneTapPwd>,,,,,,0#,,${h323Password}#
</#if>${pickedNumbers[0].countryName!'US'}<#if pickedNumbers[0].free> Toll-free</#if>
<#if ((pickedNumbers[0].cityName)?? && (pickedNumbers[0].cityName)?length gt 1)> (${pickedNumbers[0].cityName?html}
<#if (pickedNumbers[0].labels)?? && (pickedNumbers[0].labels)?length gt 1>,${pickedNumbers[0].labels?html})<#else>)</#if>
<#else><#if (pickedNumbers[0].labels)?? && (pickedNumbers[0].labels)?length gt 1> (${pickedNumbers[0].labels?html})</#if></#if>
</td>
</tr>
<tr>
<td colspan="2" style="vertical-align: top;">
<#if ((pickedNumbers?size) > 1 && pickedNumbers[1].country == pickedNumbers[0].country)>${pickedNumbers[1].displayNumber?replace(' ','')?replace('(0)','')},,${number?c}#<#if phoneTapPwd>,,,,,,0#,,${h323Password}#</#if> ${pickedNumbers[0].countryName!'US'}<#if pickedNumbers[1].free> Toll-free</#if><#if ((pickedNumbers[1].cityName)?? && (pickedNumbers[1].cityName)?length gt 1)> (${pickedNumbers[1].cityName?html}<#if (pickedNumbers[1].labels)?? && (pickedNumbers[1].labels)?length gt 1>,${pickedNumbers[1].labels?html})<#else>)</#if><#else><#if (pickedNumbers[1].labels)?? && (pickedNumbers[1].labels)?length gt 1> (${pickedNumbers[1].labels?html})</#if></#if>
</#if>
</td>
</tr>
<tr>
</#if>
<td colspan="2" style="vertical-align: top; padding-top: 10px;">
Dial by your location:
<#list pickedNumbers as pickedNumber>
</br>
${pickedNumber.displayNumber}
<#if pickedNumber.free>${pickedNumber.countryName!'US'} Toll-free<#else>${pickedNumber.countryName!'US'}</#if>
<#if ((pickedNumber.cityName)?? && (pickedNumber.cityName)?length gt 1)> (${pickedNumber.cityName?html}
<#if (pickedNumber.labels)?? && (pickedNumber.labels)?length gt 1>,${pickedNumber.labels?html})<#else>)</#if>
<#else>
<#if (pickedNumber.labels)?? && (pickedNumber.labels)?length gt 1> (${pickedNumber.labels?html})</#if></#if>
</#list>
</td>
</tr>
<tr>
<td colspan="2" style="vertical-align: top; padding-top: 10px;">
Meeting ID: ${meetingNumber}
<#else>
<#if supportOneTap?? && supportOneTap>
One tap mobile
${tollNumbers[0]?replace(' ','')?replace('(0)','')},,${number?c}#<#if phoneTapPwd>,,,,,,0#,,${h323Password}#</#if> ${tollCountry!'US'} Toll
<#if ((tollNumbers?size) > 1)>${tollNumbers[1]?replace(' ','')?replace('(0)','')},,${number?c}#<#if phoneTapPwd>,,,,,,0#,,${h323Password}#</#if> ${tollCountry!'US'} Toll
</#if>
</#if>
</td>
</tr>
<tr>
<td colspan="2" style="vertical-align: top; padding-top: 10px;">
Dial by your location
${tollNumbers[0]} ${tollCountry!'US'} Toll
<#if ((tollNumbers?size) > 1)>${tollNumbers[1]} ${tollCountry!'US'} Toll</#if>
<#if premiumNumbers?? && ((premiumNumbers?size) > 0)>
<#list premiumNumbers as pmNum>
${pmNum.displayNumber} ${pmNum.countryName!'US'} Toll
</#list>
</#if>
</td>
</tr>
<tr>
<td colspan="2" style="vertical-align: top; padding-top: 10px;">
<#if tollFreeNumbers?? && ((tollFreeNumbers?size) > 0)>
<#list tollFreeNumbers as tfreeNum>
${tfreeNum} ${tollFreeCountrys[tfreeNum_index]!'US'} Toll-free
</#list>
</#if>
</td>
</tr>
<tr>
<td colspan="2" style="vertical-align: top; padding-top: 10px;">
Meeting ID: ${meetingNumber}
</#if>
<#if h323Password?? && enablePSTNPasswordProtected?? && !(isUserTSPEnabled??)>
Passcode: ${h323Password}
</#if>
</td>
</tr>
<tr>
<td colspan="2" style="vertical-align: top; padding-top: 10px;">
<#if showInternationalNumbersLink?? && showInternationalNumbersLink>
Find your local number: ${teleConferenceUrl}
</#if>
<#elseif useOtherAudioConference>
Join by phone
${otherAudioConferenceInfo!''}
</#if>
</td>
</tr>
<tr>
<td colspan="2" style="vertical-align: top; padding-top: 10px;">
<#if h323Gateway?? && ((h323Gateway?size) > 0)>
<#if isCRC?? && isCRC>
Join by SIP
${number?c}${(sipDomain!'@zoomcrc.com')?html}
<#else>
Join by SIP
${number?c}@${h323Gateway[0]}
<#if ((h323Gateway?size) > 1)>
${number?c}@${h323Gateway[1]}
</#if>
</#if>
Join by H.323
<#list h323Gateway as rc>
${rc}
</#list>
Meeting ID: ${meetingNumber}
<#if h323Password??>
Passcode: ${h323Password}
</#if>
<#if shortSIPUrl??>
More H.323 IP addresses: ${shortSIPUrl}
</#if>
</#if>
</td>
</tr>
<tr>
<td colspan="2" style="vertical-align: top; padding-top: 10px;">
<#if enableLync??>
Join by Skype for Business
${lyncUrl}
</#if>
</td>
</tr>
<tr>
<td colspan="2" style="vertical-align: top; padding-top: 10px;">
<#if moreCustomizedContent??>
${moreCustomizedContent}
</#if>
</td>
</tr>
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%">
<tbody>
<tr>
<td>
<img src="https://us02web.zoom.us/account/branding/p/8ea99ae5-b914-4976-a41c-f449d0eb7d61.png">
</td>
</tr>
</tbody>
</table>
</table>
</td>
</tr>
<tr>
<td style="padding-top: 30px; padding-bottom: 30px; background-color: #FFFFFF; border-top-color:#F0F2F4; border-top-style: solid; border-top-width: 1px;">
<table align="center" border="0" cellpadding="0" cellspacing="0">
<tbody>
<tr>
<td>
<a href="https://www.instagram.com/integrated.work/" style="text-decoration: none;">
<img alt="" border="0" height="25" src="https://us02web.zoom.us/account/branding/p/32de5379-2c9a-4425-ad66-f86bb6c2d0f8.png"
style="width: 25px; height: 25px; border-width: 0px; border-style: solid;"
/>
</a>
</td>
<td style="padding-left:10px; padding-right: 10px;">
<a href="https://www.linkedin.com/company/integratedwork"
style="text-decoration: none;">
<img alt="" border="0" height="25" src="https://us02web.zoom.us/account/branding/p/5ca521c7-cb34-489e-9a17-02061732ceda.png"
style="width: 25px; height: 25px; border-width: 0px; border-style: solid;"
/>
</a>
</td>
<td>
<a href="https://integratedwork.com/blog" style="text-decoration: none;">
<img alt="" border="0" src="https://us02web.zoom.us/account/branding/p/3c36a454-3b66-480a-b957-62cc785fc74d.png"
style="width: 25px; height: 25px; border-width: 0px; border-style: solid;"
/>
</a>
</td>
</tr>
</tbody>
</table>
<table align="center" border="0" cellpadding="0" cellspacing="0" width="100%">
<tbody>
<tr>
<td style="font-family:helvetica,arial; font-size:12px; line-height:18px; color:#999999; text-align:center; padding-top: 10px;">
Copyright ©2020 Integrated Work, Inc. All rights reserved.
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>