Figma Sensor
What it ingests
Design files, comments, and version history from the Figma REST API:
| Figma object | Ontology entity | Signal |
|---|---|---|
| Design file, FigJam board, Slides deck | CreativeWork (source_type: design_document) | file |
| Comment or reply | Comment | comment |
| File creator, last editor, version and comment authors | Person (identity) | — |
Each design file becomes one Design Document. Its creator, its last editor, and anyone who saved a version are linked to it with AUTHORED. Each comment becomes a Comment that its author AUTHORED. The comment is RELATED_TO its Design Document, and a reply is also RELATED_TO the comment it answers.
Comment threads are the decision signal. When a thread is resolved, the root comment is ingested again with the whole thread as its content and status: resolved, so the discussion that settled a design question is one readable record. Named versions ("Ready for dev", plus their descriptions) land in the Design Document's content.
People are matched by their Figma user id, which is stored as a figma:<id> identity key. Figma never returns another user's email address, so a Figma author links to an existing Person by display name the first time. After that the id keeps them on the same Person.
Read-only and poll-based against https://api.figma.com. The sensor never calls Figma's Tier 1 endpoints (the full document tree, nodes, or image renders). See Rate limits.
Credential
References a figma_access_token credential by id. The secret is a personal access token (figd_…), sent as X-Figma-Token. An OAuth access token is also accepted and sent as Authorization: Bearer. The sensor tells the two apart by the figd_ prefix.
curl -sS -X POST https://your-corveil-host/api/credentials \
-H "Authorization: Bearer <admin>" -H "Content-Type: application/json" \
-d '{"type":"figma_access_token","name":"acme-figma","secret":"figd_<token>"}'Store only the token value, not X-Figma-Token: … or Bearer ….
The token reads only what its user can see in Figma. Scopes narrow that further; they never widen it. Use a dedicated design-ops account that has access to the teams you want ingested, so the sensor keeps working when one person changes roles.
Required scopes
| Scope | Why |
|---|---|
file_metadata:read | File name, folder, creator, last editor, and link |
folders:read | List a team's folders (projects) and their files. A token created before August 2026 with the deprecated projects:read also works. |
file_comments:read | Comments. Optional if enabled_objects leaves comments out. |
file_versions:read | Version history. Optional if enabled_objects leaves versions out. |
file_content:read is not needed.
Config fields
Set at least one of team_ids, folder_ids, or file_keys.
| Key | Required | Notes |
|---|---|---|
team_ids | one of three | Comma-separated team IDs. Every folder the token can see in the team is polled, including nested folders (up to three levels, 200 folders). A team URL is accepted too. |
folder_ids | one of three | Comma-separated folder (project) IDs, polled with their subfolders, on their own or alongside whole teams. |
file_keys | one of three | Comma-separated file keys or Figma file URLs (/design/, /file/, /board/, /slides/, …). Use this for drafts, files shared with you from outside your teams, or an OAuth token. |
enabled_objects | no | Extras on top of the files themselves: comments, versions. Blank ingests both. files ingests the files alone. Unknown names are rejected at configure time. |
There is no default_entity_type. This source emits two primary types (CreativeWork and Comment), so a single override would silently mistype the other.
Provider-side setup
- Sign in to Figma as the account the sensor will read as.
- Open Settings → Security → Personal access tokens and choose Generate new token.
- Set an expiry. Figma requires one on new personal access tokens, so note the date and rotate the credential before it lapses.
- Grant the read scopes in the table above: File metadata, Folders (or Projects on an older token), Comments, and Version history, each set to read-only.
- Copy the token. Figma shows it only once.
- Find each team ID. Open the team in the Figma file browser; the ID is the number after
/team/in the URL (https://www.figma.com/files/team/1234567890123456789/Acme). For a single folder, open it and copy the number after/project/or/folder/.
Figma does not let a token list teams, so the team IDs have to come from the URL.
There is no admin-consent step. The token sees whatever its user sees.
Wire-up
POST /api/sensors
{
"sensor_type": "figma",
"name": "Acme Figma",
"config": {
"team_ids": "1234567890123456789",
"file_keys": "https://www.figma.com/design/AbC123xYz00/Checkout-Flow",
"enabled_objects": "comments, versions"
},
"credentials": { "access_token": "<figma_access_token credential id>" }
}Verify
Run Test connection. It reads the first configured team's folders (or the first folder's files, or the first file's metadata). A rejected token fails at save or test rather than on the first poll. So does a valid token that can't see the team you named.
Once it's polling, the per-poll summary log shows files_ingested, files_swept_for_comments, and files_tracked. A team with files but files_tracked=0 usually means the token's user isn't a member of that team.
How incremental sync works
- The cadence is the worker's poll interval. Each poll lists every folder's files and compares each file's
last_modifiedwith the value it last ingested. - Only changed files are re-read. A changed file costs three requests: its metadata, its versions newer than the last one ingested, and its comments. Unchanged files cost nothing beyond the listing.
- First sync is spread out. One poll ingests at most 25 changed files, oldest edit first. The rest follow on the next polls. A team of 500 files takes about 20 polls to sync the first time.
- Comments on unchanged files arrive through a sweep. Figma's
last_modifiedmoves on design edits, not on comments. So each poll also re-reads the comments of 10 unchanged files, round-robin. With N files, a comment on a file nobody is editing arrives within about N/10 polls. A comment on a file being edited arrives on the next poll. - The folder tree is cached for six hours. Changing
team_idsorfolder_idsre-walks it immediately. - An edited file lands a new event. The event id is
figma:file:<key>@<last_modified>; the Design Document's entity id staysfigma:file:<key>, so its properties refresh instead of duplicating. - A renamed file keeps its first name in the graph. The current name is on the entity's
titleproperty. - A file named "Untitled" gets its key appended (
Untitled (AbC123xYz00)), so each untitled file stays its own Design Document. - Deletes are invisible. A file that disappears from a complete listing stops being tracked, but its entities stay.
Rate limits
Figma rate-limits per user and per endpoint tier, and the budget is shared with that user's other Figma integrations. The sensor uses file metadata (Tier 3) and folders, comments, and versions (Tier 2). It never uses Tier 1, which View and Collab seats get only a handful of times a month.
| Seat | Tier 2 | Tier 3 |
|---|---|---|
| Full or Dev seat | 25–100 per minute, by plan | 50–150 per minute, by plan |
| View or Collab seat | up to 5 per minute | up to 10 per minute |
Requests are paced 2.5 seconds apart, under the lowest paid Full-seat budget. A 429 with a Retry-After of a minute or less is retried up to three times. A longer wait means the user's budget is exhausted, so the poll stops. Files it already finished keep their progress; the rest resume next poll. Use a Full or Dev seat for the sensor's account. A View seat works for a handful of files but syncs slowly.
Known limits
- Branches are not ingested. Only main files are listed.
- No canvas content. Frame names, text layers, and components need the Tier 1 document tree, which the sensor deliberately doesn't call.
- OAuth tokens can't list folders. Figma doesn't offer the folders endpoints to public OAuth apps, so an OAuth token only reads
file_keys. Corveil does not refresh an OAuth token; rotate the credential when it expires. - Comment edits are not re-read. Figma comments have no edit timestamp, so a comment's text is captured when it's created (and again when its thread resolves).
- Webhooks are not used. Figma's v2 webhooks need a public endpoint registered per team. Polling is the supported path.
- Reactions are counted, not ingested as entities.