Knowledge

Search knowledge

Searches across documents and memories using hybrid retrieval — a vector query and a full-text query per source, fused by reciprocal rank — or by file paths, document IDs, memory store IDs, or tags. At least one of query, tags, document_paths, document_ids, or memory_store_ids must be provided.

post/v1/projects/{project_id}/knowledge/search

Request

  • Base URL: https://api.naturali.ai
  • URL: https://api.naturali.ai/v1/projects/{project_id}/knowledge/search
  • Auth: one of:
    • HTTP bearer
    • OAuth 2 (authorization code, authorization URL https://api.naturali.ai/authorize, token URL https://api.naturali.ai/token, scopes: mcp:access)

Request body

querystring

Search query text. Runs a vector query and a full-text query over each store and fuses the rankings; the full-text channel matches exact tokens and phrases, the vector channel everything else.

min_similaritynumber

Minimum raw cosine similarity_score a vector candidate must reach to take part in ranking, applied before fusion. Only applies when query is provided. Lexical candidates are deliberately exempt: a result that literally contains the searched token is the evidence, and dropping it for a low cosine is the failure hybrid search exists to prevent. Not a floor on score, whose fused value encodes rank position rather than similarity.

rrf_kinteger

The k in the reciprocal rank fusion term 1 / (k + rank), which sets how steeply a result's contribution decays with its position in each ranked list. A smaller value weights the very top of each list more heavily. Defaults to the deployment's KNOWLEDGE_RRF_K, itself 60 by default. Only applies when query is provided.

recency_half_life_daysnumber

Half-life, in days, of a recency decay applied to memory store results after fusion: a result's score is multiplied by 2 ^ (-age_in_days / recency_half_life_days), where age is measured from its updated_at. Document results are never decayed. 0 — the default, and the default of the deployment's KNOWLEDGE_RECENCY_HALF_LIFE_DAYS — disables the blend entirely; it is a switch, not a lower bound, and sending 0 turns off a deployment-wide decay for this one request. Accepts fractions (0.5 is twelve hours). How many ranks a given half-life costs depends on rrf_k. Only applies when query is provided. No value is known to be safe in general: every half-life measured against the reference corpus that lifted recency-sensitive queries also cost relevance-sensitive ones, which is why this ships disabled. Measure against your own corpus before setting it — see the Retrieval Quality guide.

limitinteger

Maximum number of results to return (default 10). A value above 100 is clamped to 100 — the ceiling bounds the vector scan this one request performs, so a larger limit returns everything there is up to that many rows rather than being refused.

include_documentsboolean

Set false to leave the document store out of this search. A query names no store, so it reaches both; the store-specific filters narrow within a store rather than choosing between them. This is the switch, and a filter naming the other store never overrides it. false for both stores is 400.

include_memoriesboolean

Set false to leave the memory store out of this search. The mirror of include_documents, for a caller that wants documents alone.

memory_store_idsstring[]

Search memories within these specific memory stores

document_pathsstring[]

Filter results to documents whose file path starts with one of these prefixes

document_idsstring[]

Filter results to specific document IDs

tagsTagBag

Key-value labels on a resource. A flat object of string values — an array, a nested object or a number is rejected with 400 VALIDATION_FAILED, never coerced. Keys are opaque and stored verbatim, so cost_center and costCenter are two different tags. Matched by JSONB containment wherever tags are read: the ?tags= filter and knowledge search.

Keys beginning system. are reserved: the platform writes them to record which conversation, actor, agent and role a row came from, and a write naming one is refused with 400 RESERVED_TAG_KEY. They are read and filtered like any other tag.

The bag is bounded, because every pair reaches the IAM context of every access check on the resource: at most 50 keys, each key at most 128 characters and each value at most 256. A write past a bound — including a merge that would grow the stored bag past the key count — is 400 VALIDATION_FAILED with meta.limit naming the bound it crossed. system.* keys are the platform's and do not count against the 50.

metadataMetadataFilter

Narrows results by the metadata bag. Each key is a field; its value is either a value to match exactly, or an object naming one or more operators.

Equality and in match the stored value exactly, so 3 and "3" are different filters.

gt, gte, lt and lte order the field against the operand, and the operand's own JSON type says which comparison is meant: a number orders numerically, a string lexicographically. They work on any field, in any scope. A document whose field holds another type is excluded from the comparison rather than erroring on it, so a range never fails on a bag that happens to hold text where another holds a number. An operand that is neither a number nor a string — a boolean, null, a list, an object — has no ordering and is 400 VALIDATION_FAILED, naming the field in meta.field.

Example request

{
  "query": "customer communication preferences",
  "min_similarity": 0.5,
  "rrf_k": 60,
  "recency_half_life_days": 30,
  "limit": 10,
  "memory_store_ids": [
    "mstore_V1StGXR8Z5jdHi6B"
  ],
  "document_paths": [
    "/sales/",
    "/hr/"
  ],
  "document_ids": [
    "doc_V1StGXR8Z5jdHi6B"
  ],
  "tags": {
    "team": "finance",
    "env": "prod"
  },
  "metadata": {
    "quarter": "Q1",
    "revision": {
      "gte": 3,
      "lt": 11
    },
    "status": {
      "in": [
        "draft",
        "final"
      ]
    }
  }
}

Response

Search results

Example response

{
  "results": [
    {
      "source_type": "document",
      "document_id": "doc_V1StGXR8Z5jdHi6B",
      "document_version": 3,
      "chunk_id": "dchunk_V1StGXR8Z5jdHi6B",
      "page": 3,
      "file_id": "file_V1StGXR8Z5jdHi6B",
      "project_id": "proj_V1StGXR8Z5jdHi6B",
      "path": "/sales/policies.txt",
      "filename": "policies.txt",
      "size": 1024,
      "title": "Sales Communication Policy",
      "metadata": {
        "author": "John",
        "revision": 2
      },
      "tags": {
        "team": "finance",
        "env": "prod"
      },
      "score": 0.0328,
      "signals": {
        "vector": 2,
        "lexical": 1
      },
      "similarity_score": 0.82
    }
  ]
}

Changes

No recorded changes to this operation across all 1 revision of this API.