Skip to content

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_result document and the project_figma_file collection 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
GET /internal/wf2des/8f14e4.../result HTTP/1.1
Authorization: Bearer <plugin-session-token>

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_result HERE, then the plugin calls the corresponding backend flag endpoint that flips the wf2des PG row. wf2des-api NEVER 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 no project_figma_file docs where a specific file is addressed, return NOT_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 requested project_id / the doc's tenancy, an expired token (exp passed), or a read-scoped X-AI-Service-Token used 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 path project_id (or a doc's tenancy) outside the token's scope → 403.
  • Expiry. exp is enforced; a passed exp → 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.


  • AI Read API (mcp-api) I/O Definition — the sibling internal HTTP read API in the same internal-api app (backend MCP v2 → DocumentDB design / code).
  • AI WF2Des I/O Definition — the worker that PRODUCES the design_generation_result and project_figma_file documents these routes read and (in part) write; source of the exact block shapes.