Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Configure Figma API access across dev, staging, and production environments. Use when setting up per-environment tokens, managing multiple Figma files, or isolating development from production Figma resources. Trigger with phrases like "figma environments", "figma staging", "figma dev prod", "figma environment config".
.claude/skills/jeremylongshore-figma-multi-env-setup/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 28% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 154% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 11% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 19% | 0% |
| case-06 | ✗→✓ | ▲ Improved | -13% | 0% |
Configure separate Figma API credentials and file targets per environment. Use different PATs with minimal scopes, point to different Figma files, and prevent accidental production operations from dev.
| Environment | PAT Scopes | Figma File | Cache TTL | |-------------|-----------|------------|-----------| | Development | file_content:read | Copy of design file | 10s (fast iteration) | | Staging | file_content:read, file_comments:read | Staging branch/file | 60s | | Production | file_content:read, webhooks:write | Production design file | 300s |
typescript// src/config/figma.ts interface FigmaEnvConfig { token: string; fileKey: string; cacheTTL: number; webhookPasscode?: string; maxConcurrency: number; } function getFigmaConfig(): FigmaEnvConfig { const env = process.env.NODE_ENV || 'development'; const configs: Record<string, Partial<FigmaEnvConfig>> = { development: { token: process.env.FIGMA_PAT_DEV!, fileKey: process.env.FIGMA_FILE_KEY_DEV!, cacheTTL: 10_000, maxConcurrency: 1, }, staging: { token: process.env.FIGMA_PAT_STAGING!, fileKey: process.env.FIGMA_FILE_KEY_STAGING!, cacheTTL: 60_000, maxConcurrency: 3, }, production: { token: process.env.FIGMA_PAT_PROD!, fileKey: process.env.FIGMA_FILE_KEY_PROD!, cacheTTL: 300_000, maxConcurrency: 5, webhookPasscode: process.env.FIGMA_WEBHOOK_PASSCODE, }, }; const config = configs[env]; if (!config?.token) throw new Error(`Figma token not configured for env: ${env}`); if (!config?.fileKey) throw new Error(`Figma file key not configured for env: ${env}`); return config as FigmaEnvConfig; }
bash# .env.development FIGMA_PAT_DEV="figd_dev-token-read-only" FIGMA_FILE_KEY_DEV="devFileKey123" # .env.staging FIGMA_PAT_STAGING="figd_staging-token" FIGMA_FILE_KEY_STAGING="stagingFileKey456" # .env.production (stored in secret manager, not in repo) FIGMA_PAT_PROD="figd_prod-token" FIGMA_FILE_KEY_PROD="prodFileKey789" FIGMA_WEBHOOK_PASSCODE="webhook-secret" # .env.example (committed to repo as template) FIGMA_PAT_DEV= FIGMA_FILE_KEY_DEV=
bash# GitHub Actions -- use environment-scoped secrets gh secret set FIGMA_PAT_PROD --env production --body "figd_..." gh secret set FIGMA_PAT_STAGING --env staging --body "figd_..." # Google Cloud Secret Manager echo -n "figd_prod-token" | gcloud secrets create figma-pat-prod --data-file=- echo -n "figd_staging-token" | gcloud secrets create figma-pat-staging --data-file=- # Load in Cloud Run gcloud run deploy my-service \ --set-secrets="FIGMA_PAT_PROD=figma-pat-prod:latest"
typescript// Prevent production-specific operations in non-production function requireProduction(operation: string) { if (process.env.NODE_ENV !== 'production') { throw new Error( `${operation} is only allowed in production. ` + `Current env: ${process.env.NODE_ENV}` ); } } // Prevent destructive operations in production function blockInProduction(operation: string) { if (process.env.NODE_ENV === 'production') { throw new Error(`${operation} is blocked in production for safety`); } } // Usage async function createWebhook(config: any) { requireProduction('createWebhook'); // Only in prod return fetch('https://api.figma.com/v2/webhooks', { ... }); } async function deleteAllCachedData() { blockInProduction('deleteAllCachedData'); // Never in prod await cache.clear(); }
| Issue | Cause | Solution | |-------|-------|----------| | Wrong file in dev | Using prod file key | Verify FIGMA_FILE_KEY_DEV | | PAT expired in CI | 90-day expiry | Set rotation reminder per environment | | Staging webhook pointing to prod | Wrong endpoint URL | Verify webhook endpoint per env | | Config not loading | Missing NODE_ENV | Set NODE_ENV in deployment config |
Point the same integration at a staging file locally and the production file in CI using the Step 2 config loader:
bash# .env.development FIGMA_ENV=development FIGMA_FILE_KEY=stgAbC123fileKey FIGMA_WEBHOOK_ENDPOINT=https://dev.example.com/figma/webhook # CI (production) — secrets injected, never committed FIGMA_ENV=production FIGMA_FILE_KEY=prodXyZ789fileKey
Verify the guard refuses a cross-environment mistake before any API call is made:
bashFIGMA_ENV=production FIGMA_FILE_KEY=stgAbC123fileKey node sync-tokens.js # Error: production run pointed at a non-production file key — aborting
Environment strategy and per-env webhook registration (POST /v2/webhooks per environment): references/configuration-by-environment.md and references/environment-guards.md.
For observability setup, see figma-observability.
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 16,269 | 17,943 | +10% | 1 | 1 | 0% | 3,530 | 4,526 | +28% | 0 | 0 | — |
case-02 | fail→fail | 20,748 | 13,148 | -37% | 1 | 1 | 0% | 3,384 | 4,153 | +23% | 0 | 0 | — |
case-03 | fail→pass | 40,557 | 18,977 | -53% | 1 | 1 | 0% | 1,913 | 4,856 | +154% | 0 | 0 | — |
case-04 | fail→pass | 14,054 | 3,048 | -78% | 1 | 1 | 0% | 1,977 | 2,193 | +11% | 0 | 0 | — |
case-05 | fail→pass | 10,796 | 2,549 | -76% | 1 | 1 | 0% | 1,702 | 2,023 | +19% | 0 | 0 | — |
case-06 | fail→pass | 18,981 | 3,492 | -82% | 1 | 1 | 0% | 2,593 | 2,254 | -13% | 0 | 0 | — |
case-07 | fail→pass | 10,428 | 8,260 | -21% | 1 | 1 | 0% | 1,743 | 2,768 | +59% | 0 | 0 | — |
case-08 | fail→pass | 10,674 | 5,025 | -53% | 1 | 1 | 0% | 1,814 | 2,560 | +41% | 0 | 0 | — |
case-09 | pass→pass | 10,109 | 4,135 | -59% | 1 | 1 | 0% | 1,843 | 2,198 | +19% | 0 | 0 | — |
case-10 | fail→pass | 7,443 | 3,059 | -59% | 1 | 1 | 0% | 1,402 | 2,070 | +48% | 0 | 0 | — |
case-11 | fail→pass | 9,200 | 2,041 | -78% | 1 | 1 | 0% | 1,308 | 2,056 | +57% | 0 | 0 | — |
case-12 | fail→pass | 13,283 | 5,262 | -60% | 1 | 1 | 0% | 2,502 | 2,640 | +6% | 0 | 0 | — |
case-13 | fail→pass | 8,538 | 922,624 | +10706% | 1 | 1 | 0% | 1,211 | 2,124 | +75% | 0 | 0 | — |
case-14 | fail→pass | 916,439 | 4,564 | -100% | 1 | 1 | 0% | 1,652 | 2,388 | +45% | 0 | 0 | — |
case-15 | fail→pass | 7,290 | 716,908 | +9734% | 1 | 1 | 0% | 1,359 | 2,492 | +83% | 0 | 0 | — |
case-16 | fail→pass | 9,912 | 1,944 | -80% | 1 | 1 | 0% | 2,005 | 1,989 | -1% | 0 | 0 | — |
case-17 | fail→pass | 10,347 | 2,401 | -77% | 1 | 1 | 0% | 1,944 | 2,068 | +6% | 0 | 0 | — |
case-18 | fail→pass | 11,165 | 6,338 | -43% | 1 | 1 | 0% | 1,996 | 2,708 | +36% | 0 | 0 | — |
case-19 | fail→pass | 10,108 | 3,531 | -65% | 1 | 1 | 0% | 1,791 | 2,252 | +26% | 0 | 0 | — |
case-20 | fail→fail | 12,495 | 14,586 | +17% | 1 | 1 | 0% | 1,952 | 3,777 | +93% | 0 | 0 | — |
case-21 | pass→pass | 11,542 | 7,892 | -32% | 1 | 1 | 0% | 2,062 | 2,970 | +44% | 0 | 0 | — |
case-22 | pass→pass | 20,152 | 23,409 | +16% | 1 | 1 | 0% | 3,723 | 6,500 | +75% | 0 | 0 | — |
case-23 | pass→pass | 18,936 | 18,583 | -2% | 1 | 1 | 0% | 3,406 | 5,386 | +58% | 0 | 0 | — |
case-24 | pass→pass | 15,657 | 21,025 | +34% | 1 | 1 | 0% | 2,807 | 5,470 | +95% | 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. 24 cases were attempted, and 23 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 +71 percentage points is the difference between those two pass rates over the 23 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.