Google chat app is not responding for image attachment - google-apps-script

I am trying to save attachment uploaded by google chat app.
I am handling two cases if user messages anything to google chat app, it will pop up a card with widgets which is perfectly fine but I am handling if user uploads attachment it will save into google drive but the code is not working. Can someone please help?
I am new in coding.
function getByteStream(dataRef) {
var blob = "";
var driveFileName = "";
var url = "https://chat.googleapis.com/v1/media/"+ dataRef +"?alt=media"
var service = getOAuth2Service();
var response = UrlFetchApp.fetch(url, {
headers: {
'Authorization': 'Bearer ' + service.getAccessToken(),
},
'muteHttpExceptions': true,
});
if(response.getResponseCode() != 200){
return "Failed to get file content with error code: " + response.getResponseCode();
}
blob = response.getBlob();
driveFileName = DriveApp.createFile(blob);
return "The uploaded contents have been saved in a Google Drive file: "+ driveFileName;
}
function onMessage(event) {
if (event.message.attachment) {
var attachmentName = event.message.attachment[0].name;
var attachment = getByteStream(attachmentName);
console.log(attachment);
} else {
return {
"cards_v2": [{
"card_id": "addContact",
"card": {
"header": {
"title": "Hello" +" " + event.user.displayName + "!",
"subtitle": "Log your feedback!",
"imageUrl": "https://www.gstatic.com/images/branding/product/2x/contacts_48dp.png",
"imageType": "CIRCLE"
},
"sections": [
{
"widgets": [
{
"buttonList": {
"buttons": [
{
"text": "Start here",
"onClick": {
"action": {
"function": "openDialog",
"interaction": "OPEN_DIALOG"
}
}
}
]
},
"horizontalAlignment": "CENTER"
}
]
}
]
}
}]
};
}
}

Related

Using URFetchApp with Google Forms API returns 400 ccode error

I'm trying to use the new Google Forms API to add an image to an existing Google form. At the moment I'm just trying to change the image Alt Text. I haven't been able to get it to work. I keep getting a 400 error. I'm hooping someone can point out what I'm doing wrong.
TIA for any help
This is the code:
function callFormsAPI() {
Logger.log('Calling the Forms API!');
var formId = "1oxBZrRG6UEcrLSXrT9P9hcqgkvE12UQDYUOAYyg3eeA"
// Get OAuth Token
var OAuthToken = ScriptApp.getOAuthToken();
Logger.log('OAuth token is: ' + OAuthToken);
var path = "https://forms.googleapis.com/v1beta/forms/"
var formsAPIUrl = path+formId+':batchUpdate'
Logger.log('formsAPIUrl is: ' + formsAPIUrl);
var body = tempAPI()
var options = {
'headers': {
Authorization: 'Bearer ' + OAuthToken,
Accept: 'application/json'
},
muteHttpExceptions: true,
'method': 'post',
'payload' : JSON.stringify(body)
// 'payload' : body
};
var response = UrlFetchApp.fetch(formsAPIUrl, options);
Logger.log('Response from forms.responses was: ' + response);
var text = response.getContentText()
console.log(text)
}
function tempAPI(){
var itemId = "2005660890"
var imageString = 'https://images.unsplash.com/photo-1552519507-da3b142c6e3d';
var request = {
"requests": [
{
"updateItem": {
"item": {
"itemId": itemId,
"questionItem": {
"image": {
"altText": 'this is a car',
// "sourceUri": imageString
}
}
},
"updateMask": "altText"
// "updateMask": "item/questionItem/image/sourceUri"
} //end of update item
}
]
}
return request
}
This is the response I'm getting
{
"error": {
"code": 400,
"message": "Invalid JSON payload received. Unknown name \"{\"requests\":[{\"updateItem\":{\"item\":{\"itemId\":\"2005660890\",\"questionItem\":{\"image\":{\"altText\":\"this is a car\"}}},\"updateMask\":\"altText\"}}]}\": Cannot bind query parameter. Field '{\"requests\":[{\"updateItem\":{\"item\":{\"itemId\":\"2005660890\",\"questionItem\":{\"image\":{\"altText\":\"this is a car\"}}},\"updateMask\":\"altText\"}}]}' could not be found in request message.",
"status": "INVALID_ARGUMENT",
"details": [
{
"#type": "type.googleapis.com/google.rpc.BadRequest",
"fieldViolations": [
{
"description": "Invalid JSON payload received. Unknown name \"{\"requests\":[{\"updateItem\":{\"item\":{\"itemId\":\"2005660890\",\"questionItem\":{\"image\":{\"altText\":\"this is a car\"}}},\"updateMask\":\"altText\"}}]}\": Cannot bind query parameter. Field '{\"requests\":[{\"updateItem\":{\"item\":{\"itemId\":\"2005660890\",\"questionItem\":{\"image\":{\"altText\":\"this is a car\"}}},\"updateMask\":\"altText\"}}]}' could not be found in request message."
}
]
}
]
}
}

