---
title: "Submit Loss Reason"
method: POST
path: "/api/v1/followups/public/loss-reason"
tags: ["followups-public"]
---

# Submit Loss Reason

`POST /api/v1/followups/public/loss-reason`

Confirm POST from the loss page. Idempotent: a repeat submit with a
reason already recorded refuses the overwrite (stale-token protection)
and reports ``already_recorded``.

## Request body

- LossReasonSubmit
  - `token` string, required
  - `reason` 'price_too_high' | 'lead_time_too_long' | 'went_elsewhere' | 'project_cancelled' | 'spec_changed' | 'no_response' | 'other', required — Structured why-we-lost-it taxonomy for quotes (retention MVP). Captured forward-only from three sources (see ``LostReasonSource``); legacy free-text reasons in QuoteAudit JSON are NOT backfilled. Feeds the Customers CRM timeline and, via ``Quote.pricing_bucket_key``, the future pricing-calibration join (PRO-230).
  - `note` string, nullable

## Response `200`

Successful Response

- LossReasonResult
  - `recorded` boolean, required
  - `already_recorded` boolean, required

## Other responses

- `422` — Validation Error

---

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