Skip to main content

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.

Search that respects the user’s persistent trait preferences — allergens they must avoid, dietary stances (vegan, kosher, halal), religious constraints, and sourcing preferences (organic, fair-trade, local).

When to use

  • User has a preference profile (explicitly set or inferred) and searches without restating “and no nuts please” each time.
  • Any recipe / meal / grocery workflow where preferences should be defaulted.

Preferences model

Closient tracks a user’s TraitPreference list — each entry is:
  • trait — lower-snake-case slug, e.g. peanuts (allergen), vegan (dietary), kosher (religious). Pass directly into search constraints.
  • stanceAVOID / REQUIRE / PREFER / NEUTRAL
  • category — ALLERGEN / DIETARY / RELIGIOUS / SOURCING

Flow (end-to-end)

1. Read user preferences

GET /account/api/v1/preferences
Authorization: Bearer <token>
Returns the caller’s full preference list as JSON:
[
  {"trait": "peanuts", "trait_label": "Peanuts", "category": "ALLERGEN", "stance": "AVOID"},
  {"trait": "vegan",   "trait_label": "Vegan",   "category": "DIETARY",  "stance": "REQUIRE"},
  {"trait": "organic", "trait_label": "Organic", "category": "SOURCING", "stance": "PREFER"}
]
Fresh accounts return 200 + []. Read-only — writes still happen through the dashboard’s HTMX form at /preferences/.

2. Translate preferences into constraints

CategoryStanceGoes into
ALLERGENAVOIDexclude_allergens
DIETARYREQUIRErequire_certifications
RELIGIOUSREQUIRErequire_certifications
SOURCINGREQUIRErequire_certifications
ANYPREFERrank boost (client-side)
ANYNEUTRALignored
The unified surface accepts the three constraint sets directly — either as list query params on GET /search/api/v1/search or as constraints.* keys on POST /search/api/v1/search/session:
GET /search/api/v1/search?q=oat+milk
  &exclude_allergens=peanuts
  &require_certifications=vegan
  &require_certifications=organic
POST /search/api/v1/search/session
{
  "query": "oat milk",
  "latitude": 30.267,
  "longitude": -97.743,
  "constraints": {
    "exclude_allergens":       ["peanuts"],
    "require_certifications":  ["vegan", "organic"],
    "avoid_substances":        ["sucralose"]
  }
}
The session route also picks up the same slugs from natural-language phrasing — "avoid peanuts", "vegan certified", "no sucralose" — so unauthenticated callers without a stored preference list still get the filter for free.

4. Read the trait fields on each result

SearchResultOut carries the fields inline so no per-candidate round-trip is needed:
  • certifications: string[] — certification slugs the product holds (unions CertificationAssignment codes with non-allergen Trait links — DIETARY / RELIGIOUS / SOURCING).
  • contains_allergens: string[] — allergen slugs detected on the product (unions allergen-typed substance composition with explicit ALLERGEN Trait links).
  • substance_flags: string[] — substances that matched the current request’s avoid_substances filter, so the UI can show “flagged because X”.
  • allergen_data_confidence: "high" | "medium" | "low"
    • high: explicit substance composition matched (we know what’s in it).
    • medium: explicit allergen Trait link only (asserted, not compositional).
    • low: neither signal — treat missing data as not clean.

Guidance for agents

  • AVOID always beats PREFER — never recommend something that fails an AVOID constraint.
  • Trust high/medium confidence; question low. When the user has a hard avoid constraint and the row’s confidence is low, drop the candidate or ask the user instead of recommending blindly.
  • Surface the rationale — “Showing Product X because it’s certified vegan and avoids peanuts per your preferences.”
  • Ask about gaps, don’t silently guess. If the user hasn’t set a preference relevant to a tricky product (e.g. glycerin for strict vegans), ask before deciding for them.
  • local-product-search — the base search this skill wraps
  • find-alternative — when a result fails a constraint, use this to substitute