How to create post using Google My Business API - Google Apps Script

I can't figure out why I'm getting the below error when I attempt to create a post with the GMB API in Google Apps Script. I'm following this documentation https://developers.google.com/my-business/content/posts-data
{
"error": {
"code": 400,
"message": "Request contains an invalid argument.",
"status": "INVALID_ARGUMENT",
"details": [
{
"#type": "type.googleapis.com/google.mybusiness.v4.ValidationError",
"errorDetails": [
{
"code": 2,
"field": "summary",
"message": "Standard local post must have at least a media or summary."
}
]
}
]
}
}
Here is my script
function callToActionPost() {
var url = 'https://mybusiness.googleapis.com/v4/accounts/123/locations/456/localPosts';
var options = {
headers: { Authorization: "Bearer " + getGMBService_().getAccessToken() },
method: 'POST',
muteHttpExceptions: true,
languageCode: "en",
topicType: "STANDARD",
summary: "New Release!",
callToAction: {
actionType: "ORDER",
url: "https://www.artivem.com/"
},
media: {
sourceUrl: "https://untappd.akamaized.net/photos/2021_04_16/ccff4c358e362ce3c4835fcc94549a8f_640x640.jpg",
mediaFormat: "PHOTO"
}
};
var response = UrlFetchApp.fetch(url, options);
Logger.log(response);
}
I tried the following adaption, but it did not work
function callToActionPost() {
var url = 'https://mybusiness.googleapis.com/v4/accounts/123/locations/456/localPosts';
var options = {
headers: { Authorization: "Bearer " + getGMBService_().getAccessToken() },
method: 'POST',
muteHttpExceptions: true,
payload: {
languageCode: "en",
topicType: "STANDARD",
summary: "New Release!",
callToAction: {
actionType: "ORDER",
url: "https://www.artivem.com/"
},
media: {
sourceUrl: "https://untappd.akamaized.net/photos/2021_04_16/ccff4c358e362ce3c4835fcc94549a8f_640x640.jpg",
mediaFormat: "PHOTO"
}
}
};
var response = UrlFetchApp.fetch(url, options);
Logger.log(response);
}
Thanks in advance!
The request object needs to go into the payload. "payload" is a parameter of the options.
function callToActionPost() {
var url = 'https://mybusiness.googleapis.com/v4/accounts/123/locations/456/localPosts';
var payload = {
"languageCode": "en-US",
"summary": "New Release!",
"callToAction": {
"actionType": "ORDER",
"url": "https://www.artivem.com/"
},
"media": [{
"sourceUrl": "https://untappd.akamaized.net/photos/2021_04_16/9999999999999999.jpg",
"mediaFormat": "PHOTO"
}]
}
var options = {
headers: { Authorization: "Bearer " + getGMBService_().getAccessToken() },
method: 'POST',
muteHttpExceptions: true,
'contentType': 'application/json',
'payload' : JSON.stringify(payload)
};
var response = UrlFetchApp.fetch(url, options);
Logger.log(response);
if (response.getResponseCode() !== 200) {
console.log("Error Code: " + response.getResponseCode());
}
}

