> ## Documentation Index
> Fetch the complete documentation index at: https://docs.closient.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Check FSMA 204 Readiness

> Audit one or more GTINs for FSMA 204 (FDA Food Traceability Rule, 21 CFR § 1.1320) Key Data Element coverage by walking the captured EPCIS event ledger and the materialized Critical Tracking Event table for the authenticated organization.

FSMA 204 (the FDA Food Traceability Rule, 21 CFR § 1.1320) requires
that any entity handling a food on the Food Traceability List (FTL)
maintain Key Data Elements (KDEs) for each Critical Tracking Event
(CTE) — harvest, cooling, initial pack, first land-receive,
shipping, receiving, transformation. § 1.1455(c) further requires
those records be furnished to FDA within 24 hours of request. This
skill walks an agent through auditing one or more GTINs for KDE
coverage by reading the EPCIS event ledger and the materialized
CTE rows that Closient produces automatically when FSMA-relevant
events are captured.

## When to use

* A brand owner needs to know "are we FSMA 204-compliant for batch X?"
  before the Jan 20, 2026 enforcement date — and afterward, every day.
* Before issuing a recall, audit which lots have enough KDE coverage
  to construct the FDA Sortable Spreadsheet export.
* A 3PL onboarding a new shipper needs to gap-analyze their KDE
  posture before contracting.
* Continuous-readiness dashboards.

For the FDA Sortable Spreadsheet *export* (the artifact you furnish
under § 1.1455(c)), use the EPCIS endpoint
`GET /epcis/api/2.0/traceability/lots/{lot_id}/fda-export`. This skill
is the gap-analysis step that runs *before* the export to flag missing
KDEs.

## Auth

OAuth token (or `X-API-Key`) with `OWNER` or `MANAGER` membership on
the organization that owns the GTINs being audited.

## Flow

**Note**: The dedicated `/compliance/api/v1/products/{gtin}/fsma204-readiness`
endpoint called out in the original ticket scope does NOT yet exist.
Until it ships (see **Known gaps**), an agent audits readiness by
composing existing queries — described in Steps 1-4 below.

### Step 1 — Resolve GTINs to lots

For each GTIN you want to audit, list the lots that have any EPCIS
activity:

```http theme={null}
GET /epcis/api/2.0/events?MATCH_anyEPC=https%3A%2F%2Fid.gs1.org%2F01%2F{gtin14}
```

Use the prefix match (no trailing `/10/{lot}`) — the server matches
EPC URIs by exact string, so a prefix-trimmed URI captures every lot
under the GTIN. Collect distinct `epcList` values from the response,
parse the AI(10) lot segment off each one.

Alternatively, pull lots directly from the products API:
`GET /products/api/v1/products/{gtin}/lots` (if exposed for the org).

### Step 2 — Audit CTE coverage per lot

For each lot, ask the EPCIS ledger which bizSteps were observed:

```http theme={null}
GET /epcis/api/2.0/events?MATCH_epc=https%3A%2F%2Fid.gs1.org%2F01%2F{gtin14}%2F10%2F{lot}
```

Pull the distinct `bizStep` values from the response. Compare against
the FSMA 204 CTE set expected for the product's food category. The
generic checklist:

