Two functions send a text message from inside a Zoho Creator app through GoTo Connect: one keeps an OAuth access token fresh, the other sends the message. Wire the second to a button and your users can text a contact without leaving the record.
Updated for the GoTo rebrand. LogMeIn became GoTo and Jive became GoTo Connect, so the messaging host moved — the original posted to api.jive.com/messaging/v1/messages, and the documented endpoint is now api.goto.com/messaging/v1/messages. The old host still answers, but it is undocumented legacy infrastructure with no published shutdown date; do not build on it. The auth host is unchanged and still correct at authentication.logmeininc.com, which is counterintuitive but confirmed against GoTo’s current docs. The request body shape did not change. Both functions have also been moved off the legacy postUrl() onto invokeurl.
messaging.v1.send scope and an initial refresh token. Start at the GoTo Developer Center.GOTO_Token with Access_Token and Refresh_Token (both multi line). Seed Refresh_Token by hand the first time.Care_Coordinators with Work_Email and Work_Phone in this example.string GOTO.getAccessToken()
{
CLIENT_ID = "YOUR_GOTO_CLIENT_ID";
CLIENT_SECRET = "YOUR_GOTO_CLIENT_SECRET";
stored = GOTO_Token[ID != null] sort by Added_Time desc range from 0 to 1;
headerMap = Map();
headerMap.put("Authorization","Basic " + zoho.encryption.base64Encode(CLIENT_ID + ":" + CLIENT_SECRET));
headerMap.put("Content-Type","application/x-www-form-urlencoded");
bodyMap = Map();
bodyMap.put("grant_type","refresh_token");
bodyMap.put("refresh_token",stored.Refresh_Token);
response = invokeurl
[
url : "https://authentication.logmeininc.com/oauth/token"
type : POST
parameters : bodyMap
headers: headerMap
];
accessToken = response.getJSON("access_token");
refreshToken = response.getJSON("refresh_token");
if(accessToken == null || accessToken == "")
{
info "GoTo auth failed: " + response.toString();
return "";
}
stored.Access_Token = accessToken;
// GoTo rotates the refresh token. Persist it or the integration dies at day 30.
if(refreshToken != null && refreshToken != "")
{
stored.Refresh_Token = refreshToken;
}
return accessToken;
}void GOTO.sendSMS(string msg, string toPhone)
{
// Sender number comes from the logged-in user's record
getUser = Care_Coordinators[Work_Email == zoho.loginuserid];
if(getUser.count() == 0 || getUser.Work_Phone == null || getUser.Work_Phone == "")
{
info "No sending number configured for " + zoho.loginuserid;
return;
}
fromPhone = "+1" + getUser.Work_Phone.replaceAll("[^0-9]","");
accessToken = thisapp.GOTO.getAccessToken();
if(accessToken == "")
{
return;
}
headerMap = Map();
headerMap.put("Authorization","Bearer " + accessToken);
headerMap.put("Content-Type","application/json");
contacts = List();
contacts.add(toPhone);
payload = Map();
payload.put("ownerPhoneNumber",fromPhone);
payload.put("contactPhoneNumbers",contacts);
payload.put("body",msg);
response = invokeurl
[
url : "https://api.goto.com/messaging/v1/messages"
type : POST
parameters : payload.toString()
headers: headerMap
];
info "GoTo SMS response: " + response.toString();
}The 30-day trap. GoTo refresh tokens expire after 30 days and rotate — a new one comes back in the response and the old one stops working. If you do not write the new value back, everything runs perfectly for a month and then fails silently. That write-back is the stored.Refresh_Token = refreshToken line, and it is the single most important line in the first function.
Open a stateless form from a report button so the user can compose the message:
void Open.txtMessage(int recordId)
{
openUrl("#Form:TXT_MSG?LID=" + recordId,"popup window");
}The stateless form collects the message body and calls thisapp.GOTO.sendSMS(input.Message, input.To_Phone); on submit.
+1XXXXXXXXXX. The replaceAll("[^0-9]","") strips whatever formatting your users typed.This script is part of the free Creator Scripts Deluge Library.
All 39 Deluge scripts, the full Zoho Creator course, and every downloadable asset are now free. Get free access →