Autodesk forge file translation to svf not working

I am currently following the prepare the file for viewer tutorial on autodesk forge. This is the link:
https://forge.autodesk.com/en/docs/model-derivative/v2/tutorials/prep-file4viewer/
Rather than making the requests using cURL, I have made javascript code using jquery ajax to make the requests. I have made each request a function, with a button to trigger each request, as the code shows below. Currently, each request works fine except for the translate to svf request. When I attempt to make that request and check the status of my request the following message is printed:
{
"urn": "dXJuOmFkc2sub2JqZWN0czpvcy5vYmplY3Q6cHJvamVjdGZpbGVzMTIzNC9fTUMtMTQwMDYlMjBGT0xERVIlMjBBU1NZJTIwLSUyMEhTQ0YtMTIuaWFtLmR3Zg",
"derivatives": [{
"hasThumbnail": "false",
"name": "_MC-14006 FOLDER ASSY - HSCF-12.iam.dwf",
"progress": "complete",
"messages": [{
"type": "error",
"code": "DWF2D-Not_A_DWF_Error",
"message": "Unable to open, not a DWF/DWFx file."
}, {
"type": "error",
"message": "Extractor error code -11",
"code": "TranslationWorker-InternalFailure"
}],
"outputType": "svf",
"status": "failed"
}],
"hasThumbnail": "false",
"progress": "complete",
"type": "manifest",
"region": "US",
"version": "1.0",
"status": "failed"
Using postman, I can make the same request and everything will work fine. Then when I attempt to translate the file again, it will work as long as it's from the same bucket. I need files to be able to be uploaded and translated right away from the code, without having to translate it first in postman. I even attempted to copy the postman code for the translation request, but am getting the same error when the file is not already in my bucket. Below is my code for the simple webpage.
<html>
<title>Viewer</title>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jstree/3.3.7/jstree.min.js"></script>
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1, user-scalable=no" />
<meta charset="utf-8">
<link rel="stylesheet" href="https://developer.api.autodesk.com/modelderivative/v2/viewers/7.*/style.min.css" type="text/css">
<script src="https://developer.api.autodesk.com/modelderivative/v2/viewers/7.*/viewer3D.min.js"></script>
<style>
body {
margin: 0;
}
#forgeViewer {
width: 100%;
height: 100%;
margin: 40px;
background-color: #F0F8FF;
}
</style>
</head>
<script>
//variable declaration
var access_token='';
const bucketKey = 'projectfiles123874';
const policyKey = 'transient';
var urn1 = '';
var urn2 = '';
var urn3 = '';
//var fileName = '_MC-14006 FOLDER ASSY - HSCF-12.iam.dwf';
var fileName = '';
if(access_token == ''){
getToken();
}
function getToken(){
$.ajax({
method: 'POST',
url: 'https://developer.api.autodesk.com/authentication/v1/authenticate',
headers: {
'content-type': 'application/x-www-form-urlencoded',
},
data: 'client_id=lAOwCkVLo307t4fxWdEriDrPs1CAhdMl&client_secret=AhWJ5cIrm6Lh65AB&grant_type=client_credentials&scope=data:read data:write bucket:read viewables:read bucket:create data:create',
success:function(response){
access_token=response.access_token;
console.log(response);
console.log(access_token);
},
error:function(error){
console.log(error);
}
})
}
function createBucket(){
$.ajax({
method: 'POST',
url: "https://developer.api.autodesk.com/oss/v2/buckets",
headers:{
"content-type": "application/json",
// "x-ads-force":true,
Authorization: "Bearer " + access_token
},
data: JSON.stringify({
'bucketKey': bucketKey,
'policyKey': policyKey
}),
success:function(response){
console.log(response);
//window.location.href = "http://team/www/upload.html";
},
error:function(error){
if (error.response && error.response.status == 409){
console.log("bucket alrready exists, skipping creation.");
}
else{
console.log("failed to create new bucket.");
}
}
})
}
function uploadFile(){
$.ajax({
method: 'PUT',
url:"https://developer.api.autodesk.com/oss/v2/buckets/"+encodeURIComponent(bucketKey)+"/objects/"+encodeURIComponent(fileName)+"",
data: 'C:\Users\matthew_vidovic\Desktop\DWGs\_MC-7251 CLOROX CARTON FORMER.iam.dwf',
headers:{
Authorization: "Bearer " + access_token
},
success:function(response){
console.log(response);
urn1 = response.objectId;
console.log(urn1);
urn2 = btoa(urn1);
console.log(urn2);
},
error:function(error){
console.log(error);
}
})
}
function translateToSVF(){
$.ajax({
method: 'POST',
url:"https://developer.api.autodesk.com/modelderivative/v2/designdata/job",
headers:{
"content-type": "application/json",
Authorization: "Bearer " + access_token
},
data:JSON.stringify({
"input":{ "urn":urn2
},
"output":{ "destination":{
"region": "us"
},
"formats": [
{
"type": "svf",
"views": [
"2d",
"3d"
]
}
]
}
}),
success:function(response){
console.log(response);
urn3=response.urn;
console.log(urn3);
// console.log(access_token);
},
error:function(error){
console.log(error);
}
})
/* POSTMAN REQUEST CODE
var settings = {
"url": "https://developer.api.autodesk.com/modelderivative/v2/designdata/job",
"method": "POST",
"timeout": 0,
"headers": {
"Content-Type": "application/json",
"Cookie": "PF=5aKDLaxVErxdqJY5p40NQy",
Authorization: "Bearer " + access_token
},
"data": JSON.stringify({"input":{"urn":urn2,"rootFilename":fileName,"compressedUrn":false},"output":{"destination":{"region":"us"},"formats":[{"type":"svf","views":["2d","3d"]}]}}),
};
$.ajax(settings).done(function (response) {
console.log(response);
urn3 = response.urn;
console.log(urn3);
});
*/
}
function checkStatus(){
$.ajax({
method: 'GET',
url: "https://developer.api.autodesk.com/modelderivative/v2/designdata/" + encodeURIComponent(urn3) + "/manifest",
headers:{
Authorization: "Bearer " + access_token
},
success: function(response){
console.log(response);
},
error: function(error){
console.log(error);
}
})
}
/*
if(access_token == ''){
getToken();
}
else if(urn1 == ''){
uploadFile();
}
else translateToSVF();
*/
function displayViewer(){
var viewer;
var options = {
env: 'AutodeskProduction',
api: 'derivativeV2', // for models uploaded to EMEA change this option to 'derivativeV2_EU'
getAccessToken: function(onTokenReady) {
var token = access_token;
var timeInSeconds = 3600; // Use value provided by Forge Authentication (OAuth) API
onTokenReady(token, timeInSeconds);
}
};
Autodesk.Viewing.Initializer(options, function() {
var htmlDiv = document.getElementById('forgeViewer');
viewer = new Autodesk.Viewing.GuiViewer3D(htmlDiv);
var startedCode = viewer.start();
if (startedCode > 0) {
console.error('Failed to create a Viewer: WebGL not supported.');
return;
}
console.log('Initialization complete, loading a model next...');
});
var documentId = 'urn:'+urn3;
Autodesk.Viewing.Document.load(documentId, onDocumentLoadSuccess, onDocumentLoadFailure);
function onDocumentLoadSuccess(viewerDocument) {
var defaultModel = viewerDocument.getRoot().getDefaultGeometry();
viewer.loadDocumentNode(viewerDocument, defaultModel);
}
function onDocumentLoadFailure() {
console.error('Failed fetching Forge manifest');
}
}
/*
function check(){
if (access_token='') getToken
}
*/
function processSelectedFiles(fileInput) {
var files = fileInput.files;
for (var i = 0; i < files.length; i++) {
// alert("Filename " + files[i].name);
fileName = files[i].name;
console.log(fileName);
}
}
</script>
<body><h1>Model Viewer</h1>
<p><button onclick="getToken()">Get Token</button></p>
<p><button onclick="createBucket()">Create Bucket</button></p>
<p><button onclick="uploadFile()">upload file</button></p>
<p><button onclick="translateToSVF()">Translate</button></p>
<p><button onclick="checkStatus()">Check Status</button></p>
<p><button onclick="displayViewer()">display Viewer</button></p>
<p><input type="file" id="input" onchange="processSelectedFiles(this)"></p>
<div id="forgeViewer"></div>
</body>
</html>
Any help as to why this is happening would be greatly appreciated.
Cheers!
Your PUT is not specifying a file name, just a folder, so probably the upload is corrupted. Also, you don't need to provide a content-type.
The file must be selected by the user, your browser app does not have access to the local folder structure. You can create an input file or form, then pass it to the data field
method: 'PUT',
url:"https://developer.api.autodesk.com/oss/v2/buckets/"+encodeURIComponent(bucketKey)+"/objects/"+encodeURIComponent(fileName)+"",
data: theForm.theInput.files[0],
headers:{
Authorization: "Bearer " + access_token
},
You can verify your download via SHA-1 signature or, easier, comparing the file size local and live (inside the bucket).

