> ## 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.

# AB 660 / Date Labels

> How Closient renders GS1 date Application Identifiers (15/16/17/7003) as consumer-facing strings under California AB 660, and how the Products API enforces banned-phrase validation.

California [AB 660](https://leginfo.legislature.ca.gov/faces/billNavClient.xhtml?bill_id=202320240AB660) takes effect **July 1, 2026**. It is the first US law to mandate standardized date labeling on packaged food sold in California: only two consumer-facing phrases are permitted, and a long list of legacy phrases ("Sell By", "Best Before", "Expires On", etc.) is banned from consumer surfaces. See the [CDFA implementation page](https://www.cdfa.ca.gov/is/foodrecovery/fooddatelabeling/) for the regulator's overview.

This page documents the rendering contract Closient applies on resolver destination pages and via the SDK helper, the registration-API validation that rejects banned phrases on input, and the exemptions you should know about.

> Other states (NJ, IL, MA, SC) have similar bills in flight. We treat California as the floor and assume other states will follow — the contract below is designed to remain valid as additional states adopt the same phrasing.

## GS1 AI to consumer phrase mapping

The GS1 General Specifications define several Application Identifiers (AIs) for date data. Under AB 660, only three of them may be rendered to consumers, and each maps to a specific approved phrase. AI (16) "Sell By" remains legal as **encoded/coded data only** and is never rendered on a consumer-facing surface.

| AI     | GS1 meaning          | Consumer phrase (default) | Consumer phrase (freezable)        | Audience                     |
| ------ | -------------------- | ------------------------- | ---------------------------------- | ---------------------------- |
| `15`   | Best Before Date     | `Best if Used By {date}`  | `Best if Used or Frozen By {date}` | Consumer (quality)           |
| `16`   | Sell By Date         | **Never rendered**        | **Never rendered**                 | Business only — encoded data |
| `17`   | Use By Date          | `Use By {date}`           | `Use or Freeze By {date}`          | Consumer (safety)            |
| `7003` | Expiration Date/Time | `Use By {date} {time}`    | `Use or Freeze By {date} {time}`   | Consumer (regulated)         |

Dates render as ISO 8601 short form (`YYYY-MM-DD`); datetimes render as `YYYY-MM-DD HH:MM`. AB 660 does not prescribe a date format, so we use the safest unambiguous default for a multinational catalog. Brands rendering on physical packaging will typically reformat in their own print pipeline — the rendered string here is for the resolver destination page and the forthcoming SDK helper.

The longer "or Frozen" / "or Freeze" phrasing is permitted by AB 660 for freezable products and is opt-in via the `freezable` flag in the SDK helper.

### What happens to AI (16) "Sell By"

AI (16) remains a perfectly valid GS1 AI and is still useful internally for retail rotation, shrink management, and traceability surfaces. Closient accepts AI (16) on the encoded data layer (linkset, EPCIS, master-data integrations) and continues to surface it on business and regulator link types. It is **stripped from any response payload targeted at a consumer audience**, and the SDK `format_date_label` helper returns `null` when called with AI (16) so a brand who accidentally pipes it to a consumer-facing UI gets nothing, not a banned string.

## Banned-phrase validation on the Products API

To stop banned date-label strings from entering the catalog in the first place, the Products API scans free-text fields on registration and update. Submitting a banned phrase returns **HTTP 422** with the offending field name and the matched text.

### Which fields are scanned

* `POST /v1/products` — scans `product_name`, `description`, `net_content`.
* `PATCH /v1/products/{gtin}` — scans `product_name`, `description`, `net_content`, `usage_instructions` (only the fields the caller actually sends).

### Banned phrases (case-insensitive, word-boundary anchored)

`sell by`, `sell-by`, `best before`, `best-before`, `best by`, `best-by`, `expires on`, `expires by`, `expiration date`, `freshest by`, `freshest-by`, `enjoy by`, `enjoy-by`, `fresh until`, `good through`, `good-through`, `good til`, `good till`, `guaranteed fresh`, `use before`, `use-before`, `display until`, `pull date`, `purge date`.

The list is illustrative, not exhaustive — AB 660 bans any phrase that conveys the same meaning. We catch the high-volume offenders here and rely on human review of edge cases.

The match is **case-insensitive** and respects **word boundaries on both sides**, so brand names like `"Sellby Sushi Co."` and `"Bestseller Bypass"` pass cleanly. Hyphens and whitespace are treated interchangeably inside the phrase: `sell by`, `sell-by`, and `sell  by` all match.

### Example 422 response

A banned phrase in `description` on `POST /v1/products`:

```json theme={null}
{
  "detail": "Field 'description' contains banned date-label phrase 'sell by' (California AB 660). Use one of the approved phrases ('Best if Used By' or 'Use By'), or remove the date label from this field."
}
```

The error message identifies the field and the matched substring so you can correct the input and retry. See [Errors](/errors) for the standard error envelope and best practices for handling 422 responses.

### Recommended fix

* Remove the date label from free-text fields entirely. Date information belongs on its own structured AI field (15/17/7003), not embedded in `description` or `product_name`.
* If you must include a human-readable date in free text (rare), use one of the approved phrases: `Best if Used By {date}` or `Use By {date}`.

## `date_label_compliance` field

Every product response on `GET /v1/products/{gtin}` (and the list/claim/unclaim/PATCH variants) carries a `date_label_compliance` object surfacing a per-product AB 660 readiness summary:

```json theme={null}
{
  "date_label_compliance": {
    "ca_ab660": "compliant",
    "federal": "unknown",
    "notes": []
  }
}
```

Values:

| Field      | Meaning                                                                                                                                                |
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `ca_ab660` | `compliant` (no banned phrases in stored free-text fields), `non_compliant` (one or more hits), or `unknown` (scan could not run).                     |
| `federal`  | Reserved. No federal date-label rule is in force today; returns `unknown`. Bipartisan interest exists, so the field is in place for the day one ships. |
| `notes`    | Empty when compliant; otherwise a list of `"field_name: 'matched phrase'"` strings identifying which fields tripped the check.                         |

The field is computed on read by scanning the four stored free-text fields (`product_name`, `description`, `net_content`, `usage_instructions`) using the same banned-phrase scanner the registration API rejects with, so the GET-time signal and the POST/PATCH-time validation can never disagree. Historical records that pre-date AB 660 enforcement will surface as `non_compliant` here even though they were accepted at write time — use the `notes` to drive an internal cleanup pass.

## Exemptions

AB 660 does not apply to every food category. The following are explicitly exempt from the standardized phrasing requirement; brands selling these categories may continue to use legacy phrasing on the regulated surface:

* **Eggs** (shell eggs and egg products)
* **Infant formula**
* **Beer and other malt beverages**
* **USDA-regulated poultry products**

Closient still applies the banned-phrase scan to **all** product registrations regardless of category, because the catalog spans multiple jurisdictions and the consumer-facing resolver page is rendered everywhere. If you sell an exempt category and need to override the scan for a specific field, contact [support@closient.com](mailto:support@closient.com).

## Related

* [Errors](/errors) — standard error envelope, including the 422 returned by the banned-phrase check.
* [GS1 Digital Link Resolution](/guides/digital-link-resolution) — how AI (15/17/7003) date data flows through the resolver.
* [US State Ingredient Disclosure](/compliance/us-state-ingredient-disclosure) — sibling state-law compliance pattern (LA SB 14, TX SB 25); different mechanism, same compliance-as-config approach.
* [AB 660 bill text](https://leginfo.legislature.ca.gov/faces/billNavClient.xhtml?bill_id=202320240AB660) (California Legislature)
* [CDFA: Food Date Labeling](https://www.cdfa.ca.gov/is/foodrecovery/fooddatelabeling/) (regulator implementation page)
