Growing a SaaS Startup in 2020

Growing a SaaS Startup in 2020

Table of contents

As founders or team members, we constantly have to think of efficient ways to acquire users and penetrate a potentially crowded/noisy market with very scarce resources.
|
Florian Bersier
|
Founder, CEO

I was invited earlier this year to give a talk at MassChallenge Switzerland explaining how Gmelius managed to get its first 100k daily active users with a marketing budget close to $0.

Growing an early-stage SaaS startup is a daily challenge! As founders or team members, we constantly have to think of efficient ways to acquire users and penetrate a potentially crowded/noisy market with very scarce resources. This leads to a situation where we feel lost, confused, and anxious because of the amount of stuff we need to focus on to make it happen.

Gmelius was founded at the end of 2016 and we managed to reach our first 100k daily active users 14 months later. This was an exciting and demanding journey that I try to summarize in the below presentation by focusing on what worked and what did not.

[slideshare id=109978423&doc=growingasaasstartup-gmelius-180815125820]

If I had to pick my best decision and my worst one, those would be, respectively:

More in

Reclaim 1 hour per day managing emails.

var user = {}; var params = new URLSearchParams(window.location.search); var path = window.location.pathname; const userPictures = document.getElementsByClassName('user-picture'); const signupCTAs = document.getElementsByClassName('get-started'); const bookDemoCTAs = document.querySelectorAll('[href*="request-demo"]'); const isSignedIn = document.getElementsByClassName('is-user-state'); const isSignedInInline = document.getElementsByClassName('is-state-user-inline'); const upgradeCTAs = document.getElementsByClassName('button-pricing-plan'); const educationCTAs = document.getElementsByClassName('gmelius-university'); const plan = params.get('plan'); const role = params.get('role'); // Select buttons using provided CSS classes const btnsTrial = document.querySelectorAll('.button-trial'); const btnsUpgrade = document.querySelectorAll('.button-upgrade'); const btnsAccount = document.querySelectorAll('.button-account'); const btnsDowngrade = document.querySelectorAll('.button-downgrade'); const btnsUpgradeUser = document.querySelectorAll('.button-upgrade-user'); // Conditional logic based on URL params if (plan && role) { document.querySelector('.pricing-title').textContent = 'Our Plans & Pricing'; document.querySelector('.pricing-subtext').textContent = 'Contact sales@gmelius.com in case of questions'; history.replaceState(null, '', window.location.pathname); // Hide Trial Buttons btnsTrial.forEach(btn => { if (btn) btn.style.display = 'none'; }); // Display appropriate buttons based on params if (role === 'user') { switch (plan) { case 'enterprise': btnsDowngrade.forEach(btn => { btn.style.display = 'flex'; }); document.querySelector('.button-enterprise').style.display = 'none'; btnsAccount[2].style.display = 'flex' btnsAccount[2].href = 'https://app.gmelius.com/account' break; case 'pro': btnsDowngrade[0].style.display = 'flex' btnsAccount[1].style.display = 'flex' btnsAccount[1].href = 'https://app.gmelius.com/account' break; case 'growth': btnsUpgradeUser[1].style.display = 'flex' btnsAccount[0].style.display = 'flex' btnsAccount[0].href = 'https://app.gmelius.com/account' break; default: btnsUpgradeUser.forEach(btn => { btn.style.display = 'flex'; }); } } else if (role !== 'user') { switch (plan) { case 'enterprise': btnsDowngrade.forEach(btn => { btn.style.display = 'flex'; }); document.querySelector('.button-enterprise').style.display = 'none'; btnsAccount[2].style.display = 'flex' break; case 'pro': btnsDowngrade[0].style.display = 'flex' btnsAccount[1].style.display = 'flex' break; case 'growth': btnsUpgrade[1].style.display = 'flex' btnsAccount[0].style.display = 'flex' break; default: btnsUpgrade.forEach(btn => { btn.style.display = 'flex'; }); } } } try { window.onload = function() { document.getElementById('dashboard-frame-app').src = 'https://app.gmelius.com/websiteProxy.html'; }; window.addEventListener('message', function (e) { if (e.origin === 'https://app.gmelius.com') { user = e.data; window.localStorage.setItem('user', JSON.stringify(user)); } }); if (!Object.keys(user).length && window.localStorage.getItem('user')) { user = JSON.parse(window.localStorage.getItem('user')); } if (!Object.keys(user).length && (params.has('ref') && (params.get('ref') === 'dashboard' || params.get('ref') === 'extension'))) { user.picture = 'https://cloud.gmelius.com/public/logos/gmelius-32.png'; window.localStorage.setItem('user', JSON.stringify(user)); } if (Object.keys(user).length) { if (userPictures.length > 0) { Array.from(userPictures).forEach(function(picture) { if (user.email) { picture.src = user.picture || `https://avatar.oxro.io/avatar.svg?name=${user.email.toUpperCase()}&length=1`; } else { picture.src = 'https://cloud.gmelius.com/public/images/avatar-astro.png' } picture.onerror = function () { this.style.display = 'none'; } }); } if (signupCTAs.length > 0) { Array.from(signupCTAs).forEach(function(cta) { //cta.style.display = 'none'; }); } if (isSignedIn.length > 0) { Array.from(isSignedIn).forEach(function(cta) { //cta.style.display = 'none'; }); } if (isSignedInInline.length > 0) { Array.from(isSignedInInline).forEach(function(cta) { //cta.style.display = 'inline'; }); } if (educationCTAs.length > 0) { document.body.classList.add('hasAnnouncement'); Array.from(educationCTAs).forEach(function(cta) { cta.style.display = 'block'; }); } if (upgradeCTAs.length > 0) { Array.from(upgradeCTAs).forEach(function(cta) { cta.classList.add('upgrade'); }); } } } catch (e) {} // Beamer var beamer_config = { product_id : 'CIJpSEcT13796', //DO NOT CHANGE: This is your product code on Beamer selector : '.beamer-widget', /*Optional: Id, class (or list of both) of the HTML element to use as a button*/ //display : 'right', /*Optional: Choose how to display the Beamer panel in your site*/ top: 10, /*Optional: Top position offset for the notification bubble*/ //right: -10, /*Optional: Right position offset for the notification bubble*/ //bottom: 0, /*Optional: Bottom position offset for the notification bubble*/ left: 30, /*Optional: Left position offset for the notification bubble*/ //button_position: 'bottom-right', /*Optional: Position for the notification button that shows up when the selector parameter is not set*/ //icon: 'bell_lines', /*Optional: Alternative icon to display in the notification button*/ //language: 'EN', /*Optional: Bring news in the language of choice*/ //filter: 'admin', /*Optional : Bring the news for a certain role as well as all the public news*/ //lazy: false, /*Optional : true if you want to manually start the script by calling Beamer.init()*/ //alert : true, /*Optional : false if you don't want to initialize the selector*/ //delay : 0, /*Optional : Delay (in milliseconds) before initializing Beamer*/ //embed : false, /*Optional : true if you want to embed and display the feed inside the element selected by the 'selector' parameter*/ //mobile : true, /*Optional : false if you don't want to initialize Beamer on mobile devices*/ //notification_prompt : 'sidebar', /*Optional : override the method selected to prompt users for permission to receive web push notifications*/ //callback : your_callback_function, /*Optional : Beamer will call this function, with the number of new features as a parameter, after the initialization*/ //onclick : your_onclick_function(url, openInNewWindow), /*Optional : Beamer will call this function when a user clicks on a link in one of your posts*/ //onopen : your_onopen_function, /*Optional : Beamer will call this function when opening the panel*/ //onclose : your_onclose_function, /*Optional : Beamer will call this function when closing the panel*/ //---------------Visitor Information--------------- //user_firstname : "firstname", /*Optional : Input your user firstname for better statistics*/ //user_lastname : "lastname", /*Optional : Input your user lastname for better statistics*/ user_email : user.email ? user.email : null, /*Optional : Input your user email for better statistics*/ //user_id : "user_id" /*Optional : Input your user ID for better statistics*/ }; document.addEventListener('DOMContentLoaded', function () { if (user && user.email) { $crisp.push(["set", "user:email", [user.email]]); } const talkToSalesBtn = document.getElementById('talk-to-sales'); const tryProBtn = document.getElementById('try-pro'); const downgradeToGrowthBtn = document.getElementById('downgrade-growth'); const downgradeToProBtn = document.getElementById('downgrade-pro'); // Wait until Crisp is fully loaded function waitForCrisp(callback, attempts = 0) { if (window.$crisp && window.$crisp.is && window.CRISP_WEBSITE_ID) { callback(); } else if (attempts < 10) { // retry up to 10 times (5 sec) setTimeout(() => waitForCrisp(callback, attempts + 1), 500); } else { console.error('Crisp SDK failed to load.'); } } function sendVisitorMessage(messageText) { $crisp.push(["set", "message:text", [messageText]]); } // Open Crisp chat and display messages function openChatAndShowMessages(text) { if (!$crisp.is("chat:visible")) { $crisp.push(["do", "chat:show"]); } if (!$crisp.is("chat:opened")) { $crisp.push(["do", "chat:open"]); } // Small delay ensures Crisp DOM readiness setTimeout(() => { sendVisitorMessage(text); }, 500); } // Button click handler talkToSalesBtn.addEventListener('click', function (event) { event.preventDefault(); waitForCrisp(() => openChatAndShowMessages("Hi, I'd like to talk to your Enterprise Sales Team. My company name is:")); }); tryProBtn.addEventListener('click', function (event) { event.preventDefault(); waitForCrisp(() => openChatAndShowMessages("Hi, I'd like to start a trial of Gmelius Pro. My email address is:")); }); downgradeToProBtn.addEventListener('click', function (event) { event.preventDefault(); waitForCrisp(() => openChatAndShowMessages("Hi, I'd like to downgrade my subscription to Gmelius Pro.")); }); downgradeToGrowthBtn.addEventListener('click', function (event) { event.preventDefault(); waitForCrisp(() => openChatAndShowMessages("Hi, I'd like to downgrade my subscription to Gmelius Growth.")); }); btnsUpgradeUser.forEach(button => { button.addEventListener('click', event => { event.preventDefault(); waitForCrisp(() => openChatAndShowMessages("Hi, I'd like to upgrade my team's subscription.")); }); }); }); -->