Merge the two different JSON using node JS

I have two requests that return their data as JSON using NodeJS Seriate.
The first response is:
{
"status": true,
"message": "Data Found",
"data": [
{
"statusCode": 200,
"body": {
"expand": "renderedFields,names,schem,operations,editmeta,changelog,versionedRepresentations",
"id": "64672",
"self": "https://computenext.atlassian.net/rest/api/2/issue/64672",
"key": "CKS-2016",
"fields": {
"parent": {
"id": "64670",
"key": "CKS-2014"
}
}
}
}
]
}
The second response is:
{
"statusCode": 200,
"body": {
"errors": [],
"detail": [
{
"repositories": [
{
"name": "registry",
"avatar": "http://stash.computenext.com/projects/SERVICES/avatar.png?s=32",
"avatarDescription": "services"
}
]
}
]
}
}
I want to merge the two responses, that would have the following structure:
{
"status": true,
"message": "Data Found",
"data": [
{
"statusCode": 200,
"body": {
"expand": "renderedFields,names,schema,operations,editmeta,changelog,versionedRepresentations",
"id": "64672",
"self": "https://computenext.atlassian.net/rest/api/2/issue/64672",
"key": "CKS-2016",
"fields": {
"parent": {
"id": "64670",
"key": "CKS-2014",
"detail": [
{
"repositories": [
{
"name": "registry",
"avatar": "http://stash.computenext.com/projects/SERVICES/avatar.png?s=32",
"avatarDescription": "services"
}
]
}
]
}
}
}
}
]
}
I searched and found several methods of joining or extending two JSON objects but nothing similar to that.
How can I achieve this?
My Actual Code is,
exports.getIssues = function(req, res) {
console.log(filename + '>>get Issues>>');
var response = {
status : Boolean,
message : String,
data : String
};
var request = require('request');
var username = const.username ;
var password = const.password ;
var options = {
url : 'https://computenext.atlassian.net/rest/api/2/search?jql=status+%3D+Resolved+ORDER+BY+updated',
auth : {
username : username,
password : password
}
};
request( options, function(error, obj) {
if (error) {
response.message = appmsg.DATA_NT_FOUND;
response.status = false;
response.data = obj;
res.send(response);
} else {
response.message = appmsg.DATA_FOUND;
response.status = true;
response.data = JSON.parse(obj.body);
//res.send(response);
var issueKey = response.data.issues;
// var keyData = issueKey[0].key;
// console.log(response.data.issues);
// console.log(keyData);
var output = [];
for(var i = 0; i < issueKey.length; i++) {
var issue = issueKey[i].key;
//var key = [];
//key.push(issue);
console.log(issue);
var respon = {
status : Boolean,
message : String,
data : String
};
var request = require('request'),
username = const.username ,
password = const.username ,
url = "https://computenext.atlassian.net/rest/api/2/issue/" + issue,
auth = "Basic " + new Buffer(username + ":" + password).toString("base64");
//console.log(url);
request({url : url,headers : {"Authorization" : auth}}, function(err, object){
if (object) {
var info = object;
output.push(info); // this is not working as ouput is undefined at this point
//var pout = JSON.parse(output);
//console.log(info);
console.log("==============================================================================");
//console.log(output);
console.log("******************************************************************************");
if(issueKey.length === output.length){
respon.message = appmsg.DATA_FOUND;
respon.status = true;
respon.data = output;
//console.log(output);
//res.send(respon);
var id = issueKey[0].id;
console.log(id);
var commitout = [];
for(var i = 0; i < issueKey.length; i++) {
var commits = issueKey[i].id;
console.log(commits);
var request = require('request'),
username = const.username ,
password = const.password ,
url = "https://computenext.atlassian.net/rest/dev-status/1.0/issue/detail?issueId=" + commits + "&applicationType=stash&dataType=repository",
auth = "Basic " + new Buffer(username + ":" + password).toString("base64");
//console.log(url);
var test = [];
request({url : url,headers : {"Authorization" : auth}}, function(err, obj1){
if (obj1) {
var info1 = obj1.body;
commitout.push(info1);
if(issueKey.length === commitout.length){
respon.message = appmsg.DATA_FOUND;
respon.status = true;
respon.data = commitout;
// console.log(commitout);
//var test = merge(output, commitout);
var text = output.body;
var resultdone = output;
resultdone.data = resultdone + commitout.body;
console.log(resultdone.data);
res.send(resultdone.data);
}
}
});
}
}
}
});
}
}
});
};
How can i merge the two arrays? in one response. That is my question..
Never mainpulate the string of JSON directly. Parse it first.
const firstRes = JSON.parse(firstResJson);
const secondRes = JSON.prase(secondResJson);
Now, it's a bit unclear what you want to do, and why you want to do it, but try this:
firstRes.data.body.fields.parent.detail = secondRes.body.detail;
Then, you'll find your combined data in firstRes. To get it back to JSON:
JSON.stringify(firstRes);
var obj1 = {
"status": true,
"message": "Data Found",
"data": [
{
"statusCode": 200,
"body": {
"expand": "renderedFields,names,schem,operations,editmeta,changelog,versionedRepresentations",
"id": "64672",
"self": "https://computenext.atlassian.net/rest/api/2/issue/64672",
"key": "CKS-2016",
"fields": {
"parent": {
"id": "64670",
"key": "CKS-2014"
}
}
}
}
]};
var obj2 = {
"statusCode": 200,
"body": {
"errors": [],
"detail": [
{
"repositories": [
{
"name": "registry",
"avatar": "http://stash.computenext.com/projects/SERVICES/avatar.png?s=32",
"avatarDescription": "services"
}
]
}
]}
}
obj1.data.forEach(function(item, index){
item.body.fields.parent.detail = r.body.detail[index];
});
console.log(obj1);
var result1 =
{
"status": true,
"message": "Data Found",
"data": [
{
"statusCode": 200,
"body": {
"expand": "renderedFields,names,schem,operations,editmeta,changelog,versionedRepresentations",
"id": "64672",
"self": "https://computenext.atlassian.net/rest/api/2/issue/64672",
"key": "CKS-2016",
"fields": {
"parent": {
"id": "64670",
"key": "CKS-2014"
}
}
}
}
]
}
var text = result1.data[0].body;
var result2 =
{
"statusCode": 200,
"body": {
"errors": [],
"detail": [
{
"repositories": [
{
"name": "registry",
"avatar": "http://stash.computenext.com/projects/SERVICES/avatar.png?s=32",
"avatarDescription": "services"
}
]
}
]
}
};
var resultdone = result1;
resultdone.data[0].body.fields.parent= result2.body.detail;

