Figma Data Handling
Overview
Work with Figma's data APIs: comments, version history, and user information. Handle sensitive data correctly with redaction and privacy compliance.
Prerequisites
FIGMA_PAT with appropriate scopes (file_comments:read/write, file_versions:read)
- Understanding of GDPR/CCPA basics
Instructions
Step 1: Comments API
const PAT = process.env.FIGMA_PAT!;
const FILE_KEY = process.env.FIGMA_FILE_KEY!;
async function getComments(fileKey: string) {
const res = await fetch(
`https://api.figma.com/v1/files/${fileKey}/comments`,
{ headers: { 'X-Figma-Token': PAT } }
);
const data = await res.json();
return data.comments;
}
async function getCommentsAsMarkdown(fileKey: string) {
const res = await fetch(
`https://api.figma.com/v1/files/${fileKey}/comments?as_md=true`,
{ headers: { 'X-Figma-Token': PAT } }
);
return (await res.json()).comments;
}
async function postComment(fileKey: string, message: string, nodeId?: string) {
const body: any = { message };
if (nodeId) {
body.client_meta = { node_id: nodeId };
}
const res = await fetch(
`https://api.figma.com/v1/files/${fileKey}/comments`,
{
method: 'POST',
headers: {
'X-Figma-Token': PAT,
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
}
);
return res.json();
}
async function reactToComment(fileKey: string, commentId: string, emoji: string) {
return fetch(
`https://api.figma.com/v1/files/${fileKey}/comments/${commentId}/reactions`,
{
method: 'POST',
headers: {
'X-Figma-Token': PAT,
'Content-Type': 'application/json',
},
body: JSON.stringify({ emoji }),
}
).then(r => r.json());
}
Step 2: Version History API
async function getVersionHistory(fileKey: string) {
const res = await fetch(
`https://api.figma.com/v1/files/${fileKey}/versions`,
{ headers: { 'X-Figma-Token': PAT } }
);
const data = await res.json();
return data.versions;
}
async function getAllVersions(fileKey: string) {
const versions: any[] = [];
let url: string | null = `https://api.figma.com/v1/files/${fileKey}/versions`;
while (url) {
const res = await fetch(url, { headers: { 'X-Figma-Token': PAT } });
const data = await res.json();
versions.push(...data.);
url = data.?.
?
: ;
}
versions;
}
Step 3: User Data and Privacy
interface FigmaUser {
id: string;
handle: string;
img_url: string;
email: string;
}
function redactFigmaUser(user: FigmaUser): Omit<FigmaUser, 'email'> & { email: string } {
return {
...user,
email: '[REDACTED]',
img_url: '[REDACTED]',
};
}
interface DataClassification {
field: string;
sensitivity: 'public' | 'internal' | 'pii';
handling: string;
}
const figmaDataClassification: DataClassification[] = [
{ field: 'user.email', sensitivity: 'pii', handling: 'Encrypt at rest, redact in logs' },
{ field: 'user.handle', sensitivity: , : },
{ : , : , : },
{ : , : , : },
{ : , : , : },
{ : , : , : },
];
Step 4: Data Retention
interface CachedFigmaData {
data: any;
fetchedAt: Date;
expiresAt: Date;
}
function createCacheEntry(data: any, ttlMs: number): CachedFigmaData {
const now = new Date();
return {
data,
fetchedAt: now,
expiresAt: new Date(now.getTime() + ttlMs),
};
}
async function cleanupExpiredData(db: any) {
const now = new Date();
const deleted = await db.figmaCache.deleteMany({
expiresAt: { $lt: now },
});
console.log(`Cleaned up ${deleted.count} expired Figma cache entries`);
}
Step 5: Safe Logging
const REDACT_FIELDS = ['email', 'img_url', 'access_token', 'refresh_token'];
function safeFigmaLog(label: string, data: any) {
const safe = JSON.parse(JSON.stringify(data));
function redact(obj: any) {
for (const key of Object.keys(obj)) {
if (REDACT_FIELDS.includes(key)) {
obj[key] = '[REDACTED]';
} else if (typeof obj[key] === 'object' && obj[key] !== null) {
redact(obj[key]);
}
}
}
redact(safe);
console.log(`[figma] ${label}:`, JSON.stringify(safe));
}
Output
- Comments fetched and posted via REST API
- Version history retrieved with pagination
- PII redacted before logging and storage
- Data retention policies applied
Error Handling
| Error | Cause | Solution |
|---|
| 403 on comments | Missing file_comments:read scope | Regenerate PAT with scope |
| Empty version history | New file with no saved versions | Create a named version in Figma first |
| PII in logs | Missing redaction | Apply safeFigmaLog wrapper |
| Stale image URLs | URLs older than 30 days | Re-export images; do not cache URLs long-term |
Examples
Pull the latest comments as Markdown and post a reaction (Step 1 Comments API):
curl -s -H "X-Figma-Token: ${FIGMA_PAT}" \
"https://api.figma.com/v1/files/${FIGMA_FILE_KEY}/comments?as_md=true" \
| jq -r '.comments[0] | "\(.user.handle): \(.message)"'
Walk version history with pagination (Step 2):
curl -s -H "X-Figma-Token: ${FIGMA_PAT}" \
"https://api.figma.com/v1/files/${FIGMA_FILE_KEY}/versions" \
| jq '{versions: [.versions[] | {id, created_at, label}], next: .pagination.next_page}'
PII rules for what you may persist from these payloads (user handles, avatars, emails): references/user-data-and-privacy.md and references/safe-logging.md.
Resources
Next Steps
For enterprise access control, see figma-enterprise-rbac.