Skip to content

Generate Design from Wireframe (Trigger)

Method

This API follows the REST methodology. It is the trigger for a wireframe-to-design generation run: it creates the client-facing wf2des row, captures the trigger-time wireframe snapshot, and enqueues the parse message. Generation itself runs asynchronously; this endpoint returns immediately with a poll target.

HTTP Method

POST: Generate Design from Wireframe (Trigger)

Naming Convention

To ensure consistency and readability, JSON nodes in requests and responses use camelCase. SQS payload nodes use snake_case.

Request and Response

Headers

Meta information is set in HTTP headers, not in the response body.

Request Headers

  • Authorization
  • Content-Type
  • Accept
  • Accept-language

Response Headers

  • Content-Type

Generate Design from Wireframe (Trigger)

URI

POST /api/v1/organizations/{organization_id}/projects/{project_id}/wf2des

Path Parameters

Name Type Required Description
organization_id integer Required Organization ID
project_id integer Required Project ID

Request Body

The request body is JSON.

{
  "figmaFileKey": "aBc1DeFg2HiJ3kLmNoPqRs",
  "wfNodeId": "128:4567",
  "sectionNodeId": "128:4000",
  "screenId": "MEM_REG_TOP",
  "prompt": "Emphasize the primary CTA and keep the form compact",
  "placementTarget": "128:9001",
  "autoConfirm": false
}

Request Parameters

Name Type Required Description
figmaFileKey string Required Target Figma file key the wireframe frame lives in (Figma verbatim; alphanumeric, no underscore)
wfNodeId string Required Target wireframe frame node id (Figma node id, uses :)
sectionNodeId string Optional The frame's enclosing board / SECTION, resolved by the caller, used to scope the snapshot fetch. Omit it and the backend resolves the scope itself by walking the whole file document. Verified server-side, never trusted: if the frame is not inside this subtree, the backend logs wf2desSnapshot.scopeRejected and falls back to the full walk.
screenId string Required Screen ID, prefilled from the frame name and user-confirmed
prompt string Optional Generation prompt (selection input, ranked below memo intent)
placementTarget string Optional Placement node id, echoed into the result document request block
autoConfirm boolean Required false normal (parse โ†’ confirm step) / true auto-confirm (parse + assemble in one worker invocation)

trigger_surface is derived backend-side from the authenticated surface (0 api / 1 plugin) and is never caller-supplied.

Why sectionNodeId is worth sending. The snapshot needs the frame's enclosing board, and the Figma REST API cannot return a node's ancestors โ€” /nodes gives a subtree with none. Without this field the backend must therefore fetch GET /files/{key}, the entire file. Measured on a 31-page production file that response was 255 MB and took 67.5s, and one trigger failed outright at 140s with "socket connection was closed unexpectedly", surfacing as a 500. The same scope fetched as a subtree is 0.38 MB. A Figma plugin knows the ancestry for free, so the plugin client always sends it.

Response

The response is JSON (HTTP status: 201 Created).

{
  "wf2desId": "8f14e45f-ceea-467d-9a3e-1b2c3d4e5f60",
  "status": "0",
  "phase": "0",
  "figmaFileKey": "aBc1DeFg2HiJ3kLmNoPqRs",
  "wfNodeId": "128:4567",
  "screenId": "MEM_REG_TOP",
  "autoConfirm": false,
  "triggerSurface": "0",
  "attempt": 1,
  "createdAt": "2026-07-10T09:30:00Z"
}

Response Fields

Name Type Description
wf2desId string wf2des UUID โ€” the client-facing request id (the poll key for GET; echoed to the worker as job_id)
status string (enum) Generation status. On create always "0" processing (see Error Handling / Type Codes)
phase string (enum) Orchestration phase inside status "0". On create always "0" parse
figmaFileKey string Target Figma file key
wfNodeId string Target wireframe frame node id
screenId string Screen ID
autoConfirm boolean Echo of the requested auto-confirm mode
triggerSurface string (enum) "0" api / "1" plugin โ€” derived backend-side, never caller-supplied
attempt integer Monotonic fencing counter โ€” always 1 today; nothing bumps it
createdAt string Row creation timestamp (ISO 8601)

Status / phase enums (wf2des row):

  • status: "0" processing ยท "1" completed ยท "2" failed ยท "3" rejected ยท "4" cancelled
  • phase (inside status "0", null once terminal): "0" parse ยท "1" awaiting_confirm ยท "2" assemble

