Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Set up Figma REST API authentication with personal access tokens or OAuth 2.0. Use when connecting to the Figma API, generating tokens, configuring scopes, or setting up OAuth flows for Figma integrations. Trigger with phrases like "install figma", "setup figma API", "figma auth", "figma personal access token", "figma OAuth".
.claude/skills/jeremylongshore-figma-install-auth/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-02 | ✗→✓ | ▲ Improved | 13% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 70% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 72% | 0% |
| case-11 | ✗→✓ | ▲ Improved | 97% | 0% |
| case-12 | ✗→✓ | ▲ Improved | 246% | 0% |
Configure authentication for the Figma REST API. Figma supports two auth methods: Personal Access Tokens (PATs) for scripts and server-side tools, and OAuth 2.0 for apps that act on behalf of users. All requests go to https://api.figma.com.
/design/ in a Figma URL)| Scope | Access | Use Case | |-------|--------|----------| | file_content:read | Read file JSON | Inspecting layers, extracting design tokens | | file_content:write | Modify files | Programmatic design updates | | file_comments:read | Read comments | Review tooling | | file_comments:write | Post comments | Automated feedback | | file_dev_resources:read | Dev resources | Dev mode integrations | | file_variables:read | Read variables | Design token sync | | file_variables:write | Write variables | Token pipeline | | webhooks:write | Manage webhooks | Event-driven automation |
bash# .env (NEVER commit to git) FIGMA_PAT="figd_your-personal-access-token" FIGMA_FILE_KEY="abc123XYZdefaultFileKey" # .gitignore .env .env.local .env.*.local
bash# Test with curl -- should return your user profile curl -s -H "X-Figma-Token: ${FIGMA_PAT}" \ https://api.figma.com/v1/me | jq '.handle, .email'
typescript// verify-figma.ts const PAT = process.env.FIGMA_PAT!; const res = await fetch('https://api.figma.com/v1/me', { headers: { 'X-Figma-Token': PAT }, }); if (!res.ok) throw new Error(`Figma auth failed: ${res.status}`); const me = await res.json(); console.log(`Authenticated as ${me.handle} (${me.email})`);
Use OAuth when your app needs to act on behalf of other Figma users.
typescript// 1. Redirect user to Figma authorization URL const authUrl = new URL('https://www.figma.com/oauth'); authUrl.searchParams.set('client_id', process.env.FIGMA_CLIENT_ID!); authUrl.searchParams.set('redirect_uri', 'https://yourapp.com/auth/callback'); authUrl.searchParams.set('scope', 'file_content:read,file_comments:write'); authUrl.searchParams.set('state', crypto.randomUUID()); authUrl.searchParams.set('response_type', 'code'); // Redirect: res.redirect(authUrl.toString()); // 2. Exchange code for access token (must happen within 30 seconds) async function exchangeCode(code: string): Promise<string> { const res = await fetch('https://api.figma.com/v1/oauth/token', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ client_id: process.env.FIGMA_CLIENT_ID!, client_secret: process.env.FIGMA_CLIENT_SECRET!, redirect_uri: 'https://yourapp.com/auth/callback', code, grant_type: 'authorization_code', }), }); const { access_token, refresh_token, expires_in } = await res.json(); // Store refresh_token securely for later use return access_token; } // 3. Refresh expired tokens async function refreshToken(refreshToken: string): Promise<string> { const res = await fetch('https://api.figma.com/v1/oauth/refresh', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ client_id: process.env.FIGMA_CLIENT_ID!, client_secret: process.env.FIGMA_CLIENT_SECRET!, refresh_token: refreshToken, }), }); const { access_token } = await res.json(); return access_token; }
.envGET /v1/me returning your user handle| Error | Status | Cause | Solution | |-------|--------|-------|----------| | 403 Forbidden | 403 | Token lacks required scope | Regenerate PAT with correct scopes | | Invalid token | 403 | Expired or revoked PAT | Generate a new token (90-day max) | | OAuth code expired | 400 | Code exchange took >30s | Retry auth flow; exchange immediately | | Invalid redirect_uri | 400 | Redirect URL mismatch | Must match URL registered in Figma OAuth app settings | | Rate limited | 429 | Too many auth attempts | Wait for Retry-After header value |
typescript// src/figma-client.ts export function figmaFetch(path: string, options: RequestInit = {}) { const token = process.env.FIGMA_PAT; if (!token) throw new Error('FIGMA_PAT environment variable is not set'); return fetch(`https://api.figma.com${path}`, { ...options, headers: { 'X-Figma-Token': token, 'Content-Type': 'application/json', ...options.headers, }, }); } // Usage const file = await figmaFetch(`/v1/files/${fileKey}`).then(r => r.json());
After successful auth, proceed to figma-hello-world for your first real API call.
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→fail | 12,632 | 10,000 | -21% | 1 | 1 | 0% | 2,553 | 3,505 | +37% | 0 | 0 | — |
case-02 | fail→pass | 19,408 | 18,196 | -6% | 1 | 1 | 0% | 4,149 | 4,687 | +13% | 0 | 0 | — |
case-03 | fail→fail | 22,228 | 16,524 | -26% | 1 | 1 | 0% | 3,651 | 5,025 | +38% | 0 | 0 | — |
case-04 | fail→pass | 9,585 | 7,407 | -23% | 1 | 1 | 0% | 1,685 | 2,868 | +70% | 0 | 0 | — |
case-05 | pass→pass | 5,526 | 3,188 | -42% | 1 | 1 | 0% | 1,050 | 2,339 | +123% | 0 | 0 | — |
case-06 | pass→pass | 11,757 | 11,128 | -5% | 1 | 1 | 0% | 2,194 | 3,373 | +54% | 0 | 0 | — |
case-07 | pass→pass | 5,768 | 3,861 | -33% | 1 | 1 | 0% | 983 | 2,329 | +137% | 0 | 0 | — |
case-08 | pass→pass | 9,681 | 4,595 | -53% | 1 | 1 | 0% | 1,655 | 2,445 | +48% | 0 | 0 | — |
case-09 | fail→pass | 7,545 | 3,608 | -52% | 1 | 1 | 0% | 1,286 | 2,217 | +72% | 0 | 0 | — |
case-10 | pass→pass | 8,747 | 5,811 | -34% | 1 | 1 | 0% | 1,653 | 2,586 | +56% | 0 | 0 | — |
case-11 | fail→pass | 5,680 | 3,007 | -47% | 1 | 1 | 0% | 1,053 | 2,076 | +97% | 0 | 0 | — |
case-12 | fail→pass | 18,930 | 3,638 | -81% | 1 | 1 | 0% | 649 | 2,244 | +246% | 0 | 0 | — |
case-13 | fail→pass | 11,805 | 4,543 | -62% | 1 | 1 | 0% | 1,870 | 2,370 | +27% | 0 | 0 | — |
case-14 | pass→pass | 10,557 | 4,360 | -59% | 1 | 1 | 0% | 1,540 | 2,478 | +61% | 0 | 0 | — |
case-15 | fail→pass | 11,696 | 8,251 | -29% | 1 | 1 | 0% | 2,298 | 3,306 | +44% | 0 | 0 | — |
case-16 | pass→pass | 9,990 | 7,739 | -23% | 1 | 1 | 0% | 1,769 | 3,154 | +78% | 0 | 0 | — |
case-17 | fail→pass | 15,211 | 13,935 | -8% | 1 | 1 | 0% | 3,187 | 4,332 | +36% | 0 | 0 | — |
case-18 | pass→pass | 9,173 | 221,992 | +2320% | 1 | 1 | 0% | 1,253 | 2,130 | +70% | 0 | 0 | — |
case-19 | pass→pass | 7,654 | 3,844 | -50% | 1 | 1 | 0% | 1,069 | 2,168 | +103% | 0 | 0 | — |
case-20 | pass→pass | 12,115 | 17,211 | +42% | 1 | 1 | 0% | 2,238 | 4,459 | +99% | 0 | 0 | — |
case-21 | pass→pass | 27,370 | 36,094 | +32% | 1 | 1 | 0% | 5,722 | 9,878 | +73% | 0 | 0 | — |
case-22 | pass→pass | 9,540 | 12,874 | +35% | 1 | 1 | 0% | 1,991 | 3,743 | +88% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted, and 21 counted toward the lift figure. The other 1 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +36 percentage points is the difference between those two pass rates over the 21 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.