Skip to content

Confirm Parse (Enqueue Assemble)

Method

This API follows the REST methodology.

HTTP Method

POST: Confirm the parse checkpoint of an interactive generation run and enqueue the assemble phase (or, on the reject path, decline the parse).

This endpoint applies only to the interactive flow (autoConfirm = false). Auto-confirm runs chain parse straight into assemble in a single worker invocation and never reach the confirm checkpoint.

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

Confirm Parse

URI

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

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.

{
  "attempt": 1,
  "parseArtifactHash": "8f14e45fceea167a5a36dedd4bea2543"
}

Request Parameters

Name Type Required Description
attempt integer Required The attempt the plugin previewed. Compare-and-set against the row's attempt; a mismatch means a newer parse superseded the one under review (409).
parseArtifactHash string Required Content hash of the parse.json artifact the plugin previewed. Compared against the current parse artifact before enqueue; a mismatch is 409.

Response

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

{
  "wf2desId": "wf2des-001",
  "status": "0",
  "phase": "2",
  "attempt": 1
}

Response Fields

Name Type Description
wf2desId string wf2des ID
status string Row status after the confirm. '0' processing (assemble enqueued) on confirm; '3' rejected on reject.
phase string | null Generation phase inside status '0'. '2' assemble after a confirm; null (cleared) after a reject.
attempt integer The attempt that was confirmed (echoed).

The endpoint is idempotent. A repeated confirm for a run already advanced to phase '2' assemble (same attempt) returns 200 with the same body and enqueues nothing โ€” the assemble message was already sent on the first call. A repeated reject for a run already at status '3' likewise returns 200 with the reject-shaped body.

Reject Parse

Rejecting is the alternative outcome of the same checkpoint: the designer declines the parse instead of confirming it. This is not a failure โ€” it is a terminal status '3' rejected, distinct from status '2' failed.

The reject is a two-step, detail-first-flag-last sequence:

  1. Detail first. The plugin writes the reject detail to the internal wf2des-api data plane, which persists it into the design_generation_result document's parse.rejected block:
{
  "reasonCode": "wrong_roles",
  "note": "The hero CTA was tagged as a body label."
}
Name Type Required Description
reasonCode string (enum) Required One of wrong_roles, wrong_memos, wrong_sections, other.
note string Optional Free-text explanation. Recommended when reasonCode = other.
  1. Flag last. This endpoint (invoked as the reject variant โ€” request body { "attempt": 1, "reject": true }) then flips the row to status '3' rejected under the same (attempt, phase awaiting_confirm) compare-and-set. Writing the detail before flipping the flag guarantees a status '3' row always has its parse.rejected block present. No assemble message is enqueued on the reject path.

The rejected parse.rejected detail is what a later re-trigger consumes: a fresh parse (attempt + 1) is enqueued and the reject reason travels with it so the worker skips the parse cache (a fresh parse is the point).

Authentication

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

Error Handling

The following status codes are returned for errors.

Description Status Code Status Name
Missing or invalid request body (attempt absent / parseArtifactHash absent on confirm, unknown reasonCode on reject) 400 Bad Request
Missing authentication credentials 401 Unauthorized
Insufficient permissions 403 Forbidden
wf2des, project, or organization not found 404 Not Found
Compare-and-set mismatch: attempt does not match, or the row is not at phase '1' awaiting_confirm (already assembled, terminal, or auto-confirm) 409 Conflict
Internal server error 500 Internal Server Error

On a repeat confirm/reject that matches the already-applied state (same attempt, target phase/status already set), the endpoint returns 200 (idempotent echo), not 409. 409 is reserved for a genuine attempt/phase mismatch โ€” a superseded or wrong-phase run.

Processing Flow

Confirm path

  1. Extract organization ID, project ID, and wf2des_id from path parameters; extract attempt and parseArtifactHash from the request body.
  2. Verify the user has Write access to the project.
  3. Load the wf2des row; if it does not exist (or its project_id does not match), return 404.
  4. Idempotent echo check: if the row is already at phase '2' assemble with the same attempt, return 200 with the confirm body without enqueuing.
  5. Compare-and-set: require status = '0' processing, phase = '1' awaiting_confirm, and the row attempt to equal the request attempt. On mismatch, return 409.
  6. Validate parseArtifactHash against the current parse.json artifact hash for the run; on mismatch, return 409.
  7. Atomically set phase = '2' assemble (row stays status '0'), guarded by the same (attempt, phase '1') condition.
  8. Send the assemble message to the SQS generation queue (snake_case payload โ€” see below).
  9. Return the updated row (status '0', phase '2').

