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

# Concepts

> The data model in one page.

Five primitives. Every API call works on one of them. See [Models](/models) for the three-tier compatibility-model architecture that powers `/match`.

<CardGroup cols={2}>
  <Card title="User" icon="user">
    A row keyed by `user_id`, scoped to a domain. Structured fields plus dense vectors.
  </Card>

  <Card title="Schema" icon="table">
    The typed shape of a user in a domain. Hard, soft, and derived fields.
  </Card>

  <Card title="Match" icon="filter">
    Pipeline that takes a user plus filters and returns ranked candidates.
  </Card>

  <Card title="Feedback" icon="repeat">
    Outcome labels that train the ranker.
  </Card>
</CardGroup>

## Users

A `user_id` plus `domain` uniquely identifies a record. Two ways to write:

* **`POST /users`** is an upsert. You send the full payload (raw `context`, pre-built `fields`, or a `memory_ref` to an external store) and we replace or create. Right for initial intake and one-shot uploads.
* **`POST /context`** is an incremental append. You send one new piece of context (a chat message, a voice transcript chunk, a behavior event) and we merge it into the existing representation. Right for anything that streams.

Reading is `GET /users/{user_id}` and returns the full representation: structured fields, derived attributes, dense vectors per domain, and provenance.

<Tip>
  The same physical person can have one row per domain. A user in your hiring product and a user in your dating product are different rows, with different schemas and different embeddings.
</Tip>

## Schemas

A schema declares what to extract from incoming context for a given domain. Three kinds of field:

<AccordionGroup>
  <Accordion title="Hard fields" icon="lock">
    Strict types. Used as `WHERE` clauses in the SQL filter stage. Examples: `age`, `gender`, `location`, `salary_min_usd`.
  </Accordion>

  <Accordion title="Soft fields" icon="waveform">
    Free-text fields that feed the domain-adapted embedding head. Examples: `values_text`, `motivation_text`, `thesis_text`.
  </Accordion>

  <Accordion title="Derived fields" icon="wand-sparkles">
    Computed at ingest from raw input. Examples: `attachment_style` from intake transcript, `tenure_predicted` from CV trajectory, `photo_aesthetic_vector` from vision model.
  </Accordion>
</AccordionGroup>

Schemas are co-designed with you at onboarding, then versioned. You can inspect the current schema for a domain with `GET /schema?domain=...`.

## Matches

`POST /match` runs the pipeline:

```mermaid theme={"dark"}
graph LR
    A[All candidates] -->|SQL filter| B[10K]
    B -->|Light ML| C[500]
    C -->|Dense embed| D[50]
    D -->|Cross-encoder| E[10]
    E -->|LLM judge| F[Ranked output]
```

Each stage can be toggled or tuned via the `stages` object on the request:

```json theme={"dark"}
{
  "user_id": "u_alice",
  "domain": "dating",
  "stages": {
    "llm_judge": true,
    "cross_encoder": { "enabled": true, "top_k": 25 },
    "light_ml": { "min_score": 0.3 }
  },
  "explain": true
}
```

Pass `true` or `false` as a shortcut, or pass `{ "enabled": ..., "top_k": ..., "min_score": ..., "model": ... }` for finer control. Set `explain: true` to get per-candidate `stage_scores` and human-readable reasons so you can attribute any decision back to the stage that made it.

| Stage           | Per-candidate cost | Typical survivors |
| --------------- | ------------------ | ----------------- |
| SQL filter      | microseconds       | 10,000            |
| Light ML        | milliseconds       | 500               |
| Dense embedding | sub-ms (ANN)       | 50                |
| Cross-encoder   | \~10 ms            | 10                |
| LLM judge       | seconds            | 5                 |

## Feedback

`POST /feedback` accepts outcome labels (`accepted`, `rejected`, `converted`, `expired`, or a tenant-registered label) plus an optional `weight`. Outcomes accumulate into training triplets. Past a per-domain threshold, the ranker auto-fine-tunes. Use `POST /train` with `force=true` to skip the threshold.

<Tip>
  **Latency of the loop.** Fast signals (like a click) update the reranker continuously. Slow signals (like a 12-month hire tenure) update the embedding head on a longer cadence and weigh more heavily when they land.
</Tip>

## Where context comes from

You decide.

<CardGroup cols={2}>
  <Card title="Raw text on intake" icon="message">
    Pass `context` to `POST /users`. The platform extracts fields against your domain schema.
  </Card>

  <Card title="Structured fields" icon="table-cells">
    Pass `fields` directly. Skips extraction.
  </Card>

  <Card title="Memory reference" icon="database">
    Pass `memory_ref` pointing at an external store you operate (Jean Memory, Mem0, your own vector DB).
  </Card>

  <Card title="Streaming context" icon="waveform">
    `POST /context` for chat messages, transcripts, behavior events. The platform merges incrementally without you re-sending the full user.
  </Card>

  <Card title="Voice or chat intake" icon="phone">
    Optionally, we run a short intake (LLM-driven call or web widget) on your behalf and ingest the transcript.
  </Card>
</CardGroup>
