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
AuthorizationContent-TypeAcceptAccept-language
Response Headers
Content-Type
Confirm Parse
URI
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.
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).
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:
- Detail first. The plugin writes the reject detail to the internal
wf2des-apidata plane, which persists it into thedesign_generation_resultdocument'sparse.rejectedblock:
| 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. |
- Flag last. This endpoint (invoked as the reject variant โ request body
{ "attempt": 1, "reject": true }) then flips the row tostatus '3'rejected under the same(attempt, phase awaiting_confirm)compare-and-set. Writing the detail before flipping the flag guarantees astatus '3'row always has itsparse.rejectedblock 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
- Extract organization ID, project ID, and
wf2des_idfrom path parameters; extractattemptandparseArtifactHashfrom the request body. - Verify the user has Write access to the project.
- Load the
wf2desrow; if it does not exist (or itsproject_iddoes not match), return 404. - Idempotent echo check: if the row is already at
phase '2'assemble with the sameattempt, return 200 with the confirm body without enqueuing. - Compare-and-set: require
status = '0'processing,phase = '1'awaiting_confirm, and the rowattemptto equal the requestattempt. On mismatch, return 409. - Validate
parseArtifactHashagainst the currentparse.jsonartifact hash for the run; on mismatch, return 409. - Atomically set
phase = '2'assemble (row staysstatus '0'), guarded by the same(attempt, phase '1')condition. - Send the assemble message to the SQS generation queue (
snake_casepayload โ see below). - Return the updated row (
status '0',phase '2').
Reject path
- Extract path parameters and the reject body (
attempt,reject = true). - Verify the user has Write access to the project.
- Load the
wf2desrow; 404 if absent or mismatched project. - Idempotent echo check: if the row is already at
status '3'rejected, return 200 with the reject body. - Confirm the reject detail was already written to the
design_generation_resultdocument'sparse.rejectedblock viawf2des-api(detail-first-flag-last). If absent, return 400. - Compare-and-set: require
status = '0',phase = '1'awaiting_confirm, and matchingattempt; 409 on mismatch. - Atomically flip
status = '3'rejected and clearphase(NULL). No SQS message is sent. - 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,phasecleared) orstatus '2'(failed,phasecleared)
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.