Reject path

  1. Extract path parameters and the reject body (attempt, reject = true).
  2. Verify the user has Write access to the project.
  3. Load the wf2des row; 404 if absent or mismatched project.
  4. Idempotent echo check: if the row is already at status '3' rejected, return 200 with the reject body.
  5. Confirm the reject detail was already written to the design_generation_result document's parse.rejected block via wf2des-api (detail-first-flag-last). If absent, return 400.
  6. Compare-and-set: require status = '0', phase = '1' awaiting_confirm, and matching attempt; 409 on mismatch.
  7. Atomically flip status = '3' rejected and clear phase (NULL). No SQS message is sent.
  8. Return the updated row (status '3', phase null).

Detailed Flowchart

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

    Service --> AccessCheck[Access Check]
    AccessCheck --> HasAccess{Write Access?}
    HasAccess -->|No| Err403[403 Forbidden]
    HasAccess -->|Yes| LoadRow[Load wf2des Row]

    LoadRow --> RowExists{Exists &<br/>project matches?}
    RowExists -->|No| Err404[404 Not Found]
    RowExists -->|Yes| Branch{Reject flag?}

    Branch -->|No confirm| EchoC{Already phase '2'<br/>assemble, same attempt?}
    EchoC -->|Yes| Ok200[200 OK<br/>idempotent echo]
    EchoC -->|No| CASConfirm{status '0' &<br/>phase '1' awaiting_confirm &<br/>attempt matches?}
    CASConfirm -->|No| Err409[409 Conflict]
    CASConfirm -->|Yes| HashCheck{parseArtifactHash<br/>matches parse.json?}
    HashCheck -->|No| Err409
    HashCheck -->|Yes| SetAssemble[CAS set phase '2' assemble<br/>row stays status '0']
    SetAssemble --> SendSQS[Send assemble message<br/>to SQS generation queue]
    SendSQS --> SQSResult{Success?}
    SQSResult -->|No| Err500[500 Internal Server Error]
    SQSResult -->|Yes| SuccessC[200 OK<br/>status '0' / phase '2']

    Branch -->|Yes reject| EchoR{Already status '3'<br/>rejected?}
    EchoR -->|Yes| Ok200
    EchoR -->|No| DetailCheck{parse.rejected detail<br/>written via wf2des-api?}
    DetailCheck -->|No| Err400[400 Bad Request]
    DetailCheck -->|Yes| CASReject{status '0' &<br/>phase '1' awaiting_confirm &<br/>attempt matches?}
    CASReject -->|No| Err409
    CASReject -->|Yes| SetRejected[CAS flip status '3' rejected<br/>clear phase NULL<br/>no SQS]
    SetRejected --> SuccessR[200 OK<br/>status '3' / phase null]

Asynchronous Processing

On the confirm path, the assemble phase runs asynchronously on the SQS worker. The worker rehydrates its input pins from the design_generation_result document's inputs block and writes the terminal spec / selection / validator_report / confidence blocks; it never writes PostgreSQL. Completion is applied backend-side by the ai-status webhook handler, which flips the row:

  • status '0' (processing) / phase '2' (assemble) โ†’ status '1' (completed, phase cleared) or status '2' (failed, phase cleared)

The reject path is terminal at this endpoint โ€” no worker runs, no webhook fires.

SQS Payload

The assemble message sent to the SQS generation queue on the confirm path. Everything else the assemble worker needs is rehydrated from the design_generation_result document's inputs block; the message carries only the run identity, the fencing pair, and the confirm decision.

{
  "wf2des_id": "wf2des-001",
  "attempt": 1,
  "nonce": "b1946ac92492d2347c6235b4d2611184",
  "confirm": {
    "attempt": 1,
    "parse_artifact_hash": "8f14e45fceea167a5a36dedd4bea2543"
  }
}

SQS Payload Fields

Name Type Required Description
wf2des_id string Required wf2des ID (= the PG row id; echoed as job_id in the completion webhook).
attempt integer Required Fencing counter; fences the DocumentDB result-doc write (CAS on (_id, attempt)).
nonce string Required Fresh per enqueue; part of the (job_id, attempt, nonce) webhook dedupe key.
confirm.attempt integer Required The confirmed attempt (the value CAS-matched by this endpoint before enqueue).
confirm.parse_artifact_hash string Required The confirmed parse.json artifact hash (validated by this endpoint before enqueue).

No SQS message is sent on the reject path.