souporserious

Getting Started with Figma Webhooks

3 min read

Webhooks offer a first-class way to react to changes in Figma. In this article, we'll build a small service in Node to listen for these events.

Start by creating a simple server that parses JSON using express:

1const express = require('express')
2const bodyParser = require('body-parser')
3
4const PORT = 3000
5const app = express()
6
7app.use(bodyParser.json())
8
9app.listen(PORT, () => console.log(`🚀 Server running on port ${PORT}`))

Webhooks require a public endpoint that Figma can ping to let us know when an event has happened. You will most likely use this in a server or CI tool, but today we’ll be testing locally using the wonderful ngrok package. This package allows us to point our local server to a public address easily:

1const express = require('express')
2const bodyParser = require('body-parser')
3const ngrok = require('ngrok')
4
5const PORT = 3000
6const app = express()
7
8app.use(bodyParser.json())
9
10app.listen(PORT, () => console.log(`🚀 Server running on port ${PORT}`))
11
12ngrok.connect(PORT).then(async (endpoint) => {
13 // public endpoint ready to use 🎉
14})

Now that we have a public URL, we can create our webhook through Figma’s API and subscribe to any events for a team we’re on. We’ll use axios to make interacting with the API easier and the internal crypto package to create the required passcode:

1const crypto = require('crypto')
2const response = await axios({
3 url: 'https://api.figma.com/v2/webhooks',
4 method: 'post',
5 headers: {
6 'X-Figma-Token': process.env.FIGMA_TOKEN,
7 },
8 data: {
9 event_type: 'FILE_UPDATE',
10 team_id: '68782045876942050',
11 passcode: crypto.randomBytes(48).toString('hex'),
12 endpoint,
13 },
14})

Note that when running our script, we expect a figma token to be available as an environment variable. This is required for authentication and can be done using a package like dotenv or set before running the Node script.

Once our webhook is created, we must respond with a 200 status code; otherwise Figma will keep trying to ping our endpoint:

1app.post('/', (request, response) => {
2 const { file_name, timestamp } = request.body
3 console.log(`${file_name} was updated at ${timestamp}`)
4 response.sendStatus(200)
5})

We now have a working Figma Webhook service 🎉

Security

Although our code is working, it’s still open to attackers. We’ll use the passcode we created previously and check whether the same passcode has been sent back to us and respond appropriately:

1const express = require('express')
2const bodyParser = require('body-parser')
3const ngrok = require('ngrok')
4const axios = require('axios')
5const crypto = require('crypto')
6
7const PORT = 3000
8const app = express()
9const passcode = crypto.randomBytes(48).toString('hex')
10
11app.use(bodyParser.json())
12
13app.post('/', (request, response) => {
14 if (request.body.passcode === passcode) {
15 const { file_name, timestamp } = request.body
16 console.log(`${file_name} was updated at ${timestamp}`)
17 response.sendStatus(200)
18 } else {
19 response.sendStatus(403)
20 }
21})
22
23app.listen(PORT, () => console.log(`🚀 Server running on port ${PORT}`))
24
25ngrok.connect(PORT).then(async (endpoint) => {
26 const response = await axios({
27 url: 'https://api.figma.com/v2/webhooks',
28 method: 'post',
29 headers: {
30 'X-Figma-Token': process.env.FIGMA_TOKEN,
31 },
32 data: {
33 event_type: 'FILE_UPDATE',
34 team_id: '687045185276946050',
35 passcode,
36 endpoint,
37 },
38 })
39 console.log(`🎣 Webhook ${response.data.id} successfully created`)
40})

Conclusion

We looked at how to utilize Figma’s new webhooks to respond to changes when events happen within teams. In a future article, we’ll learn how we can use these hooks to source component images whenever a file updates using the Figma Tools library. I encourage you to play around with this new API and see if you can automate any part of your workflow!

Updated:
  • figma
  • design
  • development
  • webhooks
Previous post
The Key to Calling React Hooks Conditionally
Next post
Bundling Web Workers for NPM
© 2022 Travis Arnold