| Food category                 | Required CTEs (per § 1.1320 subpart and FTL row)                                 |
| ----------------------------- | -------------------------------------------------------------------------------- |
| Fresh produce (FTL Cat 1)     | `harvesting` → `cooling` → `packing` → `shipping` → `receiving`                  |
| RTE foods (FTL Cat 9)         | `packing` (initial) → `shipping` → `receiving`                                   |
| Fishery products (FTL Cat 17) | `accepting` (LAND\_RECEIVE) → `shipping` → `receiving`                           |
| Cheese / dairy (FTL Cat 6)    | `packing` → `shipping` → `receiving`                                             |
| Eggs, in shell (FTL Cat 7)    | `packing` → `shipping` → `receiving`                                             |
| Generic processed             | `shipping` → `receiving` (no INITIAL\_PACK if the lot wasn't packed by this org) |

A lot missing a required bizStep has a KDE gap.

### Step 3 — Audit KDE completeness per CTE

For each captured EPCIS event in the lot's chain, verify that the
KDEs FSMA 204 expects for that CTE type are present. Required KDEs by
CTE type (per FDA Sortable Spreadsheet template):

| CTE type       | Required KDEs                                                                           |
| -------------- | --------------------------------------------------------------------------------------- |
| `HARVEST`      | Lot code, growing area, harvest start date, harvest end date, location (GLN).           |
| `COOLING`      | Lot code, cooling date, location (GLN), reference document (PO/BOL).                    |
| `INITIAL_PACK` | Lot code, packing date, quantity, location (GLN), reference document.                   |
| `LAND_RECEIVE` | Lot code, vessel name, vessel identifier (USCG), species, landing date, location (GLN). |
| `SHIP`         | Lot code, quantity, ship date, ship-to location (GLN), trading partner.                 |
| `RECEIVE`      | Lot code, quantity, receive date, ship-from location (GLN), trading partner.            |
| `TRANSFORM`    | Input lots, output lots, transform date, location (GLN), reference document.            |

Closient extracts these KDEs from the EPCIS `ilmd` block (Step 7 of
`capture-epcis-event`) and the resolved trading-partner / location
records. An event with `bizStep=harvesting` but no `cbvmda:harvest_start_date`
in `ilmd` is a KDE gap.

### Step 4 — Compose a readiness score

A per-GTIN readiness score is a simple two-dimensional rubric:

```
score = (cte_coverage_pct + kde_coverage_pct) / 2
```

Where:

* `cte_coverage_pct` = (CTEs observed for the lot ∩ CTEs required for
  the food category) / required.
* `kde_coverage_pct` = (KDE fields populated across captured CTEs) /
  expected KDE fields across those CTE types.

A lot at \< 80% is unlikely to satisfy a § 1.1455(c) request.

## Required inputs

* `gtin` — GTIN-8 / 12 / 13 / 14 (server zero-pads shorter forms).
* `lot` (when scoping to a specific batch).

## Optional inputs

* `food_category` — to pick the correct CTE checklist. Closient infers
  this from `Product.product_category` when it's set.
* `date_range` — bound the audit to a recent window (e.g. last 365
  days of shipments).

## Output

The composed-from-existing-queries form is a list of structured rows,
one per (GTIN, lot):

```json theme={null}
[
  {
    "gtin": "00614141123456",
    "lot": "LOT-2026-042",
    "food_category": "leafy_greens",
    "cte_coverage_pct": 0.83,
    "kde_coverage_pct": 0.71,
    "score": 0.77,
    "missing_ctes": ["COOLING"],
    "missing_kdes": {
      "HARVEST": ["growing_area"],
      "INITIAL_PACK": ["reference_document"]
    },
    "links": {
      "capture_missing": "https://www.closient.com/compliance/lots/<lot_short_id>/capture",
      "fda_export": "/epcis/api/2.0/traceability/lots/<lot_short_id>/fda-export"
    }
  }
]
```

When the dedicated readiness endpoint ships, it will emit this exact
shape.

## Guidance for agents

* **Run the audit on the materialized CTE table, not raw events,
  where you can.** The `CriticalTrackingEvent` model (see
  `apps.epcis.models`) is the authoritative source of CTE rows —
  EPCIS events that didn't produce a CTE (because the bizStep
  isn't FSMA-relevant) are noise for this audit. The
  `/traceability/lots/{lot_id}/fda-export` endpoint already does
  this filtering; reading its 202-pending response tells you which
  CTEs exist for the lot.
* **Don't conflate "lot exists" with "lot is ready".** A lot with
  zero EPCIS events is `0.0` readiness, not "no data". The product
  exists in the catalog; FSMA 204 requires CTEs.
* **Lock-state matters.** Once a `ProductLot` has been used in a CTE,
  its `LOCKED_FIELDS` (lot code, harvest dates, expiration) are
  immutable. Don't propose a fix that requires editing those
  fields — instead, the agent should surface a "capture a corrective
  event with the missing KDE in ILMD" workflow (calls
  `capture-epcis-event` with the right bizStep + ilmd block).
* **Audit BY ORGANIZATION's role in the chain.** A brand owner who
  only packs (not harvests) is not on the hook for HARVEST KDEs —
  those belong to the grower upstream. The CTE checklist in Step 2
  is conditional on the org's role; for a packer-only, only
  `INITIAL_PACK` + `SHIP` are owed.
* **Surface the fix path, not just the gap.** A missing `growing_area`
  KDE on a HARVEST event is fixable by capturing a corrective
  `bizStep=harvesting` event with the right `cbvmda:growing_area` —
  hand the agent the exact endpoint and JSON shape it needs.
* **Don't recompute on every page render.** When the dedicated
  endpoint ships, it will cache the per-(GTIN, lot) score for a
  short TTL. Until then, audits cost ≥ 2 EPCIS queries per lot —
  batch them.

## Known gaps (planned)

* **No dedicated readiness endpoint.** The
  `/compliance/api/v1/products/{gtin}/fsma204-readiness` endpoint
  called out in the ticket scope does NOT exist today. Filed as
  a follow-up: a real endpoint that walks CTE + KDE coverage and
  emits the structured shape above, with a 60s server-side cache.
  Until then, agents must compose the audit from
  `GET /epcis/api/2.0/events` queries as described above.
* **No food-category-aware CTE checklist on the server.** The
  expected-CTE-by-FTL-category table lives in this skill doc rather
  than as a server-side rule. When the readiness endpoint ships, the
  checklist will move to the server and the skill will reference it
  by URL.
* **No bulk-GTIN readiness scan.** Agents auditing dozens of GTINs
  have to fan out queries. A bulk endpoint
  (`POST /compliance/api/v1/fsma204-readiness/scan` taking a GTIN
  list) is planned for the FSMA dashboard refactor.
* **Trading-partner KDE coverage is not enforced.** The audit assumes
  `SHIP`/`RECEIVE` CTEs already have a resolved `TradingPartner`
  destination; if the event captured but the partner GLN couldn't
  be resolved, the CTE row exists with `trading_partner=NULL` and
  is silently 100% on the KDE rubric. A separate "trading-partner
  resolution" health check is filed.
* **No historical-snapshot scoring.** The audit is point-in-time
  (current state of the ledger). FSMA 204 § 1.1455(c) responses
  require historical snapshots ("what KDEs did you have on date X"),
  which is reconstructable from the immutable event ledger but not
  yet surfaced as a first-class query.

## Related skills

* `capture-epcis-event` — the corrective-action surface; agents
  capture missing-KDE events to close gaps surfaced by this audit.
* `query-epcis-events` — the underlying read used by this audit
  until the dedicated endpoint ships.
* `manage-recall` — pre-recall sanity check; only issue recalls on
  lots whose readiness score is high enough to compose a defensible
  FDA Sortable Spreadsheet.
* `check-recalls` — consumer side of recalls; orthogonal to
  readiness but reads the same CTE ledger.