Send emails using Sendgrid with google appscript

I am creating a googlesheet addon to send mails.And for sending mails I am using sendgrid.
I cannot find any documentation or example code for sending mails with Google Appscript. This is the code I am using, but it is no good.
var data = {
"api_user":"username",
"api_key":"ioioi",
"to":[],
"tonnage":[],
"cc":[],
"ccname":[],
"bcc":[],
"subject":sub,
"from":from,
"html":htmlBody
}
var headers = { "Accept":"application/json",
"Content-Type":"application/json"
};
data = JSON.stringify(data);
var options = {
"method": "POST",
"payload": data,
"headers": headers,
"muteHttpExceptions": true
};
var res = UrlFetchApp.fetch("https://api.sendgrid.com/api/mail.send.json", options);
Does anyone have any idea or code to send emails with sendgrid using googl appscript?
Try the below code. It worked for me
var SENDGRID_KEY ='Your API KEY';
var headers = {
"Authorization" : "Bearer "+SENDGRID_KEY,
"Content-Type": "application/json"
}
var body =
{
"personalizations": [
{
"to": [
{
"email": "email id of the sender"
}
],
"subject": "Hello, World!"
}
],
"from": {
"email": "From email id"
},
"content": [
{
"type": "text",
"value": "Hello, World!"
}
]
}
var options = {
'method':'post',
'headers':headers,
'payload':JSON.stringify(body)
}
var response = UrlFetchApp.fetch("https://api.sendgrid.com/v3/mail/send",options);
Logger.log(response);
Also ensure that the API key you created in SendGrid has the all the credentials it needs to send the email
For anyone who has this issue in the future with Transactional Email Template:
https://sendgrid.com/docs/ui/sending-email/how-to-send-an-email-with-dynamic-transactional-templates/
This is the function to send (similar to the answer of Nikil Mathew, but for transactional email template with dynamic data):
export const sendBySendGrid = (toEmail, templateId, dynamicTemplateData) => {
const headers = {
Authorization: `Bearer ${process.env.SENDGRID_API_KEY}`,
'Content-Type': 'application/json',
}
const body = {
from: {
email: process.env.SENDGRID_FROM_EMAIL,
name: process.env.SENDGRID_FROM_NAME,
},
personalizations: [
{
to: [
{
email: toEmail,
},
],
dynamic_template_data: dynamicTemplateData,
},
],
template_id: templateId,
}
const options = {
method: 'POST',
headers,
payload: JSON.stringify(body),
}
const response = UrlFetchApp.fetch('https://api.sendgrid.com/v3/mail/send', options)
Logger.log(response)
}
You can update process.env.SENDGRID_API_KEY, process.env.SENDGRID_FROM_EMAIL, process.env.SENDGRID_FROM_NAME with your SendGrid credentials
Here's what's working for me right now in Google Apps Script, including using a dynamic template and insertion of dynamic data for the "handlebars" in my SendGrid template:
var SENDGRID_KEY ='API_KEY';
var headers = {
"Authorization" : "Bearer "+SENDGRID_KEY,
"Content-Type": "application/json"
}
function sendEmail_1() {
var body = {
"personalizations": [
{
"to": [
{
"email": "test#test.com",
"name": "Test Name"
}
],
"bcc": [
{
"email": "test#test.com"
}
],
"dynamic_template_data":
{
"firstName": "Marco Polo"
}
}
],
"from":
{
"email": "test#test.com",
"name": "Test Name"
},
"reply_to": {
"email": "test#test.com"
},
"template_id":"TEMPLATE_ID"
}
var options = {
'method':'post',
'headers':headers,
'payload':JSON.stringify(body)
}
var response = UrlFetchApp.fetch("https://api.sendgrid.com/v3/mail/send",options);
}