I am creating an app with Google apps script to send Docusign envelopes to customers when I receive a google form from them (with the responses going into a google sheet with an onFormSubmit trigger). I'm using the Docusign REST API envelopes method to create an envelope, and send directly to the customer.
Once a customer submits their form, my google apps script function will automatically create a pdf document and put it in a google drive folder. I'm using the Docusign Envelopes method with "remoteUrl" parameter (reference in the document section here: https://docs.docusign.com/esign/restapi/Envelopes/Envelopes/create/) to specifiy the google drive document ID (the pdf) from which Docusign will create the envelope from.
Obviously, these pdf files are in my google drive, and mine only, so there's no reason for me to login to my google account every day, and that's the issue I'm having. When I try to run the script without signing into google, the Docusign API throws this error message:
"errorCode": "EXTERNAL_DOC_SERVICE_ERROR", "message": "An error occurred in the external doc service. NotAuthenticated"
Is there a way I can possibly use refresh tokens with one of Google's APIs to stay logged in? In layman's terms, a method of "auto-authentication". It would have to somehow communicate with my script using the Docusign API.
Here is my current Google Apps Script:
function Docusign() {
var ss = SpreadsheetApp.openById('MYSPREADSHEETID');
var sheet = ss.getSheetByName("SHEET");
var quotesheet = ss.getSheetByName("SHEET2");
var Avals = sheet.getRange("A1:A").getValues();
var Alast = Avals.filter(String).length;
var basicinfo = sheet.getRange(Alast, 1, 1, 35).getValues()[0];
var quotelinks = quotesheet.getRange(Alast, 1, 1, 10).getValues()[0];
var name = (basicinfo[2]);
var email = (basicinfo[1]);
//This is where the full links of the pdfs are stored
var doc = (quotelinks[0]);
//"DIGITS" is six digits followed by a colon, it might be private so I put "DIGITS" instead
var sub = "DIGITS" + doc.substring(32,65);
Logger.log(sub);
var url = "https://demo.docusign.net/restApi/v2/accounts/MYACCOUNTID/envelopes";
var payload =
{
"documents": [
{
"documentId": "1",
"fileExtension": "pdf",
"name": "NAMEOFDOC",
"remoteUrl": sub
}
],
"emailSubject": "SUBJECT",
"recipients": {
"signers": [
{
"email": email,
"name": name,
"recipientId": "1",
"tabs": {
"signHereTabs": [
{
"documentId": "1",
"pageNumber": "1",
"tabLabel": "Signature",
"xPosition": "140",
"yPosition": "650"
}
],
"dateTabs": [
{
"documentId": "1",
"pageNumber": "1",
"tabLabel": "date",
"xPosition": "420",
"yPosition": "680"
}
]
}
}
]
},
"status": "sent"
};
var options =
{
"contentType": "application/json",
"method" : "post",
"headers":
{
"X-DocuSign-Authentication": "{\"Username\":\"MYUSERNAME\",\"Password\":\"MYPASSWORD\",\"IntegratorKey\":\"MYINTEGRATORKEY\"}",
},
"payload" : JSON.stringify(payload)
};
UrlFetchApp.fetch(url, options);
}
Any help would be greatly appreciated.
You can try one thing, go to DocuSign webApp and do OAUTH to Google Drive once (by adding document from G Drive at the time of sending an envelope), this way you are giving permission to DocuSign to fetch documents from Google Drive on your behalf.
Once you Google OAUTH is done, then you can use DocuSign API to fetch documents from Google drive using Remote URL. For instance, in below call "112805:0B1-EnSmMAIOmc3RhcnRlcl9maWx" is the id of the document in the Google Drive. So using DS API, I am able to fetch the document and add it to my envelope creation API call.
"documents": [
{
"remoteUrl":"112806:0B1-EnSmMAIOmc3RhcnRlcl9maWx",
"documentId": "1",
"name": "Getting started"
}
]
Related
Disclaimer, I am new to GAS and admittedly brute forcing my way into trying to make something work. If someone has a pointer, I'd really appreciate it.
Goal: was to post to slack when someone submits a form. User in slack clicks a button. That data is recorded back onto sheets into a specific column and in the row related to the form content that was submitted.
Latest progress: able to post a message to slack when someone submits a form which includes an interactive (approve / reject) message. Once you click the button, I'm able to send confirmation to Slack with a custom message that the payload has been received. Payload is in the sheets in the top row, first column, but I'm unable to parse the value correctly. Pretty certain I need to set the range and then set the values, using some array notation. I've tried some difficult code and the best I could do was post the timestamp back to Sheets.
Here's the code and payload. I'd really appreciate any tips.
function postToSlack() {
var payload = {
"text": "Take a look at this form submitted.",
"attachments": [
{
"text": "press a button",
"fallback": "You are unable to make a selection",
"callback_id": "error",
"color": "#3AA3E3",
"attachment_type": "default",
"actions": [
{
"name": "review",
"text": "approve",
"style": "primary",
"type": "button",
"value": "approve",
"action_id": "button",
},
{
"name": "review",
"text": "need more info",
"style": "danger",
"type": "button",
"value": "reject",
"action_id": "button"
}
]
}
]
}
var options = {
'method': 'post',
'payload': JSON.stringify(payload)
}
var slackWebookURL = "https://hooks.slack.com/services/XXXXX"
var response = UrlFetchApp.fetch(slackWebookURL, options);
}
function doPost(e) {
Logger.log("I was called")
if (typeof e !== 'undefined')
Logger.log(e.parameter);
var ss = SpreadsheetApp.openById("sheet ID")
var sheet = ss.getSheetByName("Sheet1")
sheet.getRange(1, 1).setValue(JSON.stringify(e))
return ContentService.createTextOutput(' Thanks for reviewing! ');
}
{"parameter":{"payload":"{\"type\":\"interactive_message\",\"actions\":[{\"name\":\"review\",\"type\":\"button\",\"value\":\"approve\"}],\"callback_id\":\"error\",\"team\":{\"id\":\"***********\",\"domain\":\"slackname-global\"},\"channel\":{\"id\":\"********\",\"name\":\"testchannel\"},\"user\":{\"id\":\"U013FTGA30D\",\"name\":\"first.last\"},\"action_ts\":\"15998273855.588360\",\"message_ts\":\"159344554.002200\",\"attachment_id\":\"1\",\"token\":\"Vksfhjah3yXSvdHHn9H7RyBF\",\"is_app_unfurl\":false,\"original_message\":{\"type\":\"message\",\"subtype\":\"bot_message\",\"text\":\"Please review submission.\",\"ts\":\"1590853854.002200\",\"bot_id\":\"B014dfE8MU\",\"attachments\":[{\"callback_id\":\"error\",\"fallback\":\"You are unable to make a selection\",\"text\":\"press the appropriate button\",\"id\":1,\"color\":\"3AA3E3\",\"actions\":[{\"id\":\"1\",\"name\":\"review\",\"text\":\"approve\",\"type\":\"button\",\"value\":\"approve\",\"style\":\"primary\"},{\"id\":\"2\",\"name\":\"review\",\"text\":\"need more info\",\"type\":\"button\",\"value\":\"reject\",\"style\":\"danger\"}]}]},\"response_url\":\"https:\\/\\/hooks.slack.com\\/actions\\/T013PEGKE0L\\/1153573701509\\/3aLa2Ece7ZiRFIJ4oTWJCECC\",\"trigger_id\":\"1178789486208.1125492660020.527c043df757d682913d2e0501f30026\"}"},"queryString":"","
"postData":{"contents":"payload=%7B%22type%22%3A%22interactive_message%22%2C%22actions%22%3A%5B%7B%22name%22%3A%22review%22%2C%22type%22%3A%22button%22%2C%22value%22%3A%22approve%22%7D%5D%2C%22callback_id%22%3A%22error%22%2C%22team%22%3A%7B%22id%22%3A%22T013PEGKE0L%22%2C%22domain%22%3A%22shino-global%22%7D%2C%22channel%22%3A%7B%22id%22%3A%22C014PMG7VU4%22%2C%22name%22%3A%22gas%22%7D%2C%22user%22%3A%7B%22id%22%3A%22U013FTGA30D%22%2C%22name%22%3A%22slack.name%22%7D%2C%22action_ts%22%3A%221590853855.588360%22%2C%22message_ts%22%3A%221590853854.002200%22%2C%22attachment_id%22%3A%221%22%2C%22token%22%3A%22Vk4deddddayXSvdHHn9H7RyBF%22%2C%22is_app_unfurl%22%3Afalse%2C%22original_message%22%3A%7B%22type%22%3A%22message%22%2C%22subtype%22%3A%22bot_message%22%2C%22text%22%3A%22Name%21+Please+review+submission.%22%2C%22ts%22%3A%221590853854.002200%22%2C%22bot_id%22%3A%22B014AQ2Q8MU%22%2C%22attachments%22%3A%5B%7B%22callback_id%22%3A%22error%22%2C%22fallback%22%3A%22You+are+unable+to+make+a+selection%22%2C%22text%22%3A%22press+the+appropriate+button%22%2C%22id%22%3A1%2C%22color%22%3A%223AA3E3%22%2C%22actions%22%3A%5B%7B%22id%22%3A%221%22%2C%22name%22%3A%22review%22%2C%22text%22%3A%22approve%22%2C%22type%22%3A%22button%22%2C%22value%22%3A%22approve%22%2C%22style%22%3A%22primary%22%7D%2C%7B%22id%22%3A%222%22%2C%22name%22%3A%22review%22%2C%22text%22%3A%22need+more+info%22%2C%22type%22%3A%22button%22%2C%22value%22%3A%22reject%22%2C%22style%22%3A%22danger%22%7D%5D%7D%5D%7D%2C%22response_url%22%3A%22https%3A%5C%2F%5C%2Fhooks.slack.com%5C%2Factions%5C%2FT013PEGKE0L%5C%2F1153571101509%5C%2F3aLa2Ece7ZiRFIJ4oTWJCECC%22%2C%22trigger_id%22%3A%221178789486208.1125492660020.527c043df757d682913d2e0501f30026%22%7D","length":1699,"name":"postData","type":"application/x-www-form-urlencoded"},"contextPath":"","contentLength":1699}
I tried using the example get method on the google drive api documentation, however what is returned is not the full metadata of the file, only this:
"kind": "drive#file",
"id": "1vbLiXALYOYoVev1KD_ajVBfh5_CgvGgP",
"name": "3.png",
"mimeType": "image/png",
"result": {
"kind": "drive#file",
"id": "1vbLiXALYOYoVev1KD_ajVBfh5_CgvGgP",
"name": "3.png",
"mimeType": "image/png"
}
}
What should appear is something with many more fields:
{
"kind": "drive#file",
"id": "1vbLiXALYOYoVev1KD_ajVBfh5_CgvGgP",
"etag": "\"MTU4Njg3NTU3MjUxOQ\"",
"selfLink": "",
"webContentLink": "",
"alternateLink": "",
"embedLink": "",
"iconLink": "",
"thumbnailLink": "",
"title": "3.png"
// ... and so on
I get the full response when I use the "try api" screen on the api docs, but not when calling it from javascript:
function printFile(fileId) {
appendPre(fileId)
var request = gapi.client.drive.files.get({
'fileId': fileId
});
request.execute(function(resp) {
console.log(JSON.stringify(resp, null, 2))
});
}
I used the https://www.googleapis.com/auth/drive.file scope, which is listed as a scope sufficient for the request to be completed. Help!
How about this modification?
At Drive API V3, when fields property is not used, a part of fields is returned. So in your case, for example, you can retrieve other parameters using fields: "*". When your script is modified, please modify as follows.
From:
var request = gapi.client.drive.files.get({
'fileId': fileId
});
To:
var request = gapi.client.drive.files.get({
'fileId': fileId,
'fields': "*" // Added
});
Reference:
Parameters of Files: get
I am trying to create an envelope using E-signature post API from AppScript. I am able to Authenticate and call other Get API's like Status of Envelope and List of all envelopes successfully. But for this Post API I am facing below error.
"errorCode":"UNSPECIFIED_ERROR",
"message":"Non-static method requires a target."
here is the object which I am passing
var createEnvelopeObj =
{
"documents": [
{
"documentBase64": "JVBERi0----------DI0NGItMThmMzAtNS41LjEzLVNOQVBTSE9UCnN0YXJ0eHJlZgoxOTY2MDcKJSVFT0YK",
"documentId": "1323457",
"fileExtension": ".pdf",
"name": "sampledoc"
}
],
"emailSubject": "Please sign below Document ref: G654sfd238",
"recipients": {
"signers": [
{
"clientUserId": "xxxx#gmail.com",
"email": "xxxx#dddd.com",
"name": "xxxx",
"recipientId": "124",
"routingOrder": "1"
}
]
},
"status": "sent"
};
Below is the API call with above object
function DocusignPost(createEnvelopeObj){
var options = {
headers: {Authorization: 'Bearer eyJ0eXAiOiJNVCIsImFsZyI6IlJTMjU2Iiwia2lkIjoiNjgxO___xxxxx_VKdnH4FHUtI80s5xtZ9tusnP1DmYw '
},
'method' : 'post',
'contentType': 'application/json',
'payload' : JSON.stringify(createEnvelopeObj),
'muteHttpExceptions': true};
try{
var response = UrlFetchApp.fetch("https://demo.docusign.net/restapi/v2.1/accounts/xxxxxxx/envelopes", options );
var postresponce = JSON.parse(response);
Logger.log("postresponce : " + JSON.stringify(postresponce));
return postresponce;}catch(exp){
Logger.log(exp); }}
I have referred both these 57258880, 35047127 but I am using latest Auth method and passing object as payload, but still facing an issue.
Request someone to look into this and shed some light on it. I got stuck here for a couple of days.
I don't see any error in your JSON. So the next step I recommend is to obtain the API trace from DocuSign to see exactly what is being received from DocuSign.
See https://support.docusign.com/en/guides/ndse-user-guide-api-request-logging
You should also carefully check how you're reading in the PDF file before base64 encoding it. PDF files are (almost always) binary, so you need to read them using a binary-clean method.
I have a little proble with Google Sheets API
I have a Google Sheet Document on my GDrive. And I can work with it using Google Sheets API and Google Drive API. So I can move it, update it or make copies.
My task is:
1. make a copy of this document (I can do this)
2. publish this copy as web app. So each copy has a doGet() functon inside so it can be published as a Web app. Manually it can be done like: Publish -> Deploy as web app.. But I can't find any API to do this.
Get an URL of published service
UPDATE
I read documentation projects.deployments.create And I maanged to create a new deployment (before that I should create a version) But my new deployemnt has no web access, no url etc. IF I check via projects.deployments.list it shows:
{
"deploymentId": "AKfycbxVfuoeIQmumgy_Efhw12NCcqE7vqosYoxbDiKj5CT4mL_GbtybXsh1ppMIX22wQX20",
"deploymentConfig": {
"scriptId": "1zfjbALVe0jGbZCtqjFR0RP2-O___hR7MtAlx3biuJGXKsrKh3y1W0hMT",
"versionNumber": 1,
"manifestFileName": "appsscript",
"description": "v1"
},
"updateTime": "2019-05-13T22:33:23.760Z"
}
And if I will do this manually via web interface it will looks like
{
"deploymentId": "AKfycbyn3smPKxJcZwsm9SzSTtzNCAcWJzf1OVs4WTslvHo",
"deploymentConfig": {
"scriptId": "1zfjbALVe0jGbZCtqjFR0RP2-O___hR7MtAlx3biuJGXKsrKh3y1W0hMT",
"manifestFileName": "appsscript"
},
"updateTime": "1970-01-01T00:00:00Z",
"entryPoints": [
{
"entryPointType": "WEB_APP",
"webApp": {
"url": "https://script.google.com/macros/s/AKfycbyn3smPKxJcZwsm9SzSTtzNCAcWJzf1OVs4WTslvHo/exec",
"entryPointConfig": {
"access": "ANYONE_ANONYMOUS",
"executeAs": "USER_DEPLOYING"
}
}
}
]
}
The Apps Script API needs to be used. You can use the REST API and make a UrlFetchApp.fetch(url) request. This is a two step process. First you must create a new Apps Script file, then you must update that new Apps Script file with new contents in the manifest file. The manifest file must have a property for webapp which is what designates the type of deployment.
The code will probably look something like the following:
function deployA_project() {
var id, OAuthToken,options,payload,rtrn,url;
id = ScriptApp.getScriptId();//Get the project ID of this script project
Logger.log('id: ' + id)
url = 'https://script.googleapis.com/v1/projects/' + id + '/deployments';//For REST
OAuthToken = ScriptApp.getOAuthToken();
payload = {
"versionNumber": number,
"manifestFileName": string,
"description": string
}
options = {
"method" : "POST",
"muteHttpExceptions": true,
"headers": {
'Authorization': 'Bearer ' + OAuthToken
},
"contentType": "application/json",
"payload": JSON.stringify(payload)
};
rtrn = UrlFetchApp.fetch(url,options);
Logger.log('rtrn: ' + rtrn)
}
See the documentation:
projects.deployments.create
How the apps script project is deployed is designated in the manifest file:
{
"timeZone": "America/New_York",
"dependencies": {
},
"webapp": {
"access": "MYSELF",
"executeAs": "USER_DEPLOYING"
},
"exceptionLogging": "STACKDRIVER"
}
The API doesn't provide a configuration setting for creating the type of deployment. So, turning the deployment into a Web App is done by updating the manifest file. This must be a two step process. First, you create the new project file, then you update it with a JSON object of file content. You can designate the file content of the manifest file named "appsscript.json"
The content must be JSON in the following format:
{
"scriptId": "scriptId",
"files": [{
"name": "appsscript",
"type": "JSON",
"source": "{\"timeZone\":\"America/New_York\", \"webapp\": {\"access\": \"MYSELF\",\"executeAs\": \"USER_DEPLOYING\"},\"exceptionLogging\":\"STACKDRIVER\"}",
"lastModifyUser": {
"name": "MyName",
"email": "example#gmail.com",
}
}]
}
You need to make another request to a different url, and the request must be a PUT request in order to make an update:
url = 'https://script.googleapis.com/v1/projects/' + id + '/deployments/' + {deploymentId}
var newContent = {
"scriptId": "scriptId",
"files": [{
"name": "appsscript",
"type": "JSON",
"source": "{\"timeZone\":\"America/New_York\", \"webapp\": {\"access\": \"MYSELF\",\"executeAs\": \"USER_DEPLOYING\"},\"exceptionLogging\":\"STACKDRIVER\"}",
"lastModifyUser": {
"name": "MyName",
"email": "example#gmail.com",
}
}]
}
var requestBody = {};
requestBody.files = newContent;
requestBody.deploymentConfig = {
"scriptId": string,
"versionNumber": number,
"manifestFileName": string,
"description": string
}
options = {
"method" : "PUT",
"muteHttpExceptions": true,
"headers": {
'Authorization': 'Bearer ' + OAuthToken
},
"contentType": "application/json",
"payload": JSON.stringify(requestBody)
};
rtrn = UrlFetchApp.fetch(url,options);
Logger.log('rtrn: ' + rtrn)
I want to add sidebar when I am generating a new spreadsheet to my drive.
var file = Drive.Files.insert({
"mimeType": "application/vnd.google-apps.spreadsheet",
"parents": [{
id: folderId
}],
"title": 'Linked spreadsheets'
});