---
title: "Semantic search"
method: POST
path: "/v2/workspaces/{id}/recall"
tags: ["Runtime"]
---

# Semantic search

`POST /v2/workspaces/{id}/recall`

## Request

- Base URL: `https://api.infolang.ai`
- URL: `https://api.infolang.ai/v2/workspaces/{id}/recall`
- Auth: one of:
  - HTTP bearer
  - HTTP bearer
  - HTTP bearer

Class A — accepts a session JWT, an `il_live_`/`il_test_` API key, or an `il_oauth_` token. On the `il_*` lanes the bearer is forwarded to Kairos verbatim and Kairos enforces the credential's grant on this workspace (its 401/403 is surfaced in the v2 envelope). The response body is Kairos's, passed through unchanged; `X-InfoLang-Tokens-Saved` / `X-InfoLang-Overage` are echoed when set.

Requires the `memories:read` scope. Accepts all three lanes: a session JWT (bounded by the caller's role on this workspace) or an `il_*` machine credential whose own scope set, workspace set and validity window cover it — both ceilings apply and neither can raise the other, so a credential carrying `memories:read` is still refused where the role does not permit it.

A credential with no scope record resolves to `LEGACY_DEFAULT`, which INCLUDES `memories:read` — every key minted before scoping keeps working here.

## Path parameters

- `id` string, required

## Request body

- object
  - `query` string, required
  - `top_k` integer
  - `namespace` string
  - `verbose` boolean
  - `properties` object
  - `golden` boolean
  - `adaptive` boolean
  - `format` 'full' | 'lossless' | 'exact' | 'snippet' | 'meta'
  - `snippet_chars` integer
  - `margin` number
  - `queries` string[]
  - `beliefs` boolean
  - `exclude_superseded` boolean
  - `regimes` boolean
  - `exclude_inapplicable` boolean
  - `warrants` boolean
  - `exclude_defeated` boolean
  - `correspondence` boolean
  - `inquiries` boolean
  - `only_outstanding` boolean

## Response `200`

Hits with similarity scores (forge shape). With `beliefs: true`, each hit also carries `belief`: `null` for a memory with no subject (absent, not unknown), else `current` / `superseded` (with `superseded_by`) / `contested` (more than one live claim — do not render one as the answer) / `unasserted`. `exclude_superseded` filters the retired ones and reports the count in `beliefs`.

With `regimes: true`, each hit also carries `regime`: `regime_agnostic: true` with `applicability: null` for an UNSCOPED memory — which means it applies EVERYWHERE, the algebra's explicit default and not an unknown — else `applicable` / `inapplicable_ended` (the phase ended; it still applied while it ran) / `inapplicable_void` (the phase never applied at any T) / `inapplicable_until_adjudicated` (two active regimes conflict and nobody has ruled — a dead end only an operator moves) / `inapplicable_adjudicated` / `not_yet_applicable`. `regime: null` is different again: no fold was available. `exclude_inapplicable` filters and reports its count, and KEEPS regime-agnostic hits.

With `warrants: true`, each hit also carries `warrant`: `examined: false` with `status: null` for a memory NO EDGE BEARS ON — nobody has stated a reason about it, which is not a doubt and not a defeat — else `warranted` / `defeated` / `unsupported` (every reason that bore on it has been withdrawn, defeated or discounted). `fold_status` always carries the kernel's own word beside the softened one. `warrant: null` is different again: no fold was available. `exclude_defeated` filters and reports its count, and KEEPS unexamined hits.

COST: there is no batch acceptance verb, so each distinct hit is one whole-workspace warrant fold PLUS one assertion-log oracle build upstream. `warrants.targets_folded` and `warrants.oracle_builds` are that call count, on the response. A workspace with no warrant records pays ONE probe for the whole page.

With `correspondence: true`, each subjected hit also carries `correspondence`: `split_risk: true` means at least one live `corresponds` link names this subject and NO operator has ruled — so the belief fold you are reading may be SPLIT across more than one key, and Phase 1's supersession guarantee may not be applying. `corresponds_to` lists the proposals, `identified_with` the pairs an operator actually ruled on, `disputed` whether anything conflicts. `beliefs_unioned` is always `false`, on the wire, because `corresponds` is not identity (CR2) and unioning two subjects would merge two facts nobody agreed were one. There is NO filter argument here, deliberately: a mark that changes no status, a payload the fold ignores, and the flag marking the rows an operator most needs to see are the only three things one could key on.

COST: ONE whole-workspace `identities` call (also the probe) plus ONE scoped neighborhood call per DISTINCT SUBJECT on the page. `correspondence.upstream_calls` is that number. A workspace with no correspondence records pays exactly one call for the whole page.

- object
  - `hits` object[], required
    - `id` string, required
    - `score` number
    - `text` union, required
      - string
      - unknown
      - unknown
    - `similarity` number
    - `source` string
    - `tags` union
      - string
      - string[]
    - `timestamp` union
      - string
      - number
  - `count` integer, required
  - `latency_ms` number
  - `namespace` string
  - `savings` object

## Other responses

- `401`: Missing/invalid credential
- `403`: The credential holds no grant on this workspace. Or `lane_not_supported` — the credential is well-formed but this route does not accept its lane
- `422`: Body failed the pinned kairos-runtime schema
- `502`: Upstream Kairos failure
- `503`: not_ready — Kairos ML backend still warming up

---

[API](https://skmtc.dev/infolang/apis/infolang-api.md) · [All operations](https://skmtc.dev/infolang/apis/infolang-api/llms.txt) · [OpenAPI document](https://skmtc.dev/infolang/apis/infolang-api/revisions/d04bf6de72c8?raw)
