AI Internal API (wf2des-api) — I/O Definition
This page is the authoritative I/O contract for the wf2des-api route group inside apps/internal-api/ (guinness-ai-v2). It is the VPC-internal HTTP data plane consumed by the Figma plugin (session-issued token) and by the backend / MCP (shared service token). Any endpoint, field, enum value, status code, path/query parameter, or error-shape change must be reflected here — and in the sibling AI Read API (mcp-api) I/O Definition, which shares the same internal-api app — before implementation.
Contract authority. The read routes return, and the write routes mutate, blocks of the
design_generation_resultdocument and theproject_figma_filecollection owned by the AI WF2Des worker. The field shapes are defined by that worker's contract; this page is the access contract over them. When a returned or written block changes shape there, this page changes with it.
Overview
wf2des-api is the plugin's only door into the AI plane. It presigns/reads DocumentDB documents and S3 artifacts, and it accepts the plugin's detail-first writes — the plugin writes the detail sub-doc HERE first, then calls the corresponding backend flag endpoint that flips the wf2des PostgreSQL row. wf2des-api never performs the PG flip itself.
flowchart LR
PLUGIN["Figma plugin<br/>(session token<br/>{org_id, project_id, exp})"]
BE["Backend / MCP<br/>(X-AI-Service-Token)"]
API["wf2des-api<br/>(internal-api app, VPC internal)<br/>/internal/wf2des/* · /internal/projects/*"]
DDB[("DocumentDB guinness_v2<br/>design_generation_result ·<br/>project_figma_file")]
S3[("S3<br/>result / parse artifacts")]
PLUGIN -->|"read: result / parse / figma-files<br/>write: reject / placement / feedback / figma-files"| API
BE -->|"read only (X-AI-Service-Token)"| API
API -->|"find_one / update sub-doc block"| DDB
API -->|"presign / proxy artifact"| S3
API -.->|"NEVER"| PG[("PostgreSQL")]
API -.->|"NEVER"| SQS[("SQS")]
PLUGIN -->|"detail first, flag last:<br/>after a write, call the BACKEND flag endpoint"| BE
| Item | Value |
|---|---|
| Route group | wf2des-api — /internal/wf2des/* + /internal/projects/* in the internal-api app (Lambda Function URL / private HTTP integration, VPC internal only) |
| Callers | Figma plugin (session token, read + write, project-scoped) · backend / MCP (shared X-AI-Service-Token, read only) |
| Reads | DocumentDB design_generation_result, project_figma_file + S3 (result / spec / parse artifacts) |
| Writes | DocumentDB sub-doc blocks (parse.rejected, placement, feedback) + project_figma_file docs. No S3 writes — media/artifacts are produced by the worker; this API presigns/proxies them |
| RDB access | None — wf2des-api NEVER connects to PostgreSQL. Every wf2des-row flag flip is a SEPARATE backend endpoint the plugin calls after the detail write |
| Queue access | None — wf2des-api NEVER sends SQS. It is a synchronous data plane, not a job producer |
| Tenancy | Every route is org/project-scoped; every read and write filters organization_id + project_id |
Hard platform rule. wf2des-api touches DocumentDB + S3 ONLY — never PostgreSQL, never SQS. It is a pure data-plane surface. The wf2des PostgreSQL row is advanced only by the backend (the ai-status webhook handler and the confirm / placement / feedback / placement endpoints); the AI-owned wf2des-events SQS queue is fed only by the backend's send-only permission. wf2des-api participates in neither.
Route Summary
| # | Route | Kind | Auth (min) | DocDB / S3 effect |
|---|---|---|---|---|
| 1 | GET /internal/wf2des/{wf2des_id}/result |
read | session token or X-AI-Service-Token |
design_generation_result.spec (presign artifact_urls.spec on spill) |
| 2 | GET /internal/wf2des/{wf2des_id}/parse |
read | session token or X-AI-Service-Token |
design_generation_result.parse + presign the immutable parse.json |
| 3 | GET /internal/projects/{project_id}/figma-files |
read | session token or X-AI-Service-Token |
project_figma_file.find (project-scoped) |
| 4 | POST /internal/wf2des/{wf2des_id}/reject |
write | session token only | write design_generation_result.parse.rejected |
| 5 | POST /internal/wf2des/{wf2des_id}/placement |
write | session token only | write design_generation_result.placement |
| 6 | POST /internal/wf2des/{wf2des_id}/feedback |
write | session token only | write design_generation_result.feedback |
| 7 | POST / PUT /internal/projects/{project_id}/figma-files |
write | session token only | upsert a project_figma_file doc — UNIQUE(organization_id, project_id, figma_file_key) |
Auth split. Read routes accept EITHER the plugin session token OR the shared X-AI-Service-Token (backend / MCP resolution). Write routes accept only the plugin session token — the backend/MCP service token is read-scoped and MUST NOT write. See Auth.
Detail-first (write routes 4–6). The plugin writes the detail sub-doc HERE first, then calls the matching backend flag endpoint that flips the wf2des PG row — detail first, flag last. Route 7 is different: it is a DIRECT wf2des-api write with no backend flag partner (project_figma_file is a DocumentDB collection, not a PG-flag surface).
Read Routes
1. GET /internal/wf2des/{wf2des_id}/result
Returns the design_generation_result DesignSpec — the spec block the assemble phase wrote. The plugin materializes native Figma nodes from this spec.
| Property | Value |
|---|---|
| Method / Path | GET /internal/wf2des/{wf2des_id}/result |
| Auth | session token or X-AI-Service-Token (read) |
| Path params | wf2des_id — the run id = the wf2des PG row id (design_generation_result._id) |
| Query params | none |
| Request body | none |
| DocDB read | design_generation_result.find_one({_id: wf2des_id, organization_id, project_id}) → the spec block |
| S3 effect | if the spec spilled (spec > ~1MB), spec is absent inline and lives at artifact_urls.spec; the API presigns that S3 object and returns the URL alongside the doc |
| PG / SQS | none |
Response body — the DesignSpec (the spec block, shape per the worker contract; parse_confirmed mirrored; five node types layout_frame / instance / compose / text / unmatched):
{
"ok": true,
"data": {
"wf2des_id": "8f14e4...",
"spec": {
"spec_version": "…",
"parse_confirmed": true,
"style_bindings": { "token/name": "figma-style-or-variable-id" },
"root": { "…": "layout_frame / instance / compose / text / unmatched tree" }
},
"confidence": { "min": 0.71, "avg": 0.88, "flag_count": 3, "formula_version": "cf@0.2" },
"artifact_urls": {
"result": "s3://…-result.json",
"spec": "https://…presigned…-spec.json" // present (presigned) ONLY when the spec spilled to S3
}
},
"error": null
}
On spec spill, data.spec may be omitted inline and data.artifact_urls.spec carries the presigned URL the plugin follows. Result artifacts are readable only via this presigning path (or the backend's own role).
2. GET /internal/wf2des/{wf2des_id}/parse
Returns the result doc's parse block plus the immutable parse.json artifact — the awaiting_confirm preview surface. The plugin previews roles / intent / memo influences from this before the designer confirms or rejects.
| Property | Value |
|---|---|
| Method / Path | GET /internal/wf2des/{wf2des_id}/parse |
| Auth | session token or X-AI-Service-Token (read) |
| Path params | wf2des_id — the run id (design_generation_result._id) |
| Query params | none |
| Request body | none |
| DocDB read | design_generation_result.find_one({_id, organization_id, project_id}) → the parse block + artifact_urls.parse |
| S3 effect | presigns the immutable parse.json (artifact_urls.parse) — the AUTHORITATIVE parse this run used; the preview reads THIS artifact, never the overwritable wireframe cache doc |
| PG / SQS | none |
Response body — the parse block + presigned artifact pointer:
{
"ok": true,
"data": {
"wf2des_id": "8f14e4...",
"parse": {
"confirmed": false,
"confirmed_by": null,
"confirmed_at": null,
"rejected": null, // structured {reason_code, note} once a reject is written (route 4)
"memo_influences": [
{ "memo_node_id": "…", "text": "…" } // TEXT SNAPSHOTTED — survives wireframe reparse
]
},
"artifact_urls": {
"parse": "https://…presigned…-parse.json" // the immutable full WFNode tree + memos as used (roles / intent)
}
},
"error": null
}
The plugin fetches the presigned parse.json to render the full parsed WFNode tree (roles, per-node intent) for the preview.
3. GET /internal/projects/{project_id}/figma-files
Lists the project's registered Figma files from project_figma_file. Used by the plugin for org/project + file resolution, and by component_sweep (via the backend/service token) to scope its walk.
| Property | Value |
|---|---|
| Method / Path | GET /internal/projects/{project_id}/figma-files |
| Auth | session token or X-AI-Service-Token (read) |
| Path params | project_id — integer, > 0 |
| Query params | none (project-scoped by path) |
| Request body | none |
| DocDB read | project_figma_file.find({organization_id, project_id}) |
| S3 effect | none |
| PG / SQS | none |
Response body — the registered files (role, config_url, components_synced_at):
{
"ok": true,
"data": {
"project_id": 3,
"items": [
{
"figma_file_key": "hDDA9BNori9OTXSClduXqR",
"role": "working", // working | library
"config_url": "s3://…/1/3/wf2des/config.json",
"components_synced_at": "2026-07-06T09:30:00Z"
}
]
},
"error": null
}
role is one of working | library. The sweep-owned fields (style_captures, sweep_marker, sweep_error) are worker-internal and are not part of the plugin-facing list shape.
Write Routes
Detail first, flag last. Routes 4–6 write a detail sub-doc of
design_generation_resultHERE, then the plugin calls the corresponding backend flag endpoint that flips thewf2desPG row.wf2des-apiNEVER writes PostgreSQL. Route 7 is a direct write with no backend partner.
4. POST /internal/wf2des/{wf2des_id}/reject
Writes the result doc's parse.rejected — the designer's structured decline of the parse at the awaiting_confirm checkpoint. A reject is NOT a failure (it maps to wf2des status '3' rejected, not '2' failed).
| Property | Value |
|---|---|
| Method / Path | POST /internal/wf2des/{wf2des_id}/reject |
| Auth | session token only (write, project-scoped) |
| Path params | wf2des_id — the run id (design_generation_result._id) |
| Request body | { reason_code, note } |
| DocDB write | set design_generation_result.parse.rejected = { reason_code, note } (scoped by organization_id + project_id) |
| S3 / PG / SQS | no S3, no PG, no SQS |
| Detail-first partner | after this write, the plugin calls the backend confirm endpoint, which flips the wf2des row to status '3' rejected |
Request body — reason_code is one of wrong_roles | wrong_memos | wrong_sections | other:
{
"reason_code": "wrong_roles", // wrong_roles | wrong_memos | wrong_sections | other
"note": "The CTA row was parsed as a header."
}
The written parse.rejected becomes the rejected value returned by route 2. The wf2des status '3' flip is a SEPARATE backend call; wf2des-api only records the detail.
5. POST /internal/wf2des/{wf2des_id}/placement
Writes the result doc's placement — the plugin's materialization record after it stitches the native Figma nodes from the spec. Records the placed node and the materializer's per-layer fallback/error report.
| Property | Value |
|---|---|
| Method / Path | POST /internal/wf2des/{wf2des_id}/placement |
| Auth | session token only (write, project-scoped) |
| Path params | wf2des_id — the run id (design_generation_result._id) |
| Request body | { placed_node_id, materialized_at, materializer_report[] } |
| DocDB write | set design_generation_result.placement (plugin-written fields; the worker already wrote placement.design_area at assemble time) |
| S3 / PG / SQS | no S3, no PG, no SQS |
| Detail-first partner | after this write, the plugin calls the backend placement endpoint, which flips materialized_at on the wf2des row |
Request body — materializer_report[].event is one of name_fallback | ordinal_fallback | build_error | font_fallback | prop_rejected | unmatched | preserved:
{
"placed_node_id": "40002029:37050",
"materialized_at": "2026-07-06T09:41:12Z",
"materializer_report": [
{
"layer_path": "root/section-1/cta",
"event": "name_fallback", // one of the seven values listed above
"detail": "component_key not found; matched by name"
}
]
}
The placement block coexists with the worker-written placement.design_area (the resolved DESIGN-area rect). The materialized_at flip on the row is the SEPARATE backend call.
6. POST /internal/wf2des/{wf2des_id}/feedback
Writes the result doc's feedback — the designer's post-materialization signal (an accepted result, or a fixed one), with the changed nodes and a diff pointer.
| Property | Value |
|---|---|
| Method / Path | POST /internal/wf2des/{wf2des_id}/feedback |
| Auth | session token only (write, project-scoped) |
| Path params | wf2des_id — the run id (design_generation_result._id) |
| Request body | { status, changed_nodes[], diff_url, at, by } |
| DocDB write | set design_generation_result.feedback |
| S3 / PG / SQS | no S3, no PG, no SQS — diff_url is a pointer to a worker/plugin-produced diff artifact, not written here |
| Detail-first partner | after this write, the plugin calls the backend feedback endpoint, which flips feedback_status on the wf2des row |
Request body — status is one of fixed | adopted:
{
"status": "adopted", // fixed | adopted
"changed_nodes": ["root/section-1/cta", "root/section-2/hero"],
"diff_url": "s3://…/1/3/wf2des/8f14e4…-…-feedback.json",
"at": "2026-07-06T10:05:00Z",
"by": "designer@example.com"
}
changed_nodes are layer_path values (matching the spec's layer_path addressing). The feedback_status flip on the row is the SEPARATE backend call.
7. POST / PUT /internal/projects/{project_id}/figma-files
Registers (POST) or updates (PUT) a project_figma_file entry — the file's role and config_url. This is a DIRECT wf2des-api write with no backend flag partner: project_figma_file is a DocumentDB collection, not a PG-flag surface.
| Property | Value |
|---|---|
| Method / Path | POST / PUT /internal/projects/{project_id}/figma-files |
| Auth | session token only (write, project-scoped) |
| Path params | project_id — integer, > 0 |
| Request body | { figma_file_key, role, config_url } |
| DocDB write | upsert a project_figma_file doc — UNIQUE(organization_id, project_id, figma_file_key). POST inserts (409 on an existing key); PUT updates the role / config_url of an existing doc |
| S3 / PG / SQS | no S3, no PG, no SQS |
| Detail-first partner | none — this is a direct write, not a detail-then-flag pair |
Request body — role is one of working | library:
{
"figma_file_key": "hDDA9BNori9OTXSClduXqR",
"role": "working", // working | library
"config_url": "s3://…/1/3/wf2des/config.json"
}
The plugin's role / config_url writes are field-level disjoint from the sweep-owned fields (style_captures, components_synced_at, sweep_marker, sweep_error) that component_sweep stamps — they never conflict. A POST that collides on UNIQUE(organization_id, project_id, figma_file_key) returns 409; use PUT to update an existing registration.
Error Handling
Errors use the same envelope as the sibling read API ({ ok, data, error } with error: { code, message }).
| Scenario | HTTP status | Error code |
|---|---|---|
Missing / invalid X-AI-Service-Token and no valid session token |
401 | UNAUTHORIZED |
Unknown wf2des_id or project_id (no such doc) |
404 | NOT_FOUND |
Wrong project scope — the token's {org_id, project_id} does not match the path scope, or a bad/expired session token |
403 | FORBIDDEN |
| Service token used on a write route (read-scoped token, write attempt) | 403 | FORBIDDEN |
POST figma-file collides on UNIQUE(organization_id, project_id, figma_file_key) |
409 | CONFLICT |
Invalid path/query/body (bad reason_code / event / status / role enum, malformed body) |
400 | BAD_REQUEST |
| DocumentDB / S3 connection or query error | 500 | INTERNAL_ERROR |
Notes:
- 404 (unknown id). A missing
design_generation_result(wf2des_id), or a project with noproject_figma_filedocs where a specific file is addressed, returnNOT_FOUND. An empty figma-files LIST (route 3) is not a 404 — it returns{ items: [] }. - 403 (wrong project scope / bad session token). Every route is org/project-scoped. A session token whose
{org_id, project_id}does not match the requestedproject_id/ the doc's tenancy, an expired token (exppassed), or a read-scopedX-AI-Service-Tokenused on a write route →FORBIDDEN. Cross-project access is never a default. - 409 (figma-file unique conflict). A POST to route 7 that hits an existing
UNIQUE(organization_id, project_id, figma_file_key)→CONFLICT. PUT is the idempotent update path.
Error body:
{
"ok": false,
"data": null,
"error": { "code": "NOT_FOUND", "message": "design_generation_result not found" }
}
Auth
Two credentials reach wf2des-api; the route's kind decides which are accepted (see the Route Summary).
Plugin session token (read + write, project-scoped)
The Figma plugin presents a session-issued data-plane token carrying {org_id, project_id, exp}:
- Scope. The token pins ONE
org_id+project_id. Every request is validated against that scope; a pathproject_id(or a doc's tenancy) outside the token's scope → 403. - Expiry.
expis enforced; a passedexp→ 403 (bad/expired session token). - Rights. Read + write. This is the ONLY credential accepted on the write routes (4–7).
Backend / MCP service token (read only)
The backend and MCP present the shared X-AI-Service-Token header, compared to the configured secret using a constant-time comparison (same as the sibling mcp-api):
- Rights. Read only. Accepted on the read routes (1–3) for org/project + file resolution and result/parse retrieval. It is rejected on the write routes — a write attempt with the service token → 403 (
FORBIDDEN). - A missing / mismatched token on a route that requires it, with no valid session token present → 401 (
UNAUTHORIZED).
What wf2des-api never holds. No PostgreSQL credentials and no SQS send permission. The wf2des-row flags are flipped by the backend endpoints the plugin calls after each detail write; the AI-owned wf2des-events queue is fed only by the backend's send-only right. wf2des-api is DocumentDB + S3 only.
Related
- AI Read API (mcp-api) I/O Definition — the sibling internal HTTP read API in the same
internal-apiapp (backend MCP v2 → DocumentDBdesign/code). - AI WF2Des I/O Definition — the worker that PRODUCES the
design_generation_resultandproject_figma_filedocuments these routes read and (in part) write; source of the exact block shapes.