Skip to content

Record Placement (Materialized)

Method

This API follows the REST methodology.

HTTP Method

POST: Record that a wf2des Design Has Been Materialized

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

Record that a wf2des Design Has Been Materialized

URI

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

Path Parameters

Name Type Required Description
organization_id integer Required Organization ID
project_id integer Required Project ID
wf2des_id string Required wf2des ID

Request Body

The request body is JSON. The Figma plugin, having already written the full materializer detail (placed_node_id, materializer_report) to the design_generation_result document's placement block through wf2des-api, calls this endpoint to flip the row-level materializedAt flag. The body carries only the node reference needed to echo the completion back to the caller; the load-bearing detail lives in the DocumentDB placement block, not on the PostgreSQL row.

{
  "placedNodeId": "1204:57"
}

Request Parameters

Name Type Required Description
placedNodeId string Required Figma node id of the frame the plugin built (mirrors placement.placed_node_id in DocumentDB)

Response

The response is JSON (HTTP status: 200 OK).

{
  "projectId": "proj-123",
  "wf2desId": "wf2des-001",
  "status": "1",
  "phase": null,
  "placedNodeId": "1204:57",
  "materializedAt": "2026-07-06T09:35:00Z"
}

Response Fields

Name Type Description
projectId string Project ID
wf2desId string wf2des ID
status string Run status โ€” always "1" (completed) for a materializable row (see Status Codes)
phase string | null Generation phase โ€” already cleared to null on a completed row
placedNodeId string Figma node id of the built frame (echo of the request)
materializedAt string ISO 8601 timestamp the row was flipped to materialized (materialized_at mirror)

A repeated placement call on a row that already carries materializedAt returns 200 OK with the existing timestamp (idempotent โ€” the flag is not re-stamped). The placedNodeId in a repeat call is ignored for the row flip; the authoritative node reference is the one the plugin has already written to the DocumentDB placement block.

Status Codes

status:

Value Meaning
0 processing
1 completed
2 failed
3 rejected (designer declined the parse; NOT failed)
4 cancelled

Only a row at status "1" (completed) is materializable. phase is namespaced inside status "0" and is already null on any terminal row, including a completed one.

Authentication

Authentication is performed using JSON Web Tokens (JWT) issued by Amazon Cognito.

The Figma plugin calls this endpoint with an authenticated session that has Write access to the project. Recording placement is not restricted to the session that triggered the generation โ€” any Write session may confirm that the frame was built. This backend endpoint is distinct from the plugin's data-plane write to wf2des-api (which carries the plugin session token, not the Cognito JWT); the materializer detail reaches DocumentDB through that data-plane path first, and only this flag flip reaches the PostgreSQL row.

Error Handling

The following status codes are returned for errors.

Description Status Code Status Name
Missing authentication credentials 401 Unauthorized
Insufficient permissions 403 Forbidden
wf2des, project, or organization not found 404 Not Found
Internal server error 500 Internal Server Error

Processing Flow

The backend owns the wf2des PostgreSQL row and is the sole writer. Recording placement is a single flag update applied backend-side โ€” the worker holds no PostgreSQL credentials and never participates. This endpoint is the flag in a detail-first-then-flag handshake: the plugin first writes placed_node_id and the seven-event materializer report (name_fallback, ordinal_fallback, build_error, font_fallback, prop_rejected, unmatched, preserved) to DocumentDB, then calls this endpoint. The detail is durable before the flag is stamped.

  1. Extract organization ID, project ID, and wf2des_id from path parameters
  2. Extract placedNodeId from the request body
  3. Verify the user has Write access to the project
  4. Fetch the wf2des row by wf2des_id; if it does not exist (or is soft-deleted), return 404
  5. Verify the row's organization_id and project_id match the path; otherwise return 404
  6. Inspect the current row:
  7. status "1" (completed) with materializedAt already set โ†’ return 200 with the existing timestamp (idempotent, no write)
  8. status "1" (completed) with materializedAt unset โ†’ proceed
  9. Any non-completed status ("0" processing / "2" failed / "3" rejected / "4" cancelled) โ†’ return 404 (no materializable result exists for this row)
  10. Apply the update in one PostgreSQL transaction โ€” set materialized_at to the current timestamp, stamp updated_at / updated_by; status stays "1" and phase stays null
  11. Return the materialized wf2des information

No SQS message is emitted by this endpoint. The generation is already terminal; this call only records that the client-side materialization step completed. It does not read or fetch the DesignSpec โ€” the DesignSpec is a native-Figma spec in the design_generation_result document (DocumentDB), fetched by the plugin through wf2des-api and materialized into the target Figma file; the backend never handles design content here.

Detailed Flowchart

flowchart TD
    Start([POST Request]) --> Auth[Auth & Parameter Extraction]
    Auth --> Service[Service Layer]

    Service --> AccessCheck[Access Check]
    AccessCheck --> HasAccess{Write Access?}
    HasAccess -->|No| Err403[403 Forbidden]
    HasAccess -->|Yes| FetchRow[Fetch wf2des Row<br/>WHERE id = :wf2des_id]

    FetchRow --> RowExists{Exists &<br/>org/project match?}
    RowExists -->|No| Err404[404 Not Found]
    RowExists -->|Yes| StatusCheck{status '1'<br/>completed?}

    StatusCheck -->|No โ€” '0' / '2' / '3' / '4'| Err404
    StatusCheck -->|Yes| MatCheck{materializedAt<br/>already set?}

    MatCheck -->|Yes| Idempotent[200 OK<br/>existing timestamp, no write]
    MatCheck -->|No| Update[Update in one PG txn<br/>materialized_at = now<br/>status '1' / phase null unchanged]

    Update --> Success[200 OK<br/>materialized wf2des info]

Detail-First-Then-Flag Handshake

Placement is recorded in two writes across two surfaces, in a fixed order:

  1. Detail (data plane, wf2des-api). The plugin writes placed_node_id, materialized_at, fingerprint/review fields, and the seven-event materializer_report. This write targets DocumentDB and never touches PostgreSQL. placement.design_area remains worker-written.
  2. Flag (this endpoint, backend). The plugin then calls POST โ€ฆ/wf2des/{wf2des_id}/placement, which flips the row-level materializedAt. The flag is stamped last, so a set materializedAt on the wf2des row guarantees the fuller placement detail is already durable in DocumentDB. GET and list surface materializedAt from the row; the report itself is read from the placement block via wf2des-api.

This ordering keeps the row flag a reliable pointer: consumers polling GET see materializedAt only after the materializer detail has been committed, never before.