Authentication

Authentication is performed using JSON Web Tokens (JWT) issued by Amazon Cognito. The authenticated surface determines trigger_surface (0 api / 1 plugin), which the backend records on the row โ€” it is not accepted from the request body.

Error Handling

The following status codes are returned for errors.

Description Status Code Status Name
Missing required fields, or invalid figmaFileKey / wfNodeId format 400 Bad Request
Missing authentication credentials 401 Unauthorized
Insufficient permissions 403 Forbidden
Organization or project not found 404 Not Found
Duplicate open generation โ€” a live run already exists for this (projectId, figmaFileKey, wfNodeId) 409 Conflict
Internal server error (e.g. snapshot capture or SQS send failure) 500 Internal Server Error

A missing Figma token surfaces as 500, not 4xx. Every failure between row creation and enqueue โ€” including "no Figma token registered for this user" โ€” is deliberately normalized so that no upstream Figma status leaks to the caller, and the row is failed so the node can be re-triggered. The remedy is to register a token first via POST /v1/figma/token; clients should check GET /v1/figma/token and prompt for one before offering Generate, rather than surfacing an opaque 500 to the designer.

409 โ€” Duplicate Open Generation

The wf2des row enforces a partial unique index on (project_id, figma_file_key, wf_node_id) WHERE status = '0' โ€” at most one live (in-progress) generation per wireframe node. A duplicate trigger does not create a second row; it returns 409 Conflict together with the existing wf2desId, so the second caller attaches to the run already in flight.

{
  "error": "duplicate_open_generation",
  "message": "A generation is already in progress for this wireframe node.",
  "wf2desId": "8f14e45f-ceea-467d-9a3e-1b2c3d4e5f60"
}

Processing Flow

  1. Extract organization_id and project_id from the path parameters.
  2. Authenticate the Cognito JWT and verify the user has Write access to the project; derive trigger_surface (0 api / 1 plugin) from the authenticated surface.
  3. Extract and validate the request body: figmaFileKey, wfNodeId, screenId (required), prompt, placementTarget, autoConfirm.
  4. Create the wf2des row FIRST โ€” status "0" processing, phase "0" parse, attempt = 1, with figma_file_key, wf_node_id, screen_id, prompt, placement_target, auto_confirm, and the derived trigger_surface. The client always has a poll target before anything is enqueued.
  5. If the insert violates the partial unique index (project_id, figma_file_key, wf_node_id) WHERE status = '0', return 409 with the existing wf2desId (the second caller attaches to the live run).
  6. Resolve the requesting user's own Figma token (figma_token.personal_access_token, keyed on their cognito_sub). Generation is a user action, so the snapshot is taken with the authority of whoever asked for it โ€” not a service-account PAT. A user therefore cannot snapshot a file they could not open themselves. If they have no registered token the run fails at this step; see the note under Error Responses.
  7. Capture the trigger-time wireframe snapshot (frame + enclosing board section, memos included) to S3, auto-registering the frame if it is not yet registered; compute the snapshot wf_content_hash. When sectionNodeId is supplied and contains the frame, only that subtree is fetched; otherwise the whole file document is walked.
  8. Send the parse message to the backend-owned generation SQS queue (snake_case payload โ€” see below).
  9. Return 201 Created with the created wf2desId, status "0", phase "0", and echoed request fields.

The worker then parses the snapshot and posts an ai-status webhook; the backend advances the row (phase "1" awaiting_confirm on interactive parse-done, or straight to assemble on autoConfirm). Workers never write PostgreSQL โ€” every row transition is backend-side.

Detailed Flowchart

