# search

> Search memory — parameters, response fields, the unverified/stale/conflict statuses, graph relations and errors.

On this page you will learn how `search` finds records, what every response field means, and how to tell from the status whether a result can be trusted.

The running example: a new developer asks the agent "how do we deploy staging?", and the agent searches MMW.

## Purpose

`search` returns the records of a workspace that match a query, together with their provenance (`source_id`, `source_hash`, `source_revision`), freshness status and knowledge-graph relations. It only reads: nothing is created or changed.

## Parameters

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `query` | string | — (required) | Query text. An empty query returns the latest records of the workspace. |
| `workspace` | string | `"default"` | Workspace. Must already exist (something was saved in it at least once). |
| `scope` | string or null | `null` | Filter by label, such as `shared` or `private`. |
| `limit` | integer | `10` | How many records to return. Values outside 1–50 are clamped to the nearest bound. |
| `current_source_hash` | string or null | `null` | Current source hash: records whose `source_hash` differs get the `stale` status. |
| `exclude_stale` | boolean | `false` | Leave out records with the `stale` status. |

## How matching works

1. The query is split into words; a record matches if at least one word occurs in its text or `source` field. `score` is the number of matched words.
2. If nothing matches, or the query is abstract, the server expands it with synonyms and project terms using a language model and searches again (also looking at `fact_key`).
3. Results are sorted by `score`, then by last update time, and cut to `limit`.
4. In an organization, only records you are allowed to see under the visibility rules are returned.

## Response

The result is a list of records; in the structured response it is in the `result` field.

| Field | Description |
| --- | --- |
| `id` | Record ID. |
| `workspace` | Workspace. |
| `scope` | Record label. |
| `content` | Record text. |
| `source` | Who saved it (`owner`, `agent`, `telegram` and so on). |
| `source_id`, `source_hash`, `source_revision` | Record provenance. |
| `fact_key` | Topic key. |
| `confidence` | Confidence 0.0–1.0. |
| `status` | `unverified`, `stale` or `conflict` (see below). |
| `derived_from` | IDs of the records this one summarizes. |
| `superseded_by` | ID of a newer version with the same `fact_key`, or `null`. Newest versions are ranked first. |
| `score` | Relevance: number of matched query words. |
| `graph_relations` | Links to other records: `relation`, `direction` (`outgoing` or `incoming`), `weight`, `target_id`, `preview` (first 120 characters of the linked record). |
| `created_at`, `updated_at` | Creation and last update time, ISO 8601. |

### Statuses

| Status | When |
| --- | --- |
| `conflict` | The record has siblings with the same `fact_key` in the same workspace but different content or source hash. |
| `stale` | `current_source_hash` was passed and differs from the record's `source_hash`, or the archivist marked the record stale. |
| `unverified` | Everything else. This is the normal status: MMW keeps provenance but does not claim the fact is true. |

When several apply, the most alarming wins: `conflict` over `stale`, `stale` over `unverified`. **A summary inherits its sources' status:** if a record was saved with `derived_from` and one of its source records is in conflict or stale, the summary shows `conflict` or `stale` too (checked up to three levels deep).

## Example

```json title="arguments"
{
  "query": "staging deployed",
  "workspace": "default",
  "limit": 5
}
```

```json
{
  "result": [
    {
      "id": "4d428a41-e7b0-4f81-a886-f40c6f6c766c",
      "workspace": "default",
      "scope": "shared",
      "content": "staging is deployed with the systemd unit app-staging",
      "source": "agent",
      "source_id": "docs/deploy.md",
      "source_hash": "sha256:9f2c4e1a",
      "source_revision": "a1b2c3d",
      "fact_key": "deploy-staging",
      "confidence": 0.9,
      "status": "unverified",
      "derived_from": [],
      "score": 2,
      "graph_relations": [
        {
          "relation": "relates_to",
          "direction": "outgoing",
          "weight": 0.8,
          "target_id": "b7e1d0c2-5a44-4f0e-8f3a-2c9d1e6f0a11",
          "preview": "prod is deployed through the release pipeline"
        }
      ],
      "created_at": "2026-10-05T09:12:00.412345+00:00",
      "updated_at": "2026-10-05T09:12:00.412345+00:00"
    }
  ]
}
```

Relations are built by the archivist, which picks `relation` from `relates_to`, `fixes_issue` and `supersedes`; `weight` is the strength of the link.

## Errors

| Text | Cause |
| --- | --- |
| `workspace '…' is not active` | No such workspace (nothing was saved in it yet) or it was deleted. Search does not create workspaces. |
| `403 Forbidden: workspace access denied for project` | The workspace belongs to another project than the key's. |
| `MCP rate limit exceeded`, `project call limit exceeded` | Too many calls per minute. |
| `bound project is missing, inactive, or outside tenant` | The key's project was deleted or is inactive. |
| `[MMW Notice]: …` | No subscription or suspended account. Search keeps working during the grace period after a subscription expires. |

## Notes

`current_source_hash` is compared with **every** returned record that has a `source_hash`, including records from other sources. To check one document, use [`validate_memory`](/reference/validate-memory/) with `source_id`; pass the hash to `search` only when the results come from a single source.

- `scope` is a label filter, not an access mechanism. In an organization, access is decided by [visibility](/organizations/visibility/).
- An empty result is not an error: try other words, check `workspace`, and make sure the key belongs to the right project.
- Reads of knowledge handed over from people who left are recorded in the [organization audit log](/organizations/audit-log/) (counts only, no content).

## Next steps
