---
title: "List Requirements"
method: GET
path: "/marketing/guidelines/requirements"
tags: ["marketing-guidelines"]
---

# List Requirements

`GET /marketing/guidelines/requirements`

The library, filterable, sortable, capped, and — when asked — ranked by its own record.

`collateral_type` and `marketing_team_id` match a requirement whose scope is unset too, because
an unset scope means "applies to everything" — filtering it out would hide the requirements that
most certainly do apply. `preset_id` does not; see below. `file_id` names the standards documents
to read from — strict membership for the same reason `preset_id` is, and narrower: it answers
"which of *this* document's rules are misbehaving", which is how a librarian who knows the
standard they are tuning reaches its rules without paging through the library.

**`order` is a parameter here rather than a route of its own.** Ranking a library by how often
reviewers dismiss what a rule produced is the same *selection* as this list, ordered differently,
and the tuning surfaces need it with every filter above still applying. A parallel route had five
things silently wrong for the same rows: `max_requirements_per_state_change` absent so the bulk
cap read zero against a server that refuses at fifty, three of the five filters dropped while
their chips stayed lit, a thinner row that blanked seven fields `RequirementCard` renders,
truncation copy telling the reader to narrow filters when narrowing is not the remedy, and two
query keys `keepPreviousData` cannot span. One route keeps all five right by construction.

With `order` set, each row carries a `signal` block and rows with no checks in the window are
**absent** — a requirement nothing has been judged against has no track record to rank. With it
unset the query, the plan and the row are exactly what they were before: the aggregates are not
joined, so the ordinary library list pays nothing for this.

**The ranking is applied here rather than in the browser** because the response is one capped
page of whatever matched, not a window onto an ordered list — so a client re-ordering what it was
sent would answer "which rules produce the most false positives" with the worst of an arbitrary
page, silently, on exactly the libraries where the question is worth asking.

## Query parameters

- `state` GuidelineRequirementState[], nullable
- `collateral_type` 'Agent Call Flow' | 'Audio' | 'Billboard' | 'Communications' | 'Conversation Design' | 'Email' | 'Help Center Article' | 'In-App' | 'Mailer' | 'Organic Social' | 'Paid Advertising' | 'Paid Search' | 'Paid Social' | 'Press Release' | 'Procedure/Decision Tree' | 'Support Agent Training' | 'TV' | 'Web' | 'Wiki' | 'Other'
- `marketing_team_id` string, uuid, nullable
- `preset_id` string[], nullable
- `file_id` string[], nullable
- `search` string, nullable
- `order` 'false_positive_rate' | 'dismissal_rate' | 'dismissal_volume' | 'fire_rate' | 'fire_volume' — What a ranking of a company's requirements is ranked *by*. Five orders because the two surfaces ask different questions and each wants a rate and a volume. A rate finds the rule that is usually wrong; a volume finds the rule that is wrong most often. Neither subsumes the other, and offering only one is how a screen ends up either dominated by rules with three data points or blind to a rule that is wrong 40% of the time on hundreds of assets. `FIRE_RATE` applies `MIN_CHECKED_FOR_RATE`; the two verdict rates are held down by `FALSE_POSITIVE_PRIOR_REVIEWS` instead, so a genuinely bad two-of-two stays visible without topping the list. The two volumes need neither: a count is already its own evidence.
- `direction` 'asc' | 'desc' — Which way a sortable list is sorted. Here rather than beside any one feature because a direction is not a property of what is being sorted. `backend/api/vendors.py`, `reviews.py` and `vendor_services.py` each spell it as `Query("asc", pattern="^(asc|desc)$")`; the wire values match, so those can adopt this without a client change, and nothing new should add a fourth spelling (`naming-semantics` N-1).
- `window_days` integer
- `min_checked` integer

## Response `200`

Successful Response

- RequirementListResponse — A page of the library. `truncated` rather than a total: counting the matches for an unanchored `ILIKE` costs the same scan twice, and what a reader needs to know is that there is more, not exactly how much more.
  - `requirements` GuidelineRequirementResponse[], required
    - `requirement_id` string, uuid, required
    - `file_id` string, uuid, required
    - `filename` string, nullable, required
    - `decomposition_id` string, uuid, required
    - `ordinal` integer, required
    - `revision_number` integer, required
    - `state` 'active' | 'disabled' | 'archived' | 'superseded', required — Whether a requirement is in the working library, and if not, why not. All four are recorded as revisions, so every one of them is reversible and readable. ARCHIVED and SUPERSEDED are deliberately distinct: ARCHIVED is a person deciding this obligation does not apply to them, SUPERSEDED is a newer decomposition of the same guideline taking over. A regeneration must never overwrite the first with the second — that would erase a human decision.
    - `requirement_text` string, required
    - `judging_guidance` string, nullable, required
    - `collateral_types` MarketingCollateralType[], nullable, required
    - `marketing_team_ids` string[], nullable, required
    - `source_collateral_terms` string[], nullable, required
    - `trigger_terms` string[], required
    - `unresolved_collateral_terms` string[], required
    - `section_label` string, nullable, required
    - `title` string, nullable, required
    - `source_page` integer, nullable, required
    - `citation` GroundedSpan, required — Where a requirement appears to have come from, and how much to trust it.
      - `page_number` integer, required
      - `boxes` BoundingBox[]
        - `x0` number, required
        - `y0` number, required
        - `x1` number, required
        - `y1` number, required
      - `source_text` string, nullable
      - `matched_terms` string[]
      - `score` number
      - `basis` string
      - `cited_page` integer, nullable
      - `refusal` string, nullable
    - `change_kind` 'extracted' | 'edited' | 'disabled' | 'enabled' | 'archived' | 'restored' | 'superseded', required — What a revision was for. Redundant with comparing a revision to its predecessor, and that is the point: the reason a change was made is the part a diff cannot recover.
    - `change_note` string, nullable, required
    - `updated_at` string, date-time, required
    - `updated_by_email` string, nullable, required
    - `has_human_edits` boolean, required
    - `signal` RequirementSignal — One requirement's track record, as a ranked read reports it. Present only when the list was asked for an `order`; `None` otherwise, so an ordinary library read pays neither the aggregate nor the payload. A row whose requirement has never been checked is absent from a ranked read entirely rather than carrying zeros — there is nothing to rank. **The two halves are in different units and must not be added or compared.** `collateral_checked` and the three `checks_*` count *checks* — one per (evaluation, requirement), which `uq_marketing_requirement_check_once_per_evaluation` guarantees. The `risks_*` count *risks*, and one check can produce several, so `risks_flagged + risks_over_flagged` is not bounded by `checks_failed`. The rates come from the server because the server ordered by them. A client re-deriving them would be a second definition of the number the ranking was built on. `None` means no evidence, which is a different claim from `0.0`.
      - `collateral_checked` integer, required
      - `checks_failed` integer, required
      - `checks_passed` integer, required
      - `checks_not_determined` integer, required
      - `checks_current_revision` integer, required
      - `verdicts` object, required
      - `risks_superseded` integer, required
      - `false_positive_rate` number, nullable, required
      - `dismissal_rate` number, nullable, required
      - `fire_rate` number, nullable, required
    - `downvotes` integer, nullable
  - `truncated` boolean, required
  - `signal_floor` integer, nullable
  - `signal_window_days` integer, nullable
  - `max_requirements_per_state_change` integer, required

## 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-service-production.skmtc.workers.dev/v1/apis/kobaltlabs/fastapi/revisions/3ff4c0bd276b/schema)
