---
title: "Create Stack Proposal"
method: POST
path: "/api/v1/projects/{project_id}/stack-proposals"
tags: ["stack-proposals"]
---

# Create Stack Proposal

`POST /api/v1/projects/{project_id}/stack-proposals`

## Path parameters

- `project_id` string, uuid, required

## Headers

- `Idempotency-Key` string, uuid, required

## Request body

- StackProposalRequest — POST /projects/{id}/stack-proposals body — the canvas door (spec §5). Composes (never subclasses) PlaceConditionSpec: the ledger stores the bare spec dump and the engine arms re-validate it with extra="forbid".
  - `conversation_id` string, uuid, required
  - `spec` PlaceConditionSpec, required — place_condition_on_markups input — targeting only, never geometry.
    - `source_condition_id` string, uuid, required
    - `target_condition_id` string, uuid, required
    - `scope` PlacementScope, required — Exactly one selector: a page, 2-10 named pages, the whole project, or explicit A markups.
      - `page_id` string, uuid, nullable
      - `page_ids` string[], nullable — 2-10 named sheets, resolved to page ids first. One sheet is page_id; every sheet is project. The 500-placement cap is aggregate across the whole scope.
      - `project` boolean
      - `measurement_ids` string[], nullable
    - `exemplar_measurement_id` string, uuid, nullable

## Response `200`

Idempotent replay or typed no-op

- StackProposalResponse — proposed → receipt carries the pending row (fresh propose also carries names/counts; an idempotent replay carries ONLY the receipt — names/counts are not stored on the row, and outcome describes the row's existence, not its current status). nothing_to_place → no row was written (spec §2) and `reason` drives the client copy: no_source_markups (A has nothing in scope) vs already_placed (every A is covered).
  - `outcome` 'proposed' | 'nothing_to_place', required
  - `reason` 'no_source_markups' | 'already_placed', nullable
  - `receipt` WriteBatchReceipt
    - `batch_id` string, uuid, required
    - `items` ActionReceipt[], required
      - `ordinal` integer, required
      - `action_id` string, uuid, required
      - `status` 'pending' | 'applied' | 'rejected' | 'undone' | 'failed', required
      - `kind` 'create_condition' | 'create_folder' | 'update_condition' | 'update_folder' | 'move_conditions' | 'delete_condition' | 'delete_folder' | 'update_measurement' | 'reassign_measurement' | 'rename_measurement_group' | 'delete_measurement' | 'restore_measurement' | 'create_annotation' | 'update_annotation_status' | 'place_measurements' | 'flag_page_verification' | 'set_page_verification_note', required
      - `entity_type` 'condition' | 'folder' | 'measurement' | 'annotation' | 'page', required
      - `entity_id` string, uuid, nullable
      - `summary` string, required
      - `impact` ActionImpact
        - `measurements` integer
        - `sheets` integer
        - `conditions` integer
        - `folders` integer
      - `error` string, nullable
  - `already_placed` integer, nullable
  - `source_name` string, nullable
  - `target_name` string, nullable

## Other responses

- `201` — Successful Response
- `4XX` — Client Error
- `5XX` — Server Error

---

[API](https://skmtc.dev/rudus/apis/rudus-api.md) · [All operations](https://skmtc.dev/rudus/apis/rudus-api/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/rudus/rudus-api/revisions/f149124a4d80/schema)
