You'll learn how to push SyncSketch messages via webhooks. In this example we'll post to a Discord server
SyncSketch has introduced support for webhooks, which allows users to receive notifications of various events like approval status changes or new item uploads. These webhooks can be used to post information into a chat channel, send a text message, or any other method of the user's choosing.
The webhook features of the SyncSketch API are currently available only to accounts that are on an Enterprise or Education Plus plan.
The concept behind webhooks is very simple, and from a coding standpoint isn't difficult either. The idea is that every time an event occurs in your SyncSketch account (an item is uploaded, the approval status changes, etc.), a message is sent to a URL that you provide. Your job is to make a function at that URL listen for the message and do something useful with it.
The easiest way to build your listener is by using Amazon Web Services, which lets you set up a function that has a URL. This way, you don't have to set up, administer or maintain a public-facing server. We will be using primarily an AWS service called Lambda for the functions.
This post will discuss the required code and the AWS configuration details. We will set up an integration with a chat application (Discord, because it's simple and free), but the AWS configuration could be adapted to integrate it with anything that has an API or has webhook support itself. The actual code is simple; there isn't much to it. The main thing we have to do is set up the API on AWS.
Discord supports incoming webhooks, in the sense that you can start your own server and get a URL that will post incoming messages into a chat channel on that server. Discord expects incoming messages to be in a particular format. SyncSketch does not currently allow users to specify the output format of its webhooks, so in this case we are going to create an intermediary function that listens for the SyncSketch webhooks and uses the information in that message to build a new message in the right format for Discord, then sends its own webhook to Discord. (This same approach could be used to adapt SyncSketch webhooks to virtually anything else.)
The sequence of steps is as follows:
- Get an AWS account
- Set up a Discord server with a Webhook
- Write an AWS Lambda function that posts into the Discord server
- Set up an API gateway for SyncSketch to send messages through
- Set up a webhook in SyncSketch
If you've never done these things before: Every one of these steps is easier than it sounds.
Getting your AWS account
If you don't already have an account, go ahead and go to aws.amazon.com and sign up for an account (or log in if you already have one). This Lambda function doesn't do much so it will most likely stay in the free tier.
Setting Up Your Discord Server
Once you have a Discord account and have downloaded the desktop client, setting up your own server is amazingly simple, and can be done in about two minutes.
In the desktop version of Discord:
-
Click the green icon with the plus to create a server.
-
Choose to Create My Own, then For Me and My Friends.
-
Give the new server a name. The icon is optional.
Now that you have a server, let's set up a webhook for it.
-
Click the plus sign next to "Text Channels" and create a new text channel called "project-notifications"
-
Click the gear icon next to project-notifications in the list at the left.
-
Click Integrations in the nav bar at the left.
-
Click Create Webhook.
-
Name it "Status Changes".
-
Click Copy Webhook URL.
-
Paste the URL somewhere where you can copy it later. (If you lose it, you can still get it from Discord.)
-
Click Save Changes.
That URL is the webhook; properly formatted messages ("requests") to that URL will add a message into the project-notifications channel. Now that you have the magic URL, let's take it to AWS.
Writing Your Lambda Function
Back in AWS, log into the AWS console. This will present you with a dizzying list of services. For this section, the service we're interested in is Lambda. Find it on the page and open it.
Make a note of the Region you're "in". In this example, this account is working in the AWS region "N. California (us-west-1)". A notion peculiar to AWS is that your services and functions are associated with a particular region, so if you're not in the right region (or referencing it) you will be wondering why your function is missing.
- Choose your region from the menu in the upper right (i.e.us-west-1). This is the region where you will find your lambda function when you log into the console.
- Click on the Functions section in the action menu at the left and select Create function.
- Click Author from Scratch.
- Give your function a Name. In this example we'll call it discord-wh-relay.
- Leave everything else at the defaults and select Create function. (For this example, we'll be writing in JavaScript, so Node.js 14.x is appropriate.)
- The view that appears should have a Code source section. A folder labeled discord-wh-relay will be selected, with an index.mjs file just underneath.
- Double-click index.mjs. This will reveal a little code template for the handler that will handle the incoming webhook from SyncSketch.
- Delete the code in the template.
Paste in these lines:
const https = require('https');
const url = require('url');
function handler(event, ctx, cb) {
const whurl = "DISCORD_WEBHOOK_GOES_HERE";
const message = `The approval status of "GroomFaintsWIP_5.mp4" in the "Wedding Scene" review has changed from **In Progress** to **Approved**`;
// CALL THE SENDER FUNCTION HERE
};
// SENDER FUNCTION GOES HERE
exports.handler = handler;
Paste in the Discord webhook between the quotes of the var whurl= line. If you need to recover your webhook URL, see "troubleshooting" below for instructions on how to get it in SyncSketch.)
Next, we'll add the line that triggers the webhook message to be sent. This will replace the // CALL THE SENDER FUNCTION HERE line:
sendWebhook(message, whurl)
.then((result) => {
cb(null, JSON.stringify(result));
})
.catch(error => cb(error));
}
Adding the Webhook Sender
Finally, we'll add the webhook() function that we are using to send the webhook message to Discord. You don't need to know much about this code, except that it creates and sends its own webhook request, formatting the message that we give it into the appropriate format for Discord. Just paste it in place of // SENDER FUNCTION GOES HERE:
function sendWebhook(message, wh) {
return new Promise((resolve, reject) => {
const whurl = url.parse(wh);
const payload = JSON.stringify({
'username': 'SyncSketch',
'content': message
});
const options = {
hostname: 'discordapp.com',
headers: {'Content-type': 'application/json'},
method: "POST",
path: whurl.path,
};
var bufferData = '';
const req = https.request(options, (res) => {
res.on("data", (data) => {
bufferData += data;
});
res.on("end", () => {
let data = {message: 'Webhook message sent!'};
if(bufferData.length) {
data = JSON.parse(bufferData);
}
if(data.code) {
reject(JSON.stringify({statusCode:data.code, body:data.message}));
} else {
resolve({statusCode: 200, body: data.message});
}
});
});
req.on("error", (error) => {
reject(Error(error));
});
req.write(payload);
req.end();
});
}
exports.handler = handler;
If you want to know more:
For Discord, this bit here is the magic:
const payload = JSON.stringify({
'username': 'SyncSketch',
'content': message
});
Discord wants a JSON object in the body containing username and content text strings and that's it. Everything else you see here is about building a webhook message of our own and doing some error-checking, then sending it and relaying Discord's response back.
Let's test it to see how we're doing so far. This should be enough code to do something gratifying.
Creating a Test Event
Lambda functions respond to events. The events we are concerned with for this example will be approval status changes in SyncSketch. Now we are going to create a test event that will eventually simulate a message coming from SyncSketch.
Just above the code window is a Test button that will trigger an event. Click the triangle next to it and choose Configure Test Event.
In the dialog that appears, a test event template called hello-world will appear, which is suitable place to start. Rename it "approvalStatusChange" and for now we'll leave everything else the way it is:
Click Save at the bottom of the dialog.
If you click Test now, the code tab will be replaced with a console tab that displays unformatted text. No message appears in Discord, and the console shows this raw response data:
{
"statusCode": 200,
"body": "\"Hello from Lambda!\""
}
Wait, where is that coming from? What's going on? The answer is that you need to deploy the new code before you test it. Think of it like a Save button, or more aptly the Publish button in a blogging app. Every time you change the code in here, you need to deploy it, so let's do that.
Click the big Deploy button.
Now you can click Test. If all is well, you should hear a "bloop" and your test message should appear in Discord! An Execution Result tab will show the following response:
"{\"statusCode\":200,\"body\":\"Webhook message sent!\"}"
The Code tab will return you to the code.
Troubleshooting
Computers were made to disappoint. Writing code is no exception. Here are some troubleshooting tips if you're not seeing the Discord message when you click Test.
Nothing posts to Discord
If everything seems OK and you're getting a status code of 200 back but nothing is appearing in Discord, the problem is surely in the webhook URL in the path variable. Go back into Discord and open the settings gear next to your server's "project-notifications" channel. Click Integrations, then View Webhooks. Select your "Status Changes" webhook and click Copy Webhook URL. Paste it between the double-quotes after path=.
Getting errors that suggest a problem with my code
If you're getting JavaScript errors, rebuild your JavaScript as follows:
Delete all of the code from index.js. Copy these first lines and paste them in:
const https = require('https');
const url = require('url');
function handler(event, ctx, cb) {
const whurl = "https://discord.com/api/webhooks/825812896431996938/og_Gr7e84y54iuFPU24Coqwgxsw7On1SmQbCFYB__GC9Pj-Q4UlZqBBpmb88MIB19iHf";
const message = JSON.stringify(`The approval status of **GroomFaintsWIP_5.mp4** in the **Wedding Scene** review has changed from **In Progress** to **Approved**`);
webhook(message, whurl)
.then((result) => {
cb(null, JSON.stringify(result));
})
.catch(error => cb(error));
}
exports.handler = handler;
- Then after those lines, append the lines from the "Add the Webhook Sender" section above.
- Finally, replace the Discord webhook URL in the example with your own, according to the procedure described in the "Nothing posts to Discord" solution above.
Creating the API Gateway
Message traffic coming into a Lambda function like this one generally must come through an API Gateway, so let's add one.
Scroll to the top of your function's page and click the Add Trigger button.
From the dropdown in the following dialog, select API Gateway.
For the trigger configuration we will create an open REST API:
- Leave Create an API selected.
- Choose the REST API box.
- Under Security, select Open.
- Click Add.
By default the name will be discord-wh-relay-API:
In the Triggers section of the Configuration tab, click the linked name to configure it.
Configuring the API Gateway
The new tab that appears is used to configure a new API gateway. The good news is that the way it's set up by default will mostly work just fine for our purposes; all we need to do is disable Lambda proxy integration and deploy it.
- Click on the Resources link in the nav bar at the left if it's not already selected.
- Click "ANY" under /discord-wh-relay-API. This will display the Method execution configuration screen.
- Click Integration Request in the header of the upper right-hand box.
- Uncheck Use Lambda Proxy Integration and confirm in the dialogs that appear.
- Deploy the API. If there's no big Deploy button in the upper right, deploy it from the dropdown:
In the dialog that follows, set the Deployment stage to default and click Deploy.
Now we can test the API gateway's ability to trigger the Lambda function.
Testing the trigger from the Gateway
- Click on the Resources link in the nav bar at the left.
- Click "ANY" under '/discord-wh-relay-API`
- Click Test. The following big dialog will appear.
- Set the Method to POST, since that is the HTTP method used to send webhook messages.
- Leave everything else the way it is and scroll to the bottom, and click Test.
The Discord bloop should sound and your dummy message should appear again in your Discord channel. It is now available through a public API.
Troubleshooting
If the API gateway test fails, check the following:
- The URL for your Discord webhook is in quotes in the path specified in index.js
- You deployed your changes in the Lambda function
- You unchecked Use Lambda Proxy Integration as described above
- Your dummy message has the correct syntax. Pay attention to the use of single quotes and double quotes in the example.
Examine the dense logging that appears in the column at the right of the API Gateway Test page, especially near the bottom. You may see an explanation (or at last a hint) as to why the execution failed.
If the images above don't look anything like what you're seeing:
- You may have created an HTTP API by mistake. Delete the discord-wh-relay-API and try again, making sure you click REST API in the API configuration dialog.
- You may have created the API gateway via some other route in the AWS console. This can work, but the process is somewhat different; it's considerably easier if you create it using the Add Trigger button on the Lambdas page, as directed above.
Writing your Webhook Listener
Now we'll get the Lambda function to handle real input.
As input, the SyncSketch webhook will send JSON in the body of the message that looks something like this example, which is what is sent with the item_approval_status_changed event:
{
"account": {
"name": "Discord-Relay Demo Account",
"id": 100
},
"item_name": "Uploaded Movie.mp4",
"review": {
"name": "Opening Credits Review",
"id": 123
},
"item_creator": {
"id": 1,
"name": "Mike Jennings",
"email": "jennings@example.com"
},
"new_status": "approved",
"project": {
"name": "Our Amazing Movie Project",
"id": 101
},
"item_id": 4056789,
"old_status": "wip"
}
We will use this example data model to build the message that gets posted to the chat.
In our code, we defined the message to be a dummy message. Now let's replace the dummy text with the values that come from the webhook message, in the event object.
- In the AWS Lambda console, return to the Lambda tab (Lambda > Functions > discord-wh-relay) and click on its Code tab
- Double-click index.js in the Code source section.
- Where it says:
const message = JSON.stringify(`The approval status of **GroomFaintsWIP_5.mp4** in the **Wedding Scene** review has changed from **In Progress** to **Approved**`);
…replace it with this:
const message = JSON.stringify(`The approval status of **${event.item_name}** in the **${event.review.name}** review has changed from **${event.old_status ? event.old_status: 'No Status'}** to **${event.new_status}**`);
Testing the Update
To test this, we will need to send a message that has a body like the one SyncSketch will send.
- Copy the example JSON above.
- Switch from the Code tab to the Test tab.
- Paste to replace the Event JSON in theapprovalStatusChange test event and click Save.
- Click the Save button.
Now test it again by clicking Test, and verify that the body data is being formatted correctly in the Discord chat.
Now let's make sure the API gateway is able to pass the POST body from the webhook to your Lambda function.
- Switch to the Configuration tab and click on the name of the API gateway.
- Click the Test box for the ANY method.
- Set the Method to POST.
- Paste the example JSON from above into theRequest Body code box at the bottom.
- ClickTest.
If this succeeds, you should see a message in Discord with data from this request body. Your function is complete and working! Now all we have to do is set up the SyncSketch webhook to send messages to it.
Creating a SyncSketch Webhook
Before we start setting up the webhook in your SyncSketch account, you need the invoke URL(or API Endpoint) of your API Gateway.
To add the Webhook into SyncSketch:
- Switch to the Configuration tab on the Lambda's page.
- Copy the discord-wh-relay API endpoint URL.
- In SyncSketch, go to the Workspace Settings and select Webhooks from the tab column at the left.
- Click+ Add Webhook.
- From the Action dropdown, select Item Status Changed
- Paste in the API endpointURL.
- Click Create Webhook.
Try It!
In your SyncSketch account, load an item and change its approval status. If all goes well: Bloop, and a new Discord message!
Other Events
Each event notification will have a different JSON data structure in its body. If you require more than one notification type, you will need to determine the event type before you build the Discord message. For this reason, we will add a little more complexity to index.js so that it supports multiple event types.
Other Listeners
You may prefer to receive notifications using other listeners, like Slack or SMS text messages. Here's how you might adapt the code we've made for other uses.
Sending Custom Request Headers
You may wish to send data specific to your account along with each webhook message, such as an authentication token. SyncSketch does not currently allow you to specify anything to be added to the body of the messages, but it does let you add custom HTTP headers in the request. Normally, Lambda only provides your handler function with body content from the message, so we will need to configure it to pass along the headers as well.
Currently, we have our Discord webhook URL hard-coded in our Lambda. This is good for security, but not so good for versatility. To illustrate the configuration details we're going to update our example listener to accept Discord webhook URLs as a custom header, in order to allow you to use this one Lambda function for any Discord server channel.