Sending Emails From Thunkable App | Gmail App

Gamil App In Thinkable-X

Send emails from your gmail right from thunkable-X apps

Hello everyone today i have made a new project/app in thunkable-X which allows you to send emails from gmail address from thunkable-X apps using Google APP SCRIPT. You can also use this method to verify your users by creating an email OTP verification system on your app. If you want to know the complete process with step-by-step explanation click here to read the complete blog. Else here is some screenshot and shot description to understand, How you can do that too…

Design Screenshot

I have created this simple design with

  • 3 input text-boxes to get the Recipient_Email, Subject & Message/Body.
  • 1 button to trigger the action
  • 1 API Component to submit the form
  • 1 Notifier to show the success/error message
  • 3 Labels Just to show some additional information on screen

Now on Google App Script i have created a new project by clicking on the + New project button at the top left corner of the webpage. ( It’ll take you to a new tab ) Here i have created one with title Gmail_APP as you can see in the below image.

:link: Click here to get the Code

Now click on Publish > Deploy as web app a popup window will be open on your screen. Here Now you have to set Project Version : New, Execute the app as: Me (Your_email_address) & Who has access to the app : Anyone, even anonymous. and then click on Deploy it’ll as for permissions simply grant those.

After successful deployment action url be like
Copy this API/Script url.

Blocks Screenshot

Drag-n-Drop other compoennts to the block area as shown in the image above. Also make sure to put the API/Script URL in the Gamil_API components Set URL block, And Parameters such as recipient, subject & body to the queryparameters block of the Gmail_API component as shown in the image.

:love_you_gesture: All done.

you can use this same method to verify your app users email address by using OTP verification method. I.e sendinga n OTP to your userd email and ask them to verify on your app. If you have any query or suggestion feel free to ask me.

Also hit the :heart: button if you found this useful.

~Ct tricks


Hi @cttricks, first thing, Thank you for this awesome tutorial!

I am trying to implement this and receiving the following error

here is my script

it may be helpful to know i get this error when running the script on the script page

and my blocks

any idea why I receive the error that recipient is not defined?

I have tried to simplify this to this code

I have made sure to copy/paste the URL exactly from the google script page.

is there a need to send headers along with the blocks?

I got it working!

for others to note. If you run the script in googleScript, you will get an error. That is because this is not a standalone script, which is a thing you can do in google script as this allows for trigger actions. But, our trigger action requires inputs via the query parameters and therefore will always produce the error.

I’m not sure exactly what the problem was but here is my working codes!

Thanks @cttricks


Hey @jared, you are getting the error because you running the script in Google Script, As the script is written in doPost, you must have to perform the action outside of the script.

1 Like

Thank you! I actually fixed it already! Then i got interested and was able to use a script to write to google sheets. It is slightly different than the format you have posted in the past (both of which will be obsolete very soon given the upcoming component release)

And my google app script

   Copyright 2011 Martin Hawksey
   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at
   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   See the License for the specific language governing permissions and
   limitations under the License.

// Usage
//  1. Enter sheet name where data is to be written below
        var SHEET_NAME = "Sheet1";
//  2. Run > setup
//  3. Publish > Deploy as web app 
//    - enter Project Version name and click 'Save New Version' 
//    - set security level and enable service (most likely execute as 'me' and access 'anyone, even anonymously) 
//  4. Copy the 'Current web app URL' and post this in your form/script action 
//  5. Insert column names on your destination sheet matching the parameter names of the data you are passing in (exactly matching case)

var SCRIPT_PROP = PropertiesService.getScriptProperties(); // new property service

