Figma Variables API + sd-transforms: Automating Designer Changes into Git PRs with Design Token CI/CD
Ever had this situation — a designer changed a color in Figma, a developer didn't know about it, and a week later someone asks "Hey, why is this a different color?" I've been there. At first I tried asking designers to share changes over Slack, tagging Jira tickets, attaching all sorts of manual processes — but something always slipped through the cracks. If you're running a design system team, you'll inevitably want to solve this problem structurally.
This post covers how to connect the Figma Variables REST API with Tokens Studio sd-transforms to build a Design Token CI/CD pipeline where designer changes in Figma automatically create GitHub PRs and trigger the token build pipeline. Rather than a conceptual overview, I want to show you how each piece fits together with working code. The depth is aimed at frontend developers with experience in design systems or CI/CD.
One thing I want to mention upfront — the read/write functionality of the Figma Variables REST API is only available on the Enterprise plan. If you're on Professional or below, you won't be able to complete the full pipeline, and routing through Tokens Studio plugin's direct Push method is the practical workaround. Check your plan first so you don't hit a wall later.
Core Concepts
Two Architecture Options
Before building the pipeline, it's worth choosing your path first. There are two main approaches, and your starting point depends on your team's situation.
| Approach | Flow | Prerequisites |
|---|---|---|
| Webhook Automation | Figma library publish → Webhook → receiver server → GitHub Actions → PR | Enterprise plan + receiver server |
| Plugin Direct Push | Designer clicks "Push Changes" → GitHub commit → GitHub Actions → PR | Available on all plans, requires manual action from designer |
Without an Enterprise plan, you can still build more than half the pipeline with the Plugin Direct Push approach. This post focuses primarily on Webhook automation, with the Plugin approach covered as an alternative.
Figma Variables REST API — The Gateway to Pulling Variables into Code
Figma Variables, released in 2023, lets you manage design values like colors, typography, and spacing as structured variables within Figma. You can read and write these variables externally via REST API — the endpoint itself is straightforward.
GET https://api.figma.com/v1/files/:file_key/variables/local
X-Figma-Token: your_personal_access_tokenThe JSON looks a bit complex at first glance, but the core is three things: collections, modes, and values.
{
"meta": {
"variableCollections": {
"VariableCollectionId:1:1": {
"id": "VariableCollectionId:1:1",
"name": "Colors",
"modes": [
{ "modeId": "1:0", "name": "Light" },
{ "modeId": "1:1", "name": "Dark" }
],
"defaultModeId": "1:0"
}
},
"variables": {
"VariableID:1:2": {
"id": "VariableID:1:2",
"name": "color/primary",
"resolvedType": "COLOR",
"valuesByMode": {
"1:0": { "r": 0, "g": 0.4, "b": 1, "a": 1 },
"1:1": { "r": 0.1, "g": 0.5, "b": 1, "a": 1 }
}
}
}
}
}Once you parse and transform this response into a W3C DTCG format token JSON, it becomes something Style Dictionary can consume. For a reference on the actual transformation logic, I recommend looking at src/token-export.ts in Figma's official repository figma/variables-github-action-example first. It has color rgba → hex conversion and per-mode branching well organized in real code.
Tokens Studio and sd-transforms — The Token Translator
Tokens Studio is a Figma plugin that acts as a bridge — exporting or importing Figma Variables as design token JSON and syncing directly with Git repositories like GitHub. From a designer's perspective, clicking "Push Changes" commits directly to a GitHub branch.
@tokens-studio/sd-transforms is an adapter package that transforms tokens exported by Tokens Studio into a format Style Dictionary can understand. Honestly, the separation of roles between these two tools was confusing when I first set things up — here's a simple breakdown:
| Tool | Role |
|---|---|
| Tokens Studio | Figma ↔ Git sync (manages token source) |
| sd-transforms | Tokens Studio format → Style Dictionary-compatible format conversion |
| Style Dictionary | Token JSON → platform-specific output (CSS/SCSS/iOS/Android, etc.) |
W3C DTCG Format — The Standard That Transcends Tools
The W3C Design Tokens Community Group's first stable spec (2025.10) was published in October 2025, and major tools including Figma, Penpot, and Sketch have adopted it. sd-transforms also natively supports transformation based on the $value/$type properties in this spec, and you can switch between legacy and DTCG formats via plugin settings.
{
"color": {
"primary": {
"$value": "#0066FF",
"$type": "color",
"$description": "Primary brand color"
}
}
}DTCG (Design Tokens Community Group): A W3C community group that defines a standard JSON format for exchanging tokens between design tools. Its hallmark is dollar-prefixed properties like
$valueand$type.
Practical Implementation
Example 1: Figma Webhook → Node.js Receiver Server → GitHub Actions Trigger
When a designer publishes a Figma library, a LIBRARY_PUBLISH webhook event fires. You need an intermediate server to receive this event and trigger GitHub Actions. Configuring it as serverless with Vercel Functions or AWS Lambda significantly reduces operational overhead.
// webhook-receiver.ts (Express-based)
import express from 'express';
import { Octokit } from '@octokit/rest';
const app = express();
app.use(express.json());
// Octokit: Official GitHub API SDK
const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
app.post('/figma-webhook', async (req, res) => {
// passcode validation — without this, anyone can trigger Actions from the outside
const passcode = req.headers['x-figma-passcode'];
if (passcode !== process.env.FIGMA_WEBHOOK_PASSCODE) {
return res.sendStatus(401);
}
const { event_type, file_key } = req.body;
if (event_type !== 'LIBRARY_PUBLISH') {
return res.sendStatus(200);
}
await octokit.actions.createWorkflowDispatch({
owner: process.env.GITHUB_OWNER!,
repo: process.env.GITHUB_REPO!,
workflow_id: 'sync-tokens.yml',
ref: 'main',
inputs: { figma_file_key: file_key }
});
res.sendStatus(200);
});| Code Point | Description |
|---|---|
x-figma-passcode validation |
Figma sends the passcode set during webhook registration as a header. Without this validation, anyone can call the endpoint and trigger Actions |
event_type !== 'LIBRARY_PUBLISH' |
Filters to only library publish events to prevent unnecessary Actions runs |
workflow_dispatch |
The API for manually triggering a GitHub Actions workflow from an external source |
figma_file_key |
The file identifier needed for subsequent Figma Variables API calls |
You also need to register the Figma Webhook. This can be done via REST API.
curl -X POST "https://api.figma.com/v2/webhooks" \
-H "X-Figma-Token: $FIGMA_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"event_type": "LIBRARY_PUBLISH",
"team_id": "YOUR_TEAM_ID",
"endpoint": "https://your-server.com/figma-webhook",
"passcode": "YOUR_SECRET_PASSCODE"
}'Example 2: GitHub Actions to Fetch Figma Variables → Build Tokens → Create PR
Once the trigger fires, the actual work happens in GitHub Actions. This is the workflow that fetches Figma Variables, transforms them with sd-transforms, and automatically creates a PR.
# .github/workflows/sync-tokens.yml
name: Sync Design Tokens
on:
push:
paths: ['tokens/**']
workflow_dispatch:
inputs:
figma_file_key:
type: string
description: Figma file key
jobs:
sync-and-build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Fetch Figma Variables
# Only call Figma API when triggered via workflow_dispatch
if: ${{ github.event_name == 'workflow_dispatch' && inputs.figma_file_key != '' }}
run: |
curl -s \
-H "X-Figma-Token: ${{ secrets.FIGMA_TOKEN }}" \
"https://api.figma.com/v1/files/${{ inputs.figma_file_key }}/variables/local" \
> tokens/figma-variables.json
- uses: pnpm/action-setup@v4
with:
version: 9
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'
- run: pnpm install --frozen-lockfile
- name: Transform Tokens
run: pnpm run build:tokens
- name: Create Pull Request
uses: peter-evans/create-pull-request@v6
with:
title: "🎨 Sync Design Tokens from Figma"
branch: "chore/sync-tokens-${{ github.run_number }}"
commit-message: "chore: sync design tokens from Figma variables"
body: |
This PR was automatically created because Figma Variables changes were detected.
Please review the changes and merge.
peter-evans/create-pull-request: A GitHub Action that detects file changes and automatically creates a PR. It handles branch creation, committing, and PR opening in a single step, simplifying pipeline configuration.
When triggered by a push event, inputs.figma_file_key is empty, causing the Figma API call to fail. The key is branching by event type with an if condition.
Example 3: sd-transforms + Style Dictionary Transform Script
This is the script that pnpm run build:tokens runs in GitHub Actions. It takes Tokens Studio format JSON and transforms it into CSS Variables, SCSS variables, and more.
In sd-transforms v1 and above, the API usage has changed. Using registerTransforms together with the usesSD3: true option is the currently recommended approach.
// build-tokens.mjs
import StyleDictionary from 'style-dictionary';
import { registerTransforms } from '@tokens-studio/sd-transforms';
// sd-transforms v1: use with usesSD3: true option
registerTransforms(StyleDictionary, { usesSD3: true });
const sd = new StyleDictionary({
source: ['tokens/**/*.json'],
platforms: {
css: {
transformGroup: 'tokens-studio',
transforms: ['name/kebab'], // specify only transforms to add on top of transformGroup
buildPath: 'dist/',
files: [
{
destination: 'variables.css',
format: 'css/variables',
options: {
outputReferences: true // preserve reference relationships as CSS var()
}
}
]
},
js: {
transformGroup: 'tokens-studio',
buildPath: 'dist/',
files: [
{
destination: 'tokens.js',
format: 'javascript/es6'
}
]
}
}
});
await sd.buildAllPlatforms();The outputReferences: true option matters. With this option, reference relationships between tokens are preserved as CSS var() functions, letting you leverage the dynamic nature of CSS Custom Properties in scenarios like theme switching. I built without this option the first time and ran into dark mode switching not working — only after finding the cause did I realize how important the initial configuration is.
Example 4: Tokens Studio Plugin Direct Push (Without a Server)
If running a Webhook server is too much overhead, or you're not on the Enterprise plan, having designers push directly to GitHub from the Tokens Studio plugin is a practical alternative.
[Designer → clicks "Push Changes" in Tokens Studio]
→ commits tokens/*.json to GitHub branch
→ GitHub Actions push event triggers (paths: ['tokens/**'])
→ sd-transforms + Style Dictionary runs
→ PR automatically createdThe core of the pipeline works without a server. However, since the designer has to operate the plugin directly, the level of automation is lower than the Webhook approach — and if the designer forgets to push or does it late, sync is delayed accordingly.
Pros and Cons
Advantages
| Item | Details |
|---|---|
| Single source of truth | Figma Variables become the sole source for design and code tokens, structurally eliminating inconsistency issues |
| Automated handoff | Designer changes in Figma are immediately reflected as code PRs, reducing manual communication |
| Version control | Every token change is tracked in Git history |
| Platform independence | Style Dictionary can simultaneously generate output for Web/iOS/Android |
| W3C standard compliance | The DTCG format ensures portability between tools |
Disadvantages and Caveats
The thing that caught our team most off guard was token naming conflicts — if you don't nail down the rules from the start, cleaning it up later is quite a hassle.
| Item | Details | Mitigation |
|---|---|---|
| Enterprise plan required | Figma Variables REST API read/write only available on Enterprise | Professional plan can use Tokens Studio Plugin direct Push instead |
| Webhook server operation | Requires a separate server or serverless function to receive LIBRARY_PUBLISH | Configuring serverless with Vercel Functions (/api/figma-webhook), AWS Lambda, etc. reduces management overhead |
| Token naming dependency | sd-transforms assumes Tokens Studio's naming conventions | Recommend documenting naming rules with design and dev teams before building the pipeline |
| Bidirectional conflict risk | Variable deletion not supported for code→Figma sync direction | sd-transforms' code→Figma sync conservatively only adds/modifies variables with matching names — it doesn't delete. Variables removed in code can remain in Figma, so it's safest to operate with a policy of allowing only additions/modifications for the code→Figma direction |
| Rate limits | Figma REST API has per-minute request limits | Debounce change events, introduce a caching layer |
| DTCG migration cost | May need to migrate existing legacy token formats to DTCG | Use the TokensBrücke plugin for bulk conversion, then migrate incrementally |
The Most Common Mistakes in Practice
-
Skipping
passcodevalidation for Figma Webhooks — If the receiver server doesn't validate theX-Figma-Passcodeheader, anyone can trigger Actions from the outside. Be sure to include logic that checks passcode matching, as shown in the Example 1 code above. -
Disabling the
outputReferencesoption and hardcoding all values — Once the reference structure is flattened, dynamic theme switching with CSS Custom Properties becomes impossible. If there are references between tokens (e.g.,semantic.color.primaryreferencesprimitive.blue.500), it's better to keep this option on. -
Design and dev teams managing token naming rules separately — If Tokens Studio's naming conventions conflict with the codebase's CSS class naming, sd-transforms output won't match expectations. Consolidating naming rules across teams before building the pipeline saves a lot of trouble later.
Wrapping Up
That situation from the beginning of this post — "Hey, why is this a different color?" — happens a lot less once this pipeline is in place. By connecting the Figma Variables REST API with sd-transforms, you can structurally eliminate the manual work and inconsistency problems in the flow from design changes to code.
Rather than trying to build everything at once, breaking it into these steps makes it much more manageable:
3-step starting points based on your situation:
-
Install Tokens Studio plugin and verify GitHub integration (Time: 30 min–1 hour / Requirement: all plans) — Enter your GitHub Personal Access Token and repository info in the plugin settings and verify that clicking "Push Changes" commits
tokens/*.json. You can immediately experience half the pipeline working without a Webhook server — I validated things in this order too. -
Add
build-tokens.mjsandsync-tokens.yml(Time: 1–2 hours / Requirement: all plans) — Install dependencies withpnpm add -D @tokens-studio/sd-transforms style-dictionary, wire the Example 3 script topnpm run build:tokens, and attach thepeter-evans/create-pull-requestAction to verify automatic PR creation. -
Register Figma Webhook + deploy Vercel Functions receiver server (Time: half a day / Requirement: Enterprise plan) — Deploy the Example 1 receiver server code to Vercel and register the
LIBRARY_PUBLISHWebhook via the Figma REST API to complete the full-automation pipeline where a designer's library publish automatically creates a PR.
Next post: I plan to cover how to connect runtime theme switching (dark mode, brand themes) with the token pipeline using Style Dictionary's
outputReferencesand CSS Custom Properties.
References
- Figma Variables REST API Official Docs
- Figma Webhooks V2 Official Docs
- figma/variables-github-action-example (Official GitHub)
- Tokens Studio GitHub Sync Official Docs
- Tokens Studio + GitHub Actions Official Guide
- tokens-studio/sd-transforms GitHub
- Style Dictionary + SD Transforms | Tokens Studio Docs
- Token Format - W3C DTCG vs Legacy | Tokens Studio Docs
- Syncing Figma variables with Design Tokens - Nate Baldwin (Medium)
- Automating design systems with Figma Variables REST API - Medium
- Building a Figma to GitHub token pipeline - DEV Community
- Advanced Figma Webhook Integration - Poespas Blog (2025)
- Syncing Figma Variables with GitHub Actions - James Ives
- GitFig - Figma Variables ↔ GitHub Sync Tool
- From Figma to npm Package: Automate Your Design Tokens Pipeline