# Context API: entity context

Context API returns a size-bounded pack of current state, actions, properties, and evidence for a client, subject, operator, or skill group. There is one request and response contract. Do not send `version`: the API rejects it. Reading requires a ready context store and does not fall back to the former `facts` response. The API is not connected to AI features automatically.

## Endpoint

```http
POST /kw/chatrooms/context/retrieve
Authorization: Bearer <access-token>
Content-Type: application/json
```

The instance and user are taken from the current authorized session. An instance identifier supplied in the request body cannot grant access to another instance.

## Read context

```json
{
  "scope": { "entity": "client", "id": 10004 },
  "sections": ["state", "actions", "properties", "evidence"],
  "targets": ["previous_client_appeals", "previous_client_conversations"],
  "actions": { "limit": 20, "actorTypes": ["client", "operator"] },
  "allowPartial": true
}
```

Supported scopes and sections:

| `scope.entity` | Sections |
|---|---|
| `client` | `state`, `actions`, `properties`, `evidence` |
| `subject` | `properties`, `evidence` |
| `operator` | `state`, `properties`, `evidence` |
| `skill_group` | `state`, `properties`, `evidence` |

`operator` uses the `recorded_participation` relation. `skill_group` uses `subject_coverage` or `member_participation`. Permissions and candidate appeal membership are rechecked against the primary MySQL database. A required unavailable source is never reported as success: without `allowPartial` the request fails, while `allowPartial: true` returns the section with `status: "unavailable"` and an explicit reason.

If `sections` is omitted, the API selects the defaults for the scope. Explicit `targets` or a `from`/`to` range also add `evidence`, and client `actions` parameters add the actions section. `query` requires `targets`. The old `clientId` and `subjectId` shorthand forms are no longer part of the contract; use `scope`.

`previous_client_conversations` and `recent_client_interactions` are read from the durable context store. `relevant_client_memory` remains unavailable until rollout reports a ready client-memory search index; there is no hidden fallback.

## Writing actions and state

| Method | Endpoint | Result |
|---|---|---|
| `POST` | `/kw/clients/:client_id/context/actions` | Registers an idempotent action; a new record returns HTTP 201 |
| `PUT` | `/kw/clients/:client_id/context/state/:namespace` | CAS state update using `expectedVersion` |

Both operations require `Idempotency-Key`. Repeating the same normalized command returns the original result with `replayed: true`; a different body with the same key is rejected. `externalEventId` independently protects an action from duplication. State, action, receipt, and the client-memory indexing obligation are committed in one transaction. The context store is primary data and is not removed with projections.

## Sessions and bounded import

The authorized routes are:

- `POST /kw/context/sessions`;
- `POST /kw/context/sessions/:session_id/messages`;
- `GET /kw/context/sessions/:session_id/messages`;
- `POST /kw/context/sessions/:session_id/close`;
- `PUT /kw/context/sessions/:session_id/working-context`;
- `POST /kw/context/import-batches` and `GET /kw/context/import-batches/:batch_id`.

Client identity comes from the durable session binding, not from a later untrusted body. A message page is limited to 100 records and one message to 1 MiB. `workingContext` is limited to 8 KiB, with bounded goal, task, entity, assertion, and source lists. An import accepts 1–100 items and at most 1 MiB, persists its checkpoint, and resumes under the same `Idempotency-Key`.

## Client context

Recent client appeals:

```json
{
  "scope": { "entity": "client", "id": 10004 },
  "targets": ["previous_client_appeals"],
  "topK": 8
}
```

Recent and relevant appeals:

```json
{
  "scope": { "entity": "client", "id": 10004 },
  "targets": ["previous_client_appeals", "relevant_appeals"],
  "query": "refund",
  "topK": 8
}
```

## Subject context

```json
{
  "scope": { "entity": "subject", "id": 3 },
  "targets": ["recent_subject_appeals", "relevant_subject_appeals"],
  "query": "delivery delay"
}
```

`query` is required when targets includes `relevant_appeals` or `relevant_subject_appeals`.

## Limits

| Field | Default | Maximum |
|---|---:|---:|
| `topK` | 8 | 20 |
| `candidateLimit` | 80 | 200 |
| `maxSnippetsPerAppeal` | 3 | 5 |
| `maxResponseBytes` | 65,536 | 262,144 |
| `tokenBudget` | 8,000 | 8,000 |
| `timeoutMs` | 2,000 | 5,000 |
| `query` | — | 512 characters |

If the pack does not fit the budget, the API removes the least important evidence and returns `coverage.partial=true` with the `pack_budget` reason.

## Response contents

- `sections.properties` contains current authorized properties from the primary MySQL database;
- `sections.evidence` contains recent or relevant appeals selected through OpenSearch;
- `citations` links every fact or snippet to its source;
- `coverage` states whether the response is complete and why it may be partial;
- `budget` reports the actual pack size.

Permissions are checked before and after the pack is assembled. If access changes during the request or a required store is unavailable, the API fails closed and does not fall back to a legacy source.

## Readiness and coverage

Store readiness and response coverage are separate. One quarantined appeal does not disable Context API for the whole instance. A response affected by that appeal has `coverage.partial=true` and an explicit coverage reason.

Conversations with up to 1,000 messages are in the normal supported range. For conversations from 1,001 to 5,000 messages, the appeal-list API returns a possible-delay warning that OperatorLine also displays. Above 5,000 messages, no search document is built; the full transcript remains available from the source with pagination, while Context API search coverage is marked incomplete.

## MCP tools

The read-only contract is available through four tools:

- `get_client_context` for client context;
- `get_subject_context` for subject context;
- `get_operator_context` for operator context;
- `get_skill_group_context` for skill-group context.

MCP uses the names `client_id`, `subject_id`, `operator_id`, `skill_group_id`, `sections`, `actions`, `top_k`, `candidate_limit`, `max_snippets_per_appeal`, `max_response_bytes`, `token_budget`, and `timeout_ms`. The tools call the same authorized HTTP endpoint and do not provide write operations.

## Errors

| HTTP | Code | Meaning |
|---:|---|---|
| 400 | `INVALID_REQUEST` | Invalid scope, ID, target, or a required query is missing |
| 400 | `QUERY_BUDGET_EXCEEDED` | Request or response budget exceeded |
| 403 | `ACCESS_DENIED` | Access is denied or changed during the request |
| 429 | request protection code | Concurrency or rate limit exceeded |
| 503 | `SEARCH_UNAVAILABLE` | The capability is disabled or MySQL/OpenSearch is not ready |

Use the instance rollout page to see the exact readiness reason.
