---
title: "Suggest Requirement"
method: POST
path: "/marketing/requirement-tuning/requirements/{requirement_id}/suggestions"
tags: ["marketing-requirement-tuning"]
---

# Suggest Requirement

`POST /marketing/requirement-tuning/requirements/{requirement_id}/suggestions`

## Request

- Base URL: `https://api.kobaltlabs.com` (the document declares no server; this is the origin it was published from)
- URL: `https://api.kobaltlabs.com/marketing/requirement-tuning/requirements/{requirement_id}/suggestions`
- Auth: none declared

Propose a rewording from this rule's own record. Writes nothing.

**Awaited, unlike a test.** One call over text the route assembled — no document to attach, no
fan-out — so it answers in a few seconds, which is the shape the surface wants: a reviewer presses
Suggest and reads a proposal, then presses Test and watches it drain.

**The evidence is read here, not accepted from the client.** The dismissals with their reasons and
notes, the findings to keep firing on, and the previous tests with the judge's own words. A
client-supplied evidence blob would be prompt-injection surface with nothing to check it against,
and a proposal a reviewer cannot trace back to a dismissal they recognise is one they have to take
on faith.

Both sides always go in. Asking only about the dismissals produces a rule narrowed until it fires
on nothing, which scores perfectly and has stopped doing its job.

**The session is closed before the call, and the call is raced.** Every field the prompt needs is
a primitive by the time `SuggestionEvidence` is built, and underneath `propose_requirement` sits
`run_structured_with_usage`'s unlowerable budget — four attempts with 8-64s backoff on a client
with a ten-minute timeout. Holding a pooled connection across that, on a web engine of
`pool_size=5, max_overflow=24, pool_timeout=5`, is how five reviewers pressing Suggest take every
route down for every tenant; and with no race, the request itself can outlive any proxy in front
of it. The test path was shaped this way from the start (`helpers/requirement_tests`); this is the
same discipline on the awaited half.

## Path parameters

- `requirement_id` string, uuid, required

## Request body

- SuggestRequirementRequest: Propose a rewording, from what reviewers did and what the last tests found.
  - `draft` RequirementDraft: A wording and scope nobody has saved yet. Every field is required rather than optional-with-carry-forward, unlike the PATCH this precedes. A test has to judge *something specific*, and a payload that omitted the scope would silently inherit the live rule's — so a reviewer who had narrowed the collateral types on screen would be shown a result for the scope they were replacing.
    - `requirement_text` string, required
    - `judging_guidance` string, nullable
    - `trigger_terms` string[]
    - `collateral_types` MarketingCollateralType[], nullable
    - `marketing_team_ids` string[], nullable
  - `goal` 'reduce_false_positives' | 'reduce_over_firing': Which way a suggested rewording should move the rule. The two surfaces arrive with different complaints and the same rule must not be rewritten the same way for both. `REDUCE_FALSE_POSITIVES` is asked to keep every finding the reviewer confirmed while losing the ones they dismissed — a narrowing that also loses the confirmations is a regression the reviewer cannot see from the wording alone. `REDUCE_OVER_FIRING` is asked about a rule that fires on nearly everything, where the fix is usually scope or trigger terms rather than the obligation itself.
  - `notes` string, nullable

## Response `200`

Successful Response

- RequirementSuggestion: A proposed rewording. Nothing is written — the reviewer applies it through the ordinary PATCH. `changes` is what the surface renders under each field it touched, as the provenance line the design asks for: *"Added: internal ticket references exempt — from 19 dismissals as Already addressed"*. One entry per field the model changed, in its own words, naming the evidence it came from. A proposal a reviewer cannot trace back to a dismissal is one they have to take on faith.
  - `requirement_text` string, required
  - `judging_guidance` string, nullable
  - `trigger_terms` string[]
  - `collateral_types` string[], nullable
  - `changes` string[]
  - `rationale` string, required
  - `rule_change_needed` boolean
  - `dismissals_considered` integer
  - `confirmations_considered` integer
  - `tests_considered` integer

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/kobaltlabs/apis/fastapi.md) - [All operations](https://skmtc.dev/kobaltlabs/apis/fastapi/llms.txt) - [OpenAPI document](https://skmtc.dev/kobaltlabs/apis/fastapi/revisions/8587332d5e43?raw)