// If you don't want to expose either GET or POST methods you can comment out the appropriate function
function doGet(e){
  return handleResponse(e);

function doPost(e){
  return handleResponse(e);

function handleResponse(e) {
  // shortly after my original solution Google announced the LockService[1]
  // this prevents concurrent access overwritting data
  // [1]
  // we want a public lock, one that locks for all invocations
  var lock = LockService.getPublicLock();
  lock.waitLock(30000);  // wait 30 seconds before conceding defeat.
  try {
    // next set where we write the data - you could write to multiple/alternate destinations
    var doc = SpreadsheetApp.openById(SCRIPT_PROP.getProperty("key"));
    var sheet = doc.getSheetByName(SHEET_NAME);
    // we'll assume header is in row 1 but you can override with header_row in GET/POST data
    var headRow = e.parameter.header_row || 1;
    var headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0];
    var nextRow = sheet.getLastRow()+1; // get next row
    var row = []; 
    // loop through the header columns
    for (i in headers){
      if (headers[i] == "Timestamp"){ // special case if you include a 'Timestamp' column
        row.push(new Date());
      } else { // else use header name to get data
    // more efficient to set values as [][] array than individually
    sheet.getRange(nextRow, 1, 1, row.length).setValues([row]);
    // return json success results
    return ContentService
          .createTextOutput(JSON.stringify({"result":"success", "row": nextRow}))
  } catch(e){
    // if error return this
    return ContentService
          .createTextOutput(JSON.stringify({"result":"error", "error": e}))
  } finally { //release lock
function setup() {
    var doc = SpreadsheetApp.getActiveSpreadsheet();
    SCRIPT_PROP.setProperty("key", doc.getId());

Great, I have made my own script to READ/WRITE data on spreadsheet Using API component but as ThunkableX has introduced there own component for the same use i didn’t introduced my script with the community :grin:

Google App script is really a very awesome tool to use if you know how to use it. I’m also a learner but if you have any query related to this feel free to ask :grinning:

~Ct tricks

1 Like

Thank you! I am just a :baby: in the coding community but am trying to learn this AppleScript. I saw today it can also be used to produce PDF. I am now wondering just how much it can do! Like, maybe it could be used to.

What I would like to learn to do is set up a static HTML page with javascript in it. And then send data to the app script. i think this would be the workaround for one problem with Thunkable currently wich is you cannot pass data to an HTML file. i think this could on such workaround

I will begin learning how to use HTML with appscript to pull in data

why you need a static HTML page to send data to the app script?

I want to be able to use google charts. they dont provide the static image generator anymore so you have to use code liek this

    <script type="text/javascript" src=""></script>
    <script type="text/javascript">
      google.charts.load('current', {'packages':['corechart']});

      function drawChart() {
        var data = google.visualization.arrayToDataTable([
          ['Year', 'Sales', 'Expenses'],
          ['2004',  1000,      400],
          ['2005',  1170,      460],
          ['2006',  660,       1120],
          ['2007',  1030,      540]

        var options = {
          title: 'Company Performance',
          curveType: 'function',
          legend: { position: 'bottom' }

        var chart = new google.visualization.LineChart(document.getElementById('curve_chart'));

        chart.draw(data, options);
    <div id="curve_chart" style="width: 900px; height: 500px"></div>

my issue is that my data is stored in airtable. so i think my user will need to call the data on their mobile, and then send it to the appscript. the appscript can then plug the data into this page. the page returns the chart image? (i think that’s how it would work)

for the time being, i use which is great, but limited for my needs. I want to be able to draw trendlines on a semilogarithmic chart. I also want to be able to customize my charts a little more

hi @cttricks @jared. i tested this great contribution, thanks a lot!. Just one thing, for my test i can see the recipient actually is getting the mail but in my app (test) the screen turns white and i can do nothing more in it, do you know what could be happening?

This is an error of some type. Can you share the code you’re using

hi @jared, please your help:

Start with this. The blocks that are pointed to will not work where you have them. You are using the ‘value’ block out of scope. The only place you can use that is in the ‘then do’ section of the method block it came from. if you know where it comes from, instead of using the value block. go back to the method block it comes from. cretate var app _tempData and then in the ‘then do section’ set app _tempData to Value

actually the “value” block is in the “then do” section but i didn’t show all the blocks:

Try sending an email to yourself using this method without all of the extra validation. If it’s not that, look to other things. It’s hard to diagnose just by looking at the code sometimes. Then add back in the validation one step at a time to find the issue


Thanks a lot @Jared, now it is working without any issue. I really dont know what was the mistake but i changed a few things in the blocks. Thanks again!

1 Like

Hi @jared,

We can do this without using appscript :grinning: I found a solution of this. what we all need to do is when you call data [ It should be in the format for ColumnChart ]

Chart Data

      ['Year', 'Sales', 'Expenses'],
      ['2004',  1000,      400],
      ['2005',  1170,      460],
      ['2006',  660,       1120],
      ['2007',  1030,      540]

In formatted format ['Year', 'Sales', 'Expenses'],['2004', 1000, 400],['2005', 1170, 460],['2006', 660, 1120],['2007', 1030, 540]

Components Required

  • Web_Viewer1
  • Button1

Blocks ScreenShot


Remove all extra spaces[ It’s not much necessary I did it to make it more formattable :smile: ]

<!DOCTYPE html><html><head><script type="text/javascript" src=""></script><script type="text/javascript">google.charts.load('current', {'packages': ['corechart']});google.charts.setOnLoadCallback(drawChart);function drawChart() {var data = google.visualization.arrayToDataTable([REPLACE_THIS_WITH_YOUR_CHART_DATA]);var options = {title: '',curveType: 'function',legend: {position: 'bottom'}};var chart = new google.visualization.LineChart(document.getElementById('curve_chart'));var chart_div = document.getElementById('curve_chart');var chart = new google.visualization.ColumnChart(chart_div);chart.draw(data, options);}</script></head><body><div id="curve_chart" style="width: 100%; height: auto;"></div>></body></html>


Test/Sample Project

:link: Click here to view

Please check this out! And let me know if its works for you too.
Then we 'll make a new tutorial topic related with this :grin:

~Ct tricks

@cttricks This doesn’t work on iOS. :frowning:

the webveiwer on iOS only likes native HTML files. On the bright side, they are going to allow for injecting javascript into a locally hosted html file with the new updates. Now, i need to learn how to send data to an html file with the POST method. :sweat:

We can do one more thing… we can convert this chart into B64 string and then store this into google drive, after file/image [PNG] is created we’ll return this file/image url to the app and then can diplay this the chart on app using image component

I think this will work…

I’m sorry buddy. I don’t have an iOS device and i haven’t tested it before seding the useless method to you :sweat_smile:

1 Like

That sounds promising but that would mean static charts, correct? If we turn the chart into an image and store it to be retrieved later, the user could not update the chart I think.

p.s. I am brand new to html/JavaScript, so thank you for all your help and suggestions!