---
title: "Reject a proposal with an immutable review receipt"
method: POST
path: "/optimization-proposals/{proposalId}/reject"
---

# Reject a proposal with an immutable review receipt

`POST /optimization-proposals/{proposalId}/reject`

## Request

- Base URL: `https://api.caveman.so/api/v1`
- URL: `https://api.caveman.so/api/v1/optimization-proposals/{proposalId}/reject`
- Auth: one of:
  - OAuth 2
    - Authorization code: `/api/v1/oauth/authorize`
    - Token URL: `/api/v1/oauth/token` scopes: `proposal:draft`
  - HTTP bearer
  - API key in cookie `cave_access`
  - API key in cookie `__Secure-cave_identity.session_token`

POST /api/v1/optimization-proposals/{proposalId}/reject on the Caveman Cloud control plane. Requires the proposal:draft scope. Not exposed to autonomous agents: Records a human rejection reason and feeds it into optimization learning. Agent access would let the optimizer impersonate its reviewer and poison its own feedback signal. The client supplies one stable canonical UUID per human decision and a closed reason code; exact retries return the same receipt without another transition or audit row.

## Request body

- object
  - `idempotency_key` string, uuid, required: Stable client decision identity. Reuse after an uncertain network result; rotate after success, conflict, cancellation, or a new decision.
  - `reason_code` 'quality_risk' | 'security_risk' | 'scope_too_broad' | 'cost_not_justified' | 'stale_knowledge' | 'human_preference' | 'duplicate_change' | 'unsupported_change' | 'policy_conflict', required

## Response `200`

Proposal rejected, or an exact retry returned the original immutable decision receipt.

- object
  - `id` string, uuid, required
  - `status` 'rejected', required
  - `learning_recorded` boolean, required: Explicit learning outcome; false means no typed learning feedback was recorded.
  - `decision_receipt` object, required
    - `id` string, uuid, required
    - `project_id` string, uuid, required
    - `proposal_id` string, uuid, required
    - `decision_generation` integer, required
    - `decision` 'rejected', required
    - `idempotency_key` string, uuid, required
    - `actor_user_id` string, uuid, required
    - `reason_domain` 'quality' | 'security' | 'scope' | 'cost' | 'stale_knowledge' | 'human_preference' | 'duplicate' | 'unsupported' | 'policy', required
    - `reason_code` 'quality_risk' | 'security_risk' | 'scope_too_broad' | 'cost_not_justified' | 'stale_knowledge' | 'human_preference' | 'duplicate_change' | 'unsupported_change' | 'policy_conflict', required
    - `predecessor_status` 'drafting' | 'proposed' | 'pr_open', required
    - `learning_recorded` boolean, required: True only when the same transaction recorded typed human_rejected feedback against an immutable optimization-technique binding.
    - `receipt_origin` 'api', required
    - `decided_at` string, date-time, required
    - `created_at` string, date-time, required

## Other responses

- `400`: Missing, malformed, non-canonical, unknown, or trailing request input
- `404`: Proposal not found in the caller's tenant scope
- `409`: Illegal predecessor or a mismatched immutable review decision
- `4XX`: Structured failure. `error.code` is stable; `error.request_id` is echoed in the x-cave-request-id header.
- `5XX`: Structured failure. `error.code` is stable; `error.request_id` is echoed in the x-cave-request-id header.

---

[API](https://skmtc.dev/caveman/apis/caveman-cloud-api.md) - [All operations](https://skmtc.dev/caveman/apis/caveman-cloud-api/llms.txt) - [OpenAPI document](https://skmtc.dev/caveman/apis/caveman-cloud-api/revisions/508312d01cfc?raw)