flowchart TD
    Start([POST Request]) --> Route[Route Handler]
    Route --> Auth[Auth & Parameter Extraction<br/>derive trigger_surface 0 api / 1 plugin]
    Auth --> AccessCheck[Access Check]

    AccessCheck --> HasAccess{Write Access?}
    HasAccess -->|No| Err403[403 Forbidden]
    HasAccess -->|Yes| VerifyScope[Verify Organization & Project]

    VerifyScope --> ScopeExists{Exists?}
    ScopeExists -->|No| Err404[404 Not Found]
    ScopeExists -->|Yes| Validate[Validate Body<br/>figmaFileKey / wfNodeId / screenId]

    Validate --> BodyValid{Valid?}
    BodyValid -->|No| Err400[400 Bad Request]
    BodyValid -->|Yes| CreateRow[Create wf2des Row FIRST<br/>status=0 processing, phase=0 parse<br/>attempt=1]

    CreateRow --> Dedupe{Open generation exists?<br/>partial unique figma_file_key + wf_node_id<br/>WHERE status='0'}
    Dedupe -->|Yes| Err409[409 Conflict<br/>+ existing wf2desId]
    Dedupe -->|No| Snapshot[Capture trigger-time WF snapshot<br/>auto-register unregistered frame<br/>compute wf_content_hash]

    Snapshot --> SnapResult{Success?}
    SnapResult -->|No| Err500[500 Internal Server Error]
    SnapResult -->|Yes| SendSQS[SendMessage parse payload<br/>to generation SQS queue]

    SendSQS --> SQSResult{Success?}
    SQSResult -->|No| Err500
    SQSResult -->|Yes| Success[201 Created<br/>wf2desId, status=0, phase=0]

Asynchronous Processing

The design is generated asynchronously by the wf2des worker. The backend โ€” never the worker โ€” advances the row:

  • status "0" processing, phase "0" parse (on create)
  • โ†’ phase "1" awaiting_confirm (interactive parse-done, via the ai-status webhook) or straight into phase "2" assemble on autoConfirm
  • โ†’ terminal status "1" completed / "2" failed (via the ai-status webhook), or "3" rejected (parse declined) / "4" cancelled (cancel endpoint)

The output is a native-Figma DesignSpec written to the design_generation_result DocumentDB collection (keyed by the wf2desId), fetched via the wf2des data-plane API and materialized by the Figma plugin. phase is cleared (null) once the row reaches a terminal status.

SQS Payload

On trigger, the backend sends the parse message to the backend-owned generation SQS queue. The message is in snake_case.

{
  "wf2des_id": "8f14e45f-ceea-467d-9a3e-1b2c3d4e5f60",
  "attempt": 1,
  "nonce": "9c2b7f1a-0e44-4b8f-9d2a-77c1e0a3b512",
  "wireframe_img_url": "s3://bucket-name/1/3/wf2des/snapshots/aBc1DeFg2HiJ3kLmNoPqRs/128:4567.png",
  "wireframe_json_url": "s3://bucket-name/1/42/wf2des/snapshots/aBc1DeFg2HiJ3kLmNoPqRs/128-4567.<content_hash>.json",
  "snapshot_scope": {
    "frame_node_id": "128:4567",
    "section_node_id": "128:4000"
  },
  "wf_content_hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
  "screen_id": "MEM_REG_TOP",
  "prompt": "Emphasize the primary CTA and keep the form compact",
  "placement_target": "128:9001",
  "auto_confirm": false,
  "design_rule_file_url": "s3://bucket-name/1/3/wf2des/rules/rule.json",
  "structure_file_url": null,
  "detail_design_file_url": null,
  "design_file_url": null
}

SQS Payload Fields

Name Type Required Description
wf2des_id string (uuid) Required = the wf2des row id; echoed as job_id in the webhook. Run identity / fencing pair member
attempt integer Required Monotonic fencing counter; fences DocDB commits (CAS on (_id, attempt)). 1 on trigger
nonce string Required Fresh per enqueue; part of the (job_id, attempt, nonce) webhook dedupe key
wireframe_img_url string (s3://) Required Trigger-time wireframe image snapshot, pre-collected by the backend
wireframe_json_url string (s3://) Required Trigger-time wireframe JSON snapshot (frame + enclosing board section, memos included)
snapshot_scope object Required {frame_node_id, section_node_id} โ€” the frame and its enclosing board section
wf_content_hash string (sha256) Required Snapshot content hash, computed at capture; the parse-cache key
screen_id string Required Screen ID, from the request
prompt string | null Optional Generation prompt (selection input, ranked below memo intent)
placement_target string | null Optional Placement node id, echoed into the result document request block
auto_confirm boolean Required false normal / true auto โ†’ parse + assemble in one worker invocation
design_rule_file_url string (s3://) Optional Pre-collected design rule file URL, for pinning
structure_file_url string (s3://) Optional Pre-collected structure file URL, for pinning
detail_design_file_url string (s3://) Optional Pre-collected detail design file URL, for pinning
design_file_url string (s3://) Optional Pre-collected reference design file URL, for pinning