@rolling-design-sync/agent-cli 0.0.0-stage → 0.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1243 @@
1
+ {
2
+ "$comment": "Generated by scripts/gen-commands.js from the API's OpenAPI document (x-rds-cli operations). Do not edit; run `npm run gen` in scripts/.",
3
+ "api": "3.0.0",
4
+ "commands": [
5
+ {
6
+ "command": "files ls",
7
+ "summary": "List tracked files",
8
+ "description": "Every file the plugin has synced, most recently synced first, with node counts (total, dirty, clean, blocked) and the last sync time. Use it to find a `fileKey` when you have no Figma link; with a link, pass it as `url` (or use `resolve_url`).",
9
+ "method": "GET",
10
+ "path": "/v3/files",
11
+ "flags": []
12
+ },
13
+ {
14
+ "command": "files show",
15
+ "summary": "Get a file",
16
+ "description": "One tracked file's node counts and last sync time: a quick check of how much is dirty. For counts by category, type or frame use `summarize`. `file_not_found`: the file was never synced — take the key from `list_files`, or pass the Figma link as `url` with fileKey '-'.",
17
+ "method": "GET",
18
+ "path": "/v3/files/{fileKey}",
19
+ "flags": [
20
+ {
21
+ "flag": "file-key",
22
+ "in": "path",
23
+ "name": "fileKey",
24
+ "type": "string",
25
+ "required": true,
26
+ "description": "The Figma file key, e.g. `AbCdEf123456` from figma.com/design/<fileKey>/… — not a URL. Pass '-' to take it from `url` instead."
27
+ },
28
+ {
29
+ "flag": "url",
30
+ "in": "query",
31
+ "name": "url",
32
+ "type": "string",
33
+ "description": "A Figma link (figma.com/design/<fileKey>/…?node-id=1-2). Supplies the path parameters given as '-'; on routes with `under`, its node-id is the default `under`."
34
+ }
35
+ ]
36
+ },
37
+ {
38
+ "command": "scopes ls",
39
+ "summary": "List synced scopes",
40
+ "description": "The scopes (sections/frames) the plugin has synced in a file: each root's name, last sync, and current counts. Only nodes under these roots exist in the API; a `node_not_found` usually means the node is outside every scope and must be synced from the plugin first.",
41
+ "method": "GET",
42
+ "path": "/v3/files/{fileKey}/scopes",
43
+ "flags": [
44
+ {
45
+ "flag": "file-key",
46
+ "in": "path",
47
+ "name": "fileKey",
48
+ "type": "string",
49
+ "required": true,
50
+ "description": "The Figma file key, e.g. `AbCdEf123456` from figma.com/design/<fileKey>/… — not a URL. Pass '-' to take it from `url` instead."
51
+ },
52
+ {
53
+ "flag": "url",
54
+ "in": "query",
55
+ "name": "url",
56
+ "type": "string",
57
+ "description": "A Figma link (figma.com/design/<fileKey>/…?node-id=1-2). Supplies the path parameters given as '-'; on routes with `under`, its node-id is the default `under`."
58
+ }
59
+ ]
60
+ },
61
+ {
62
+ "command": "nodes ls",
63
+ "summary": "List nodes",
64
+ "description": "Nodes of a file with their property-level changes — by default the dirty ones (design changed since last built), compact, in document order (the design's layer order), 100 per page. The main read for implementation work: filter by subtree (`under` — one node id or several comma-separated — or a `url` with node-id), type, change, category, component or name; values within a parameter are OR, parameters are AND. Each node's `changes` is `{ prop: { built, now } }` and `contentHash` is what `clear_nodes` needs. For counts only use `summarize`; to work component by component use `list_work_items`. Every node carries `changedProps` and, for instances, `isInstance` / `mainComponentId` (`instance=true` lists only instances). By default (`changes=auto`) `added` nodes have no `changes` — on a fresh sync every node is added, so pages stay small; get their values with `changes=diff`, `view=full` or `get_node`. Change values over 1 KB are replaced by `{ truncated: true, bytes }` and the page has `truncated: true`; `view=full` or `get_node` returns them whole. `expand=instances` returns everything a screen uses in one call: with `under` = the screen, it adds the main components its instances point at (and theirs, transitively), each once; filters apply to that union (`expand=instances&state=dirty` = every dirty node the screen uses). Scope nodes come first, then each component in discovery order; expanded nodes carry `via` (the instance that reached them), and `expansion` reports the count, `truncated` and `unresolved` (unsynced) components. Page with `nextCursor` → `cursor`. On `unknown_parameter` / `invalid_parameter` follow `hint`; on `invalid_cursor` keep every other parameter unchanged or drop `cursor`.",
65
+ "method": "GET",
66
+ "path": "/v3/files/{fileKey}/nodes",
67
+ "flags": [
68
+ {
69
+ "flag": "file-key",
70
+ "in": "path",
71
+ "name": "fileKey",
72
+ "type": "string",
73
+ "required": true,
74
+ "description": "The Figma file key, e.g. `AbCdEf123456` from figma.com/design/<fileKey>/… — not a URL. Pass '-' to take it from `url` instead."
75
+ },
76
+ {
77
+ "flag": "state",
78
+ "in": "query",
79
+ "name": "state",
80
+ "type": "string",
81
+ "description": "Comma-separated: dirty, clean, blocked, all. Default dirty (changed since last built, not blocked); `all` for every state."
82
+ },
83
+ {
84
+ "flag": "under",
85
+ "in": "query",
86
+ "name": "under",
87
+ "type": "string",
88
+ "description": "Only this node's subtree, the node included (1:2 or 1-2). Several subtrees: up to 50 comma-separated ids (1:2,1:5) for their union, each node once, in the order given. Defaults to the `url` link's node-id."
89
+ },
90
+ {
91
+ "flag": "depth",
92
+ "in": "query",
93
+ "name": "depth",
94
+ "type": "integer",
95
+ "minimum": 0,
96
+ "description": "Needs `under`: at most this many levels below it — below each root when there are several (1 = direct children)."
97
+ },
98
+ {
99
+ "flag": "type",
100
+ "in": "query",
101
+ "name": "type",
102
+ "type": "string",
103
+ "description": "Comma-separated Figma node types, e.g. TEXT,INSTANCE,FRAME."
104
+ },
105
+ {
106
+ "flag": "change-type",
107
+ "in": "query",
108
+ "name": "changeType",
109
+ "type": "string",
110
+ "description": "Comma-separated: added, modified, deleted (dirty nodes only)."
111
+ },
112
+ {
113
+ "flag": "changed",
114
+ "in": "query",
115
+ "name": "changed",
116
+ "type": "string",
117
+ "description": "Comma-separated changed property names (fills, paddingLeft); a trailing `*` matches a prefix (padding*)."
118
+ },
119
+ {
120
+ "flag": "category",
121
+ "in": "query",
122
+ "name": "category",
123
+ "type": "string",
124
+ "description": "Comma-separated change categories: layout, color, typography, effects, component, content, structure, prototype, other."
125
+ },
126
+ {
127
+ "flag": "component",
128
+ "in": "query",
129
+ "name": "component",
130
+ "type": "string",
131
+ "description": "Comma-separated main component ids: the components and all their instances."
132
+ },
133
+ {
134
+ "flag": "name",
135
+ "in": "query",
136
+ "name": "name",
137
+ "type": "string",
138
+ "description": "Comma-separated case-insensitive name globs (`*`, `?`), e.g. Button*."
139
+ },
140
+ {
141
+ "flag": "since",
142
+ "in": "query",
143
+ "name": "since",
144
+ "type": "string",
145
+ "description": "ISO 8601 time: only nodes changed at or after it."
146
+ },
147
+ {
148
+ "flag": "claimed",
149
+ "in": "query",
150
+ "name": "claimed",
151
+ "type": "string",
152
+ "description": "Comma-separated: true (leased), false (free), mine (leased to `worker`)."
153
+ },
154
+ {
155
+ "flag": "worker",
156
+ "in": "query",
157
+ "name": "worker",
158
+ "type": "string",
159
+ "description": "Your worker id; needed with `claimed=mine`."
160
+ },
161
+ {
162
+ "flag": "instance",
163
+ "in": "query",
164
+ "name": "instance",
165
+ "type": "string",
166
+ "enum": [
167
+ "true",
168
+ "false"
169
+ ],
170
+ "description": "true: only component instances (`isInstance`); false: only other nodes."
171
+ },
172
+ {
173
+ "flag": "q",
174
+ "in": "query",
175
+ "name": "q",
176
+ "type": "string",
177
+ "description": "All the filters above in one string: space-separated `key:value` pairs, e.g. `state:dirty under:1:2 changed:fills` (several subtrees: `under:1:2,1:5`); quote values with spaces (name:\"Primary Button\"). A key may not also be passed as its own parameter."
178
+ },
179
+ {
180
+ "flag": "view",
181
+ "in": "query",
182
+ "name": "view",
183
+ "type": "string",
184
+ "enum": [
185
+ "compact",
186
+ "full"
187
+ ],
188
+ "description": "compact (default: state, changes, hash, claim, block) or full (adds attrs, implementedAttrs, path; change values never truncated)."
189
+ },
190
+ {
191
+ "flag": "changes",
192
+ "in": "query",
193
+ "name": "changes",
194
+ "type": "string",
195
+ "enum": [
196
+ "auto",
197
+ "diff",
198
+ "names"
199
+ ],
200
+ "description": "auto (default): the built→now diff for modified and deleted nodes, omitted for added ones (their property names are in `changedProps`) — keeps fresh-sync pages small; diff: the diff for every node, including added ones' full values; names: never `changes`, only `changedProps`. Ask for `changes=diff` (or `get_node`) when you need the values of added nodes."
201
+ },
202
+ {
203
+ "flag": "fields",
204
+ "in": "query",
205
+ "name": "fields",
206
+ "type": "string",
207
+ "description": "Comma-separated node fields to return (overrides view): nodeId, name, type, parentId, isInstance, mainComponentId, component, state, changeType, changedProps, changes, changesKnown, contentHash, figmaUrl, claim, blocked, attrs, implementedAttrs, path, deleted, history, descendants, via (`via` needs `expand=instances`)."
208
+ },
209
+ {
210
+ "flag": "include",
211
+ "in": "query",
212
+ "name": "include",
213
+ "type": "string",
214
+ "enum": [
215
+ "counts"
216
+ ],
217
+ "description": "`counts` adds subtree counts (`descendants`) to each node."
218
+ },
219
+ {
220
+ "flag": "sort",
221
+ "in": "query",
222
+ "name": "sort",
223
+ "type": "string",
224
+ "enum": [
225
+ "document",
226
+ "path",
227
+ "updatedAt",
228
+ "name",
229
+ "-document",
230
+ "-path",
231
+ "-updatedAt",
232
+ "-name"
233
+ ],
234
+ "description": "document (default: the design's layer order — auto-layout flow order, else top-to-bottom, left-to-right), path (node-id path order), updatedAt or name; prefix `-` for descending."
235
+ },
236
+ {
237
+ "flag": "limit",
238
+ "in": "query",
239
+ "name": "limit",
240
+ "type": "integer",
241
+ "minimum": 1,
242
+ "maximum": 1000,
243
+ "description": "Page size (default 100, max 1000). With format=outline: rendered lines per page (default: every line)."
244
+ },
245
+ {
246
+ "flag": "cursor",
247
+ "in": "query",
248
+ "name": "cursor",
249
+ "type": "string",
250
+ "description": "The previous page's `nextCursor`, unchanged, with all other parameters the same (else `invalid_cursor`). With format=outline: the previous response's `X-RDS-Next-Cursor` header (a line position in the rendered outline)."
251
+ },
252
+ {
253
+ "flag": "url",
254
+ "in": "query",
255
+ "name": "url",
256
+ "type": "string",
257
+ "description": "A Figma link (figma.com/design/<fileKey>/…?node-id=1-2). Supplies the path parameters given as '-'; on routes with `under`, its node-id is the default `under`."
258
+ },
259
+ {
260
+ "flag": "expand",
261
+ "in": "query",
262
+ "name": "expand",
263
+ "type": "string",
264
+ "enum": [
265
+ "instances"
266
+ ],
267
+ "description": "`instances`: also include the subtree of every main component the scope's instances use (the scope is `under`, or the whole file) — transitively through instances inside those components, breadth-first, each component once. Use it for \"everything this screen uses\" / \"which components do I build for this screen\". Other filters apply after expansion; `depth` cannot be combined with it."
268
+ },
269
+ {
270
+ "flag": "expand-depth",
271
+ "in": "query",
272
+ "name": "expandDepth",
273
+ "type": "integer",
274
+ "minimum": 1,
275
+ "maximum": 20,
276
+ "description": "With `expand=instances`: at most this many instance → main hops (default 10, max 20; 1 = only the components the scope uses directly)."
277
+ },
278
+ {
279
+ "flag": "format",
280
+ "in": "query",
281
+ "name": "format",
282
+ "type": "string",
283
+ "enum": [
284
+ "json",
285
+ "ndjson",
286
+ "md",
287
+ "outline"
288
+ ],
289
+ "description": "json (default); ndjson — one JSON object per line, streamed; md — a markdown checklist, one line per row, streamed. ndjson and md return every match from `cursor` on (up to `limit` when given) instead of one page. outline — text/plain, an indented tree with one line per node: use this first to see a screen's structure (under=<the screen>, and expand=instances for the components inside). Line: `Name (TYPE) · added` — `clean`, `added`, `deleted`, or `modified (3: fills, paddingLeft, …)` (changed property count and the first names), `· blocked` when blocked; an instance shows `(INSTANCE → <mainComponentId> <main name>)`. Two spaces per level of the tree below `under` (the `under` node itself, when it matches, is at the left margin; with several `under` ids, the tree of each root follows that of the previous root, in the order given, each root at the left margin). A node whose ancestors the filters leave out is indented under its nearest shown ancestor (at the margin when none is shown) — no placeholder lines. With expand=instances, a component's subtree is nested, as `↳ Name (COMPONENT) · …`, under the first instance that reached it; other instances of it show `→ <mainComponentId> (shown above)` (or `(shown below)`). Always in document order (the design's layer order; `sort=path` for node-id path order); nodes positioned absolutely in an auto-layout parent are marked `(absolute)`. Lines are the `fields`-independent summary. The whole result (up to 2000 rows, in order) is rendered once, then paged by rendered lines, so nesting and `(shown above)` read the same at any page size: `limit` is lines per page (default: every line), and when lines remain the response has an `X-RDS-Next-Cursor` header — pass it as `cursor`, with every other parameter unchanged, for the next lines. Past the 2000-row cap the outline's last line (counted like any other) is `… N more (use cursor/limit) — cursor=<c>` (the same cursor is in the header), which continues after the rendered part."
290
+ }
291
+ ],
292
+ "paging": {
293
+ "param": "cursor",
294
+ "next": "nextCursor",
295
+ "items": "nodes"
296
+ }
297
+ },
298
+ {
299
+ "command": "nodes show",
300
+ "summary": "Get a node",
301
+ "description": "One node in full: state, property changes (whole values, never truncated), component link (`isInstance`, `mainComponentId`), current `contentHash`, claim, block, subtree counts and history (when and in which commit it was last built). Use it to re-read a node before clearing it — e.g. after `clear_nodes` reports it `stale`. `node_not_found`: check the id against the link's node-id and that it is under a synced scope (`list_scopes`).",
302
+ "method": "GET",
303
+ "path": "/v3/files/{fileKey}/nodes/{nodeId}",
304
+ "positionals": [
305
+ {
306
+ "name": "nodeId",
307
+ "description": "The node id, 1:2 or 1-2 (a link's node-id). Pass '-' to take it from `url`."
308
+ }
309
+ ],
310
+ "flags": [
311
+ {
312
+ "flag": "file-key",
313
+ "in": "path",
314
+ "name": "fileKey",
315
+ "type": "string",
316
+ "required": true,
317
+ "description": "The Figma file key, e.g. `AbCdEf123456` from figma.com/design/<fileKey>/… — not a URL. Pass '-' to take it from `url` instead."
318
+ },
319
+ {
320
+ "flag": "fields",
321
+ "in": "query",
322
+ "name": "fields",
323
+ "type": "string",
324
+ "description": "Comma-separated node fields to return (default: all): nodeId, name, type, parentId, isInstance, mainComponentId, component, state, changeType, changedProps, changes, changesKnown, contentHash, figmaUrl, claim, blocked, attrs, implementedAttrs, path, deleted, history, descendants."
325
+ },
326
+ {
327
+ "flag": "changes",
328
+ "in": "query",
329
+ "name": "changes",
330
+ "type": "string",
331
+ "enum": [
332
+ "auto",
333
+ "diff",
334
+ "names"
335
+ ],
336
+ "description": "diff (default): the whole built→now diff, never truncated — the way to read an added node's values; auto: omitted for added nodes; names: never `changes`, only `changedProps`."
337
+ },
338
+ {
339
+ "flag": "url",
340
+ "in": "query",
341
+ "name": "url",
342
+ "type": "string",
343
+ "description": "A Figma link (figma.com/design/<fileKey>/…?node-id=1-2). Supplies the path parameters given as '-'; on routes with `under`, its node-id is the default `under`."
344
+ }
345
+ ]
346
+ },
347
+ {
348
+ "command": "resolve",
349
+ "summary": "Resolve a Figma link",
350
+ "description": "Turns a Figma link into `{ fileKey, nodeId }` (nodeId as 1:2; `null` when the link has no node-id). Needs no tracked file. Most calls also take the link directly as `url` with '-' path parameters, so use this only when you need the ids themselves. `invalid_url`: pass a figma.com/design/… link, URL-encoded.",
351
+ "method": "GET",
352
+ "path": "/v3/resolve",
353
+ "flags": [
354
+ {
355
+ "flag": "url",
356
+ "in": "query",
357
+ "name": "url",
358
+ "type": "string",
359
+ "required": true,
360
+ "description": "A Figma design link, e.g. https://www.figma.com/design/<fileKey>/<name>?node-id=1-2."
361
+ }
362
+ ]
363
+ },
364
+ {
365
+ "command": "claims ls",
366
+ "summary": "List file claims",
367
+ "description": "Active (unexpired) claims, soonest expiry first. `nodeCount` counts nodes still leased to each claim; `nodeIds` contains at most 500 of them in node-id order; `nodes` names the first 3 in document order. Cleared, released, expired or reassigned nodes are excluded. Use this to see agents at work, then list_nodes with claimed=mine and worker to inspect a worker's nodes.",
368
+ "method": "GET",
369
+ "path": "/v3/files/{fileKey}/claims",
370
+ "flags": [
371
+ {
372
+ "flag": "file-key",
373
+ "in": "path",
374
+ "name": "fileKey",
375
+ "type": "string",
376
+ "required": true,
377
+ "description": "The Figma file key, e.g. `AbCdEf123456` from figma.com/design/<fileKey>/… — not a URL. Pass '-' to take it from `url` instead."
378
+ },
379
+ {
380
+ "flag": "url",
381
+ "in": "query",
382
+ "name": "url",
383
+ "type": "string",
384
+ "description": "A Figma link (figma.com/design/<fileKey>/…?node-id=1-2). Supplies the path parameters given as '-'; on routes with `under`, its node-id is the default `under`."
385
+ }
386
+ ]
387
+ },
388
+ {
389
+ "command": "claim",
390
+ "summary": "Claim nodes",
391
+ "description": "Leases nodes to `worker` so parallel agents do not implement the same nodes. Claim before working when other agents may share the file; alone, you can skip claims. Select by `nodeIds` or by `filter` (not both; neither = the dirty nodes, up to `limit`). Only free, expired or already-yours nodes are taken, and the response lists exactly those — work on those only; an empty `nodes` means nothing free matched. The lease ends at `expiresAt`: `renew_claim` before then for long work; `clear_nodes` releases cleared nodes, `release_claim` the rest.",
392
+ "method": "POST",
393
+ "path": "/v3/files/{fileKey}/claims",
394
+ "flags": [
395
+ {
396
+ "flag": "file-key",
397
+ "in": "path",
398
+ "name": "fileKey",
399
+ "type": "string",
400
+ "required": true,
401
+ "description": "The Figma file key, e.g. `AbCdEf123456` from figma.com/design/<fileKey>/… — not a URL. Pass '-' to take it from `url` instead."
402
+ },
403
+ {
404
+ "flag": "url",
405
+ "in": "query",
406
+ "name": "url",
407
+ "type": "string",
408
+ "description": "A Figma link (figma.com/design/<fileKey>/…?node-id=1-2). Supplies the path parameters given as '-'; on routes with `under`, its node-id is the default `under`."
409
+ },
410
+ {
411
+ "flag": "worker",
412
+ "in": "body",
413
+ "name": "worker",
414
+ "type": "string",
415
+ "required": true,
416
+ "description": "A stable id for your agent session; reuse it on every call."
417
+ },
418
+ {
419
+ "flag": "label",
420
+ "in": "body",
421
+ "name": "label",
422
+ "type": "string",
423
+ "description": "A short human-readable name for the work, shown in logs and UIs."
424
+ },
425
+ {
426
+ "flag": "ttl-seconds",
427
+ "in": "body",
428
+ "name": "ttlSeconds",
429
+ "type": "integer",
430
+ "minimum": 1,
431
+ "description": "Lease length in seconds (default: the server's RDS_CLAIM_TTL_SECONDS; capped at 86400 = 24 h)."
432
+ },
433
+ {
434
+ "flag": "limit",
435
+ "in": "body",
436
+ "name": "limit",
437
+ "type": "integer",
438
+ "minimum": 1,
439
+ "maximum": 1000,
440
+ "description": "At most this many nodes (default 100 with a filter; every listed id with nodeIds)."
441
+ },
442
+ {
443
+ "flag": "node-ids",
444
+ "in": "body",
445
+ "name": "nodeIds",
446
+ "type": "array",
447
+ "items": "string",
448
+ "description": "Exact node ids to claim (1:2 or 1-2); unknown ids are skipped."
449
+ },
450
+ {
451
+ "flag": "q",
452
+ "in": "body",
453
+ "name": "q",
454
+ "object": "filter",
455
+ "type": "string",
456
+ "description": "All the filters above in one string: space-separated `key:value` pairs, e.g. `state:dirty under:1:2 changed:fills` (several subtrees: `under:1:2,1:5`); quote values with spaces (name:\"Primary Button\"). A key may not also be passed as its own parameter."
457
+ },
458
+ {
459
+ "flag": "state",
460
+ "in": "body",
461
+ "name": "state",
462
+ "object": "filter",
463
+ "type": "string",
464
+ "description": "Comma-separated: dirty, clean, blocked, all. Default dirty (changed since last built, not blocked); `all` for every state."
465
+ },
466
+ {
467
+ "flag": "under",
468
+ "in": "body",
469
+ "name": "under",
470
+ "object": "filter",
471
+ "type": "string",
472
+ "description": "Only this node's subtree, the node included (1:2 or 1-2). Several subtrees: up to 50 comma-separated ids (1:2,1:5) for their union, each node once, in the order given. Defaults to the `url` link's node-id."
473
+ },
474
+ {
475
+ "flag": "depth",
476
+ "in": "body",
477
+ "name": "depth",
478
+ "object": "filter",
479
+ "type": "integer",
480
+ "minimum": 0,
481
+ "description": "Needs `under`: at most this many levels below it — below each root when there are several (1 = direct children)."
482
+ },
483
+ {
484
+ "flag": "type",
485
+ "in": "body",
486
+ "name": "type",
487
+ "object": "filter",
488
+ "type": "string",
489
+ "description": "Comma-separated Figma node types, e.g. TEXT,INSTANCE,FRAME."
490
+ },
491
+ {
492
+ "flag": "change-type",
493
+ "in": "body",
494
+ "name": "changeType",
495
+ "object": "filter",
496
+ "type": "string",
497
+ "description": "Comma-separated: added, modified, deleted (dirty nodes only)."
498
+ },
499
+ {
500
+ "flag": "changed",
501
+ "in": "body",
502
+ "name": "changed",
503
+ "object": "filter",
504
+ "type": "string",
505
+ "description": "Comma-separated changed property names (fills, paddingLeft); a trailing `*` matches a prefix (padding*)."
506
+ },
507
+ {
508
+ "flag": "category",
509
+ "in": "body",
510
+ "name": "category",
511
+ "object": "filter",
512
+ "type": "string",
513
+ "description": "Comma-separated change categories: layout, color, typography, effects, component, content, structure, prototype, other."
514
+ },
515
+ {
516
+ "flag": "component",
517
+ "in": "body",
518
+ "name": "component",
519
+ "object": "filter",
520
+ "type": "string",
521
+ "description": "Comma-separated main component ids: the components and all their instances."
522
+ },
523
+ {
524
+ "flag": "instance",
525
+ "in": "body",
526
+ "name": "instance",
527
+ "object": "filter",
528
+ "type": "string",
529
+ "enum": [
530
+ "true",
531
+ "false"
532
+ ],
533
+ "description": "true: only component instances (`isInstance`); false: only other nodes."
534
+ },
535
+ {
536
+ "flag": "name",
537
+ "in": "body",
538
+ "name": "name",
539
+ "object": "filter",
540
+ "type": "string",
541
+ "description": "Comma-separated case-insensitive name globs (`*`, `?`), e.g. Button*."
542
+ },
543
+ {
544
+ "flag": "since",
545
+ "in": "body",
546
+ "name": "since",
547
+ "object": "filter",
548
+ "type": "string",
549
+ "description": "ISO 8601 time: only nodes changed at or after it."
550
+ }
551
+ ],
552
+ "body": true
553
+ },
554
+ {
555
+ "command": "clear",
556
+ "summary": "Clear implemented nodes",
557
+ "description": "Records nodes as implemented (up to 500 per call) once your code matches the design: they become clean, their current design becomes the new baseline, and their leases are released. Send each node with the `contentHash` you implemented (from `list_nodes` / `get_node`). Each item is checked on its own and the call is 200 even when some fail: `cleared`; `stale` — the design changed since you read it, so re-read that node with `get_node`, implement the new changes and clear it with the returned `contentHash`; `not_found` — a wrong id. Clear only what you actually built; never clear to hide changes (use `block_node` for nodes you cannot implement). The `nodes.cleared` event's actor is the body's `worker` when given, `portal:<label>` on a session-authenticated call, else the token label.",
558
+ "method": "POST",
559
+ "path": "/v3/files/{fileKey}/clear",
560
+ "items": {
561
+ "name": "items",
562
+ "keys": [
563
+ "nodeId",
564
+ "contentHash"
565
+ ],
566
+ "minItems": 1,
567
+ "maxItems": 500,
568
+ "required": true,
569
+ "description": "The nodes to clear (1–500), each with the `contentHash` you implemented."
570
+ },
571
+ "flags": [
572
+ {
573
+ "flag": "file-key",
574
+ "in": "path",
575
+ "name": "fileKey",
576
+ "type": "string",
577
+ "required": true,
578
+ "description": "The Figma file key, e.g. `AbCdEf123456` from figma.com/design/<fileKey>/… — not a URL. Pass '-' to take it from `url` instead."
579
+ },
580
+ {
581
+ "flag": "url",
582
+ "in": "query",
583
+ "name": "url",
584
+ "type": "string",
585
+ "description": "A Figma link (figma.com/design/<fileKey>/…?node-id=1-2). Supplies the path parameters given as '-'; on routes with `under`, its node-id is the default `under`."
586
+ },
587
+ {
588
+ "flag": "commit-ref",
589
+ "in": "body",
590
+ "name": "commitRef",
591
+ "type": "string",
592
+ "description": "The commit (SHA or ref) that implements the nodes; shown in node history."
593
+ },
594
+ {
595
+ "flag": "worker",
596
+ "in": "body",
597
+ "name": "worker",
598
+ "type": "string",
599
+ "description": "Your worker id; recorded as the `nodes.cleared` actor (default: the token label)."
600
+ }
601
+ ],
602
+ "body": true
603
+ },
604
+ {
605
+ "command": "renew",
606
+ "summary": "Renew a claim",
607
+ "description": "Extends the lease (to now + `ttlSeconds`) on the nodes still held under this claim; nodes cleared, released, expired or re-claimed since are not renewed, and the response lists the rest. Renew before `expiresAt` while work takes longer than the lease. `claim_not_found`: the id is wrong — take a new claim with `claim_nodes`.",
608
+ "method": "POST",
609
+ "path": "/v3/files/{fileKey}/claims/{claimId}/renew",
610
+ "positionals": [
611
+ {
612
+ "name": "claimId",
613
+ "description": "The `claimId` that `claim_nodes` returned."
614
+ }
615
+ ],
616
+ "flags": [
617
+ {
618
+ "flag": "file-key",
619
+ "in": "path",
620
+ "name": "fileKey",
621
+ "type": "string",
622
+ "required": true,
623
+ "description": "The Figma file key, e.g. `AbCdEf123456` from figma.com/design/<fileKey>/… — not a URL. Pass '-' to take it from `url` instead."
624
+ },
625
+ {
626
+ "flag": "url",
627
+ "in": "query",
628
+ "name": "url",
629
+ "type": "string",
630
+ "description": "A Figma link (figma.com/design/<fileKey>/…?node-id=1-2). Supplies the path parameters given as '-'; on routes with `under`, its node-id is the default `under`."
631
+ },
632
+ {
633
+ "flag": "ttl-seconds",
634
+ "in": "body",
635
+ "name": "ttlSeconds",
636
+ "type": "integer",
637
+ "minimum": 1,
638
+ "description": "Lease length in seconds (default: the server's RDS_CLAIM_TTL_SECONDS; capped at 86400 = 24 h)."
639
+ }
640
+ ],
641
+ "body": true
642
+ },
643
+ {
644
+ "command": "release",
645
+ "summary": "Release a claim",
646
+ "description": "Ends the claim and frees the nodes still under it, so other agents can take them. Call it when you stop without clearing everything (cleared nodes are already released). `claim_not_found`: the id is wrong; an unreleased lease simply expires at `expiresAt`.",
647
+ "method": "DELETE",
648
+ "path": "/v3/files/{fileKey}/claims/{claimId}",
649
+ "positionals": [
650
+ {
651
+ "name": "claimId",
652
+ "description": "The `claimId` that `claim_nodes` returned."
653
+ }
654
+ ],
655
+ "flags": [
656
+ {
657
+ "flag": "file-key",
658
+ "in": "path",
659
+ "name": "fileKey",
660
+ "type": "string",
661
+ "required": true,
662
+ "description": "The Figma file key, e.g. `AbCdEf123456` from figma.com/design/<fileKey>/… — not a URL. Pass '-' to take it from `url` instead."
663
+ },
664
+ {
665
+ "flag": "url",
666
+ "in": "query",
667
+ "name": "url",
668
+ "type": "string",
669
+ "description": "A Figma link (figma.com/design/<fileKey>/…?node-id=1-2). Supplies the path parameters given as '-'; on routes with `under`, its node-id is the default `under`."
670
+ }
671
+ ]
672
+ },
673
+ {
674
+ "command": "events",
675
+ "summary": "List file events",
676
+ "description": "The file's activity log, oldest first: syncs, clears, claims, blocks (IDs, counts and labels, never design content). Use it to see what changed since you last looked, or who holds what. JSON page `{ events, nextAfter }`: poll with `after` = the previous `nextAfter`; `after=0` starts from the oldest retained event. After a `sync.completed`, re-read the affected nodes. To read the newest events first, pass `order=desc`: the page `{ events, nextBefore }` is newest first; pass `before` = the previous `nextBefore` for the next older page (omit `before` to start at the newest event). A page shorter than `limit` is the last one. Filters apply the same in both orders. Types: sync.completed, nodes.cleared, claim.created, claim.renewed, claim.released, node.blocked, node.unblocked. With `Accept: text/event-stream`: a server-sent-event stream. Each message has `id` (the event id), `event` (the type) and `data` (the event as JSON). It replays the events after `Last-Event-ID` (header, sent by EventSource on reconnect) or `?after=`, then streams live; without either it streams live only. A `: heartbeat` comment is sent every 20 s. When the requested id is older than the replay window (RDS_EVENT_REPLAY_HOURS), one `event: reset` is sent instead of the replay — re-fetch state — and the stream continues live. `format` applies to the JSON page only (`ndjson` / `md` stream every event after `after` — with order=desc, before `before`, newest first — up to `limit` when given). `nodeId` matches data.nodeId or a member of data.nodeIds in JSON and SSE. `since` is an inclusive ISO timestamp lower bound on JSON pages (ignored for SSE). Filters combine with types, after and before. `order` and `before` apply to JSON pages only (SSE is always oldest first).",
677
+ "method": "GET",
678
+ "path": "/v3/files/{fileKey}/events",
679
+ "flags": [
680
+ {
681
+ "flag": "file-key",
682
+ "in": "path",
683
+ "name": "fileKey",
684
+ "type": "string",
685
+ "required": true,
686
+ "description": "The Figma file key, e.g. `AbCdEf123456` from figma.com/design/<fileKey>/… — not a URL. Pass '-' to take it from `url` instead."
687
+ },
688
+ {
689
+ "flag": "after",
690
+ "in": "query",
691
+ "name": "after",
692
+ "type": "integer",
693
+ "minimum": 0,
694
+ "description": "Only events with a greater id: the previous page's `nextAfter` (default 0 = from the start of the log)."
695
+ },
696
+ {
697
+ "flag": "before",
698
+ "in": "query",
699
+ "name": "before",
700
+ "type": "integer",
701
+ "minimum": 0,
702
+ "description": "Only events with a smaller id: with order=desc, the previous page's `nextBefore` (default: from the newest event). JSON pages only."
703
+ },
704
+ {
705
+ "flag": "order",
706
+ "in": "query",
707
+ "name": "order",
708
+ "type": "string",
709
+ "enum": [
710
+ "asc",
711
+ "desc"
712
+ ],
713
+ "default": "asc",
714
+ "description": "`asc` (default): oldest first, page with `after` / `nextAfter`. `desc`: newest first, page with `before` / `nextBefore`. JSON pages only; SSE is unchanged."
715
+ },
716
+ {
717
+ "flag": "types",
718
+ "in": "query",
719
+ "name": "types",
720
+ "type": "string",
721
+ "description": "Comma-separated event types: sync.completed, nodes.cleared, claim.created, claim.renewed, claim.released, node.blocked, node.unblocked."
722
+ },
723
+ {
724
+ "flag": "limit",
725
+ "in": "query",
726
+ "name": "limit",
727
+ "type": "integer",
728
+ "minimum": 1,
729
+ "maximum": 1000,
730
+ "description": "JSON page size (default 100, max 1000)."
731
+ },
732
+ {
733
+ "flag": "url",
734
+ "in": "query",
735
+ "name": "url",
736
+ "type": "string",
737
+ "description": "A Figma link (figma.com/design/<fileKey>/…?node-id=1-2). Supplies the path parameters given as '-'; on routes with `under`, its node-id is the default `under`."
738
+ },
739
+ {
740
+ "flag": "node-id",
741
+ "in": "query",
742
+ "name": "nodeId",
743
+ "type": "string",
744
+ "description": "Only events mentioning this node in data.nodeId or data.nodeIds (1:2 or 1-2); JSON and SSE."
745
+ },
746
+ {
747
+ "flag": "since",
748
+ "in": "query",
749
+ "name": "since",
750
+ "type": "string",
751
+ "description": "Inclusive ISO timestamp lower bound on createdAt for JSON pages; ignored for SSE."
752
+ },
753
+ {
754
+ "flag": "format",
755
+ "in": "query",
756
+ "name": "format",
757
+ "type": "string",
758
+ "enum": [
759
+ "json",
760
+ "ndjson",
761
+ "md"
762
+ ],
763
+ "description": "json (default); ndjson — one JSON object per line, streamed; md — a markdown checklist, one line per row, streamed. ndjson and md return every match from `cursor` on (up to `limit` when given) instead of one page."
764
+ }
765
+ ],
766
+ "paging": {
767
+ "param": "after",
768
+ "next": "nextAfter",
769
+ "items": "events"
770
+ }
771
+ },
772
+ {
773
+ "command": "block",
774
+ "summary": "Block a node",
775
+ "description": "Marks a node you cannot implement (missing asset, unclear or impossible design, needs a human) as blocked, with a reason (1–500 chars) a person can act on. It leaves the default dirty listing (list it with `state=blocked`) until `unblock_node` or its next design change, and its lease is released. Blocking a blocked node replaces the reason. Use it instead of clearing a node you did not build.",
776
+ "method": "POST",
777
+ "path": "/v3/files/{fileKey}/nodes/{nodeId}/block",
778
+ "positionals": [
779
+ {
780
+ "name": "nodeId",
781
+ "description": "The node id, 1:2 or 1-2 (a link's node-id). Pass '-' to take it from `url`."
782
+ }
783
+ ],
784
+ "flags": [
785
+ {
786
+ "flag": "file-key",
787
+ "in": "path",
788
+ "name": "fileKey",
789
+ "type": "string",
790
+ "required": true,
791
+ "description": "The Figma file key, e.g. `AbCdEf123456` from figma.com/design/<fileKey>/… — not a URL. Pass '-' to take it from `url` instead."
792
+ },
793
+ {
794
+ "flag": "url",
795
+ "in": "query",
796
+ "name": "url",
797
+ "type": "string",
798
+ "description": "A Figma link (figma.com/design/<fileKey>/…?node-id=1-2). Supplies the path parameters given as '-'; on routes with `under`, its node-id is the default `under`."
799
+ },
800
+ {
801
+ "flag": "reason",
802
+ "in": "body",
803
+ "name": "reason",
804
+ "type": "string",
805
+ "required": true,
806
+ "description": "Why the node cannot be implemented and what would unblock it."
807
+ },
808
+ {
809
+ "flag": "worker",
810
+ "in": "body",
811
+ "name": "worker",
812
+ "type": "string",
813
+ "description": "Your worker id; recorded as `by` (default: the token label)."
814
+ }
815
+ ],
816
+ "body": true
817
+ },
818
+ {
819
+ "command": "unblock",
820
+ "summary": "Unblock a node",
821
+ "description": "Removes a node's block once its blocker is resolved: it is dirty again (back in the default listing) or clean, by its hashes. `unblocked: false` when it was not blocked — nothing changed. A design change unblocks a node on its own.",
822
+ "method": "POST",
823
+ "path": "/v3/files/{fileKey}/nodes/{nodeId}/unblock",
824
+ "positionals": [
825
+ {
826
+ "name": "nodeId",
827
+ "description": "The node id, 1:2 or 1-2 (a link's node-id). Pass '-' to take it from `url`."
828
+ }
829
+ ],
830
+ "flags": [
831
+ {
832
+ "flag": "file-key",
833
+ "in": "path",
834
+ "name": "fileKey",
835
+ "type": "string",
836
+ "required": true,
837
+ "description": "The Figma file key, e.g. `AbCdEf123456` from figma.com/design/<fileKey>/… — not a URL. Pass '-' to take it from `url` instead."
838
+ },
839
+ {
840
+ "flag": "url",
841
+ "in": "query",
842
+ "name": "url",
843
+ "type": "string",
844
+ "description": "A Figma link (figma.com/design/<fileKey>/…?node-id=1-2). Supplies the path parameters given as '-'; on routes with `under`, its node-id is the default `under`."
845
+ },
846
+ {
847
+ "flag": "worker",
848
+ "in": "body",
849
+ "name": "worker",
850
+ "type": "string",
851
+ "description": "Your worker id; recorded as `by` (default: the token label)."
852
+ }
853
+ ],
854
+ "body": true
855
+ },
856
+ {
857
+ "command": "summary",
858
+ "summary": "Summarize changes",
859
+ "description": "Counts of the nodes the `list_nodes` filters match (default: dirty) by state, change type, change category and node type — a cheap overview before listing or planning work. With one `under` (e.g. a page), `byFrame` also counts per child frame of it (not with several `under` ids). With `expand=instances` the counts cover everything the scope uses — its nodes plus the main components its instances reach, transitively (`byFrame` still counts only nodes under `under`): how big is this screen, components included? Returns no nodes; use `list_nodes` or `list_work_items` for those.",
860
+ "method": "GET",
861
+ "path": "/v3/files/{fileKey}/summary",
862
+ "flags": [
863
+ {
864
+ "flag": "file-key",
865
+ "in": "path",
866
+ "name": "fileKey",
867
+ "type": "string",
868
+ "required": true,
869
+ "description": "The Figma file key, e.g. `AbCdEf123456` from figma.com/design/<fileKey>/… — not a URL. Pass '-' to take it from `url` instead."
870
+ },
871
+ {
872
+ "flag": "state",
873
+ "in": "query",
874
+ "name": "state",
875
+ "type": "string",
876
+ "description": "Comma-separated: dirty, clean, blocked, all. Default dirty (changed since last built, not blocked); `all` for every state."
877
+ },
878
+ {
879
+ "flag": "under",
880
+ "in": "query",
881
+ "name": "under",
882
+ "type": "string",
883
+ "description": "Only this node's subtree, the node included (1:2 or 1-2). Several subtrees: up to 50 comma-separated ids (1:2,1:5) for their union, each node once, in the order given. Defaults to the `url` link's node-id."
884
+ },
885
+ {
886
+ "flag": "depth",
887
+ "in": "query",
888
+ "name": "depth",
889
+ "type": "integer",
890
+ "minimum": 0,
891
+ "description": "Needs `under`: at most this many levels below it — below each root when there are several (1 = direct children)."
892
+ },
893
+ {
894
+ "flag": "type",
895
+ "in": "query",
896
+ "name": "type",
897
+ "type": "string",
898
+ "description": "Comma-separated Figma node types, e.g. TEXT,INSTANCE,FRAME."
899
+ },
900
+ {
901
+ "flag": "change-type",
902
+ "in": "query",
903
+ "name": "changeType",
904
+ "type": "string",
905
+ "description": "Comma-separated: added, modified, deleted (dirty nodes only)."
906
+ },
907
+ {
908
+ "flag": "changed",
909
+ "in": "query",
910
+ "name": "changed",
911
+ "type": "string",
912
+ "description": "Comma-separated changed property names (fills, paddingLeft); a trailing `*` matches a prefix (padding*)."
913
+ },
914
+ {
915
+ "flag": "category",
916
+ "in": "query",
917
+ "name": "category",
918
+ "type": "string",
919
+ "description": "Comma-separated change categories: layout, color, typography, effects, component, content, structure, prototype, other."
920
+ },
921
+ {
922
+ "flag": "component",
923
+ "in": "query",
924
+ "name": "component",
925
+ "type": "string",
926
+ "description": "Comma-separated main component ids: the components and all their instances."
927
+ },
928
+ {
929
+ "flag": "instance",
930
+ "in": "query",
931
+ "name": "instance",
932
+ "type": "string",
933
+ "enum": [
934
+ "true",
935
+ "false"
936
+ ],
937
+ "description": "true: only component instances (`isInstance`); false: only other nodes."
938
+ },
939
+ {
940
+ "flag": "name",
941
+ "in": "query",
942
+ "name": "name",
943
+ "type": "string",
944
+ "description": "Comma-separated case-insensitive name globs (`*`, `?`), e.g. Button*."
945
+ },
946
+ {
947
+ "flag": "since",
948
+ "in": "query",
949
+ "name": "since",
950
+ "type": "string",
951
+ "description": "ISO 8601 time: only nodes changed at or after it."
952
+ },
953
+ {
954
+ "flag": "claimed",
955
+ "in": "query",
956
+ "name": "claimed",
957
+ "type": "string",
958
+ "description": "Comma-separated: true (leased), false (free), mine (leased to `worker`)."
959
+ },
960
+ {
961
+ "flag": "worker",
962
+ "in": "query",
963
+ "name": "worker",
964
+ "type": "string",
965
+ "description": "Your worker id; needed with `claimed=mine`."
966
+ },
967
+ {
968
+ "flag": "q",
969
+ "in": "query",
970
+ "name": "q",
971
+ "type": "string",
972
+ "description": "All the filters above in one string: space-separated `key:value` pairs, e.g. `state:dirty under:1:2 changed:fills` (several subtrees: `under:1:2,1:5`); quote values with spaces (name:\"Primary Button\"). A key may not also be passed as its own parameter."
973
+ },
974
+ {
975
+ "flag": "url",
976
+ "in": "query",
977
+ "name": "url",
978
+ "type": "string",
979
+ "description": "A Figma link (figma.com/design/<fileKey>/…?node-id=1-2). Supplies the path parameters given as '-'; on routes with `under`, its node-id is the default `under`."
980
+ },
981
+ {
982
+ "flag": "expand",
983
+ "in": "query",
984
+ "name": "expand",
985
+ "type": "string",
986
+ "enum": [
987
+ "instances"
988
+ ],
989
+ "description": "`instances`: also include the subtree of every main component the scope's instances use (the scope is `under`, or the whole file) — transitively through instances inside those components, breadth-first, each component once. Use it for \"everything this screen uses\" / \"which components do I build for this screen\". Other filters apply after expansion; `depth` cannot be combined with it."
990
+ },
991
+ {
992
+ "flag": "expand-depth",
993
+ "in": "query",
994
+ "name": "expandDepth",
995
+ "type": "integer",
996
+ "minimum": 1,
997
+ "maximum": 20,
998
+ "description": "With `expand=instances`: at most this many instance → main hops (default 10, max 20; 1 = only the components the scope uses directly)."
999
+ }
1000
+ ]
1001
+ },
1002
+ {
1003
+ "command": "trends",
1004
+ "summary": "Weekly trends",
1005
+ "description": "How fast the backlog moves: nodes cleared and nodes newly dirtied by syncs in the last `windowDays` days (default 7) and the change in clears vs the window before, for the whole server (`total`) and each tracked file (`files`); `fileKey` narrows both to one file. Counts come from the event log, so `complete` is false when RDS_EVENT_RETENTION_DAYS (`retentionDays`) keeps less than two windows — `clearedPrevious` and `clearedChangePct` may then undercount. Returns no nodes.",
1006
+ "method": "GET",
1007
+ "path": "/v3/insights/trends",
1008
+ "flags": [
1009
+ {
1010
+ "flag": "window-days",
1011
+ "in": "query",
1012
+ "name": "windowDays",
1013
+ "type": "integer",
1014
+ "minimum": 1,
1015
+ "maximum": 14,
1016
+ "default": 7,
1017
+ "description": "Window length in days, 1–14 (default 7)."
1018
+ },
1019
+ {
1020
+ "flag": "file-key",
1021
+ "in": "query",
1022
+ "name": "fileKey",
1023
+ "type": "string",
1024
+ "description": "Only this tracked file (file_not_found otherwise)."
1025
+ }
1026
+ ]
1027
+ },
1028
+ {
1029
+ "command": "work",
1030
+ "summary": "List work items",
1031
+ "description": "The nodes the `list_nodes` filters match (default: dirty) bundled into work items — the best way to plan: fix a component once and its instances follow. Items are in document order (the design's layer order, by each item's key node), paged by `cursor`; each has its node ids (`claim_nodes` takes them), changed properties and categories. `group=component` (default): by the nearest COMPONENT / COMPONENT_SET ancestor-or-self; nodes with none form one item with `key: null`, listed last. `group=set`: merge variants of each known component set into one item named like the Figma set, with its Figma key, set reference and variants [{ key, name, nodeCount }]. Use this to build a design system. Standalone components and variants whose set is unavailable keep their component item; the default grouping is unchanged. `group=frame`: by the ancestor one level below `under` (required); the `under` node itself is in the `key: null` item. `include=progress` adds { total, clean } for each returned item across all states, independent of state= and the nodeIds cap. Use clean / total for a progress bar. `expand=instances` answers \"which components do I need to build for this screen, and how often does it use each?\": with `under` = the screen, items also cover every main component its instances reach (transitively, each once; filters apply after expansion), and with `group=component` each item has `placementsInScope` beside the file-wide `placements`. Nodes of expanded components that are outside `under` fall in the `key: null` item with `group=frame`. Each item's `changes` maps its listed node ids to their diffs (`changes=auto`: modified and deleted nodes; `diff`: added ones too; `names`: none — `changedProps` only). Values over 1 KB are `{ truncated: true, bytes }` and the page has `truncated: true`; `get_node` returns them whole.",
1032
+ "method": "GET",
1033
+ "path": "/v3/files/{fileKey}/work",
1034
+ "flags": [
1035
+ {
1036
+ "flag": "file-key",
1037
+ "in": "path",
1038
+ "name": "fileKey",
1039
+ "type": "string",
1040
+ "required": true,
1041
+ "description": "The Figma file key, e.g. `AbCdEf123456` from figma.com/design/<fileKey>/… — not a URL. Pass '-' to take it from `url` instead."
1042
+ },
1043
+ {
1044
+ "flag": "state",
1045
+ "in": "query",
1046
+ "name": "state",
1047
+ "type": "string",
1048
+ "description": "Comma-separated: dirty, clean, blocked, all. Default dirty (changed since last built, not blocked); `all` for every state."
1049
+ },
1050
+ {
1051
+ "flag": "under",
1052
+ "in": "query",
1053
+ "name": "under",
1054
+ "type": "string",
1055
+ "description": "Only this node's subtree, the node included (1:2 or 1-2). Several subtrees: up to 50 comma-separated ids (1:2,1:5) for their union, each node once, in the order given. Defaults to the `url` link's node-id."
1056
+ },
1057
+ {
1058
+ "flag": "depth",
1059
+ "in": "query",
1060
+ "name": "depth",
1061
+ "type": "integer",
1062
+ "minimum": 0,
1063
+ "description": "Needs `under`: at most this many levels below it — below each root when there are several (1 = direct children)."
1064
+ },
1065
+ {
1066
+ "flag": "type",
1067
+ "in": "query",
1068
+ "name": "type",
1069
+ "type": "string",
1070
+ "description": "Comma-separated Figma node types, e.g. TEXT,INSTANCE,FRAME."
1071
+ },
1072
+ {
1073
+ "flag": "change-type",
1074
+ "in": "query",
1075
+ "name": "changeType",
1076
+ "type": "string",
1077
+ "description": "Comma-separated: added, modified, deleted (dirty nodes only)."
1078
+ },
1079
+ {
1080
+ "flag": "changed",
1081
+ "in": "query",
1082
+ "name": "changed",
1083
+ "type": "string",
1084
+ "description": "Comma-separated changed property names (fills, paddingLeft); a trailing `*` matches a prefix (padding*)."
1085
+ },
1086
+ {
1087
+ "flag": "category",
1088
+ "in": "query",
1089
+ "name": "category",
1090
+ "type": "string",
1091
+ "description": "Comma-separated change categories: layout, color, typography, effects, component, content, structure, prototype, other."
1092
+ },
1093
+ {
1094
+ "flag": "component",
1095
+ "in": "query",
1096
+ "name": "component",
1097
+ "type": "string",
1098
+ "description": "Comma-separated main component ids: the components and all their instances."
1099
+ },
1100
+ {
1101
+ "flag": "instance",
1102
+ "in": "query",
1103
+ "name": "instance",
1104
+ "type": "string",
1105
+ "enum": [
1106
+ "true",
1107
+ "false"
1108
+ ],
1109
+ "description": "true: only component instances (`isInstance`); false: only other nodes."
1110
+ },
1111
+ {
1112
+ "flag": "name",
1113
+ "in": "query",
1114
+ "name": "name",
1115
+ "type": "string",
1116
+ "description": "Comma-separated case-insensitive name globs (`*`, `?`), e.g. Button*."
1117
+ },
1118
+ {
1119
+ "flag": "since",
1120
+ "in": "query",
1121
+ "name": "since",
1122
+ "type": "string",
1123
+ "description": "ISO 8601 time: only nodes changed at or after it."
1124
+ },
1125
+ {
1126
+ "flag": "claimed",
1127
+ "in": "query",
1128
+ "name": "claimed",
1129
+ "type": "string",
1130
+ "description": "Comma-separated: true (leased), false (free), mine (leased to `worker`)."
1131
+ },
1132
+ {
1133
+ "flag": "worker",
1134
+ "in": "query",
1135
+ "name": "worker",
1136
+ "type": "string",
1137
+ "description": "Your worker id; needed with `claimed=mine`."
1138
+ },
1139
+ {
1140
+ "flag": "q",
1141
+ "in": "query",
1142
+ "name": "q",
1143
+ "type": "string",
1144
+ "description": "All the filters above in one string: space-separated `key:value` pairs, e.g. `state:dirty under:1:2 changed:fills` (several subtrees: `under:1:2,1:5`); quote values with spaces (name:\"Primary Button\"). A key may not also be passed as its own parameter."
1145
+ },
1146
+ {
1147
+ "flag": "url",
1148
+ "in": "query",
1149
+ "name": "url",
1150
+ "type": "string",
1151
+ "description": "A Figma link (figma.com/design/<fileKey>/…?node-id=1-2). Supplies the path parameters given as '-'; on routes with `under`, its node-id is the default `under`."
1152
+ },
1153
+ {
1154
+ "flag": "expand",
1155
+ "in": "query",
1156
+ "name": "expand",
1157
+ "type": "string",
1158
+ "enum": [
1159
+ "instances"
1160
+ ],
1161
+ "description": "`instances`: also include the subtree of every main component the scope's instances use (the scope is `under`, or the whole file) — transitively through instances inside those components, breadth-first, each component once. Use it for \"everything this screen uses\" / \"which components do I build for this screen\". Other filters apply after expansion; `depth` cannot be combined with it."
1162
+ },
1163
+ {
1164
+ "flag": "expand-depth",
1165
+ "in": "query",
1166
+ "name": "expandDepth",
1167
+ "type": "integer",
1168
+ "minimum": 1,
1169
+ "maximum": 20,
1170
+ "description": "With `expand=instances`: at most this many instance → main hops (default 10, max 20; 1 = only the components the scope uses directly)."
1171
+ },
1172
+ {
1173
+ "flag": "group",
1174
+ "in": "query",
1175
+ "name": "group",
1176
+ "type": "string",
1177
+ "enum": [
1178
+ "component",
1179
+ "frame",
1180
+ "set"
1181
+ ],
1182
+ "description": "component (default): by nearest component; set: merge all matching variants of a known component set (best for design systems); frame: by child frame of `under` (needs exactly one `under`)."
1183
+ },
1184
+ {
1185
+ "flag": "changes",
1186
+ "in": "query",
1187
+ "name": "changes",
1188
+ "type": "string",
1189
+ "enum": [
1190
+ "auto",
1191
+ "diff",
1192
+ "names"
1193
+ ],
1194
+ "description": "auto (default): each item's `changes` has the diffs of its modified and deleted nodes; diff: of every listed node, added ones included (their full values); names: no `changes`, only `changedProps`."
1195
+ },
1196
+ {
1197
+ "flag": "limit",
1198
+ "in": "query",
1199
+ "name": "limit",
1200
+ "type": "integer",
1201
+ "minimum": 1,
1202
+ "maximum": 1000,
1203
+ "description": "Items per page (default 100, max 1000). With format=outline: rendered lines per page (default: every line)."
1204
+ },
1205
+ {
1206
+ "flag": "cursor",
1207
+ "in": "query",
1208
+ "name": "cursor",
1209
+ "type": "string",
1210
+ "description": "The previous page's `nextCursor`, unchanged, with all other parameters the same (else `invalid_cursor`). With format=outline: the previous response's `X-RDS-Next-Cursor` header (a line position in the rendered outline)."
1211
+ },
1212
+ {
1213
+ "flag": "include",
1214
+ "in": "query",
1215
+ "name": "include",
1216
+ "type": "string",
1217
+ "enum": [
1218
+ "progress"
1219
+ ],
1220
+ "description": "progress: add total and clean node counts across all states for each work item."
1221
+ },
1222
+ {
1223
+ "flag": "format",
1224
+ "in": "query",
1225
+ "name": "format",
1226
+ "type": "string",
1227
+ "enum": [
1228
+ "json",
1229
+ "ndjson",
1230
+ "md",
1231
+ "outline"
1232
+ ],
1233
+ "description": "json (default); ndjson — one JSON object per line, streamed; md — a markdown checklist, one line per row, streamed. ndjson and md return every match from `cursor` on (up to `limit` when given) instead of one page. outline — text/plain, an indented tree with one line per item: use this first to see a screen's structure (under=<the screen>, and expand=instances for the components inside). Each item is a line `Name (TYPE) · 3 nodes · changed (4: characters, fills, …) · 2 placements`, with its listed nodes as an indented tree below it (`Name (TYPE) · modified (…)`, indented under their nearest listed ancestor). Lines are the `fields`-independent summary. The whole result (up to 2000 lines of whole items, in order) is rendered once, then paged by rendered lines, so nesting and `(shown above)` read the same at any page size: `limit` is lines per page (default: every line), and when lines remain the response has an `X-RDS-Next-Cursor` header — pass it as `cursor`, with every other parameter unchanged, for the next lines. Past the 2000-line cap the outline's last line (counted like any other) is `… N more items (use cursor/limit) — cursor=<c>` (the same cursor is in the header), which continues after the rendered part."
1234
+ }
1235
+ ],
1236
+ "paging": {
1237
+ "param": "cursor",
1238
+ "next": "nextCursor",
1239
+ "items": "items"
1240
+ }
1241
+ }
1242
+ ]
1243
+ }