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

# Using Filters

> Learn how to filter workspaces, peers, sessions, and messages using Honcho's powerful filtering system

Honcho provides a sophisticated filtering system that allows you to query workspaces, peers, sessions, and messages with precise control. The filtering system supports logical operators, comparison operators, metadata filtering, and wildcards to help you find exactly what you need.

## Basic Filtering Concepts

Filters in Honcho are expressed as dictionaries that define conditions for matching resources. The system supports both simple equality filters and complex queries with multiple conditions.

### Simple Filters

The most basic filters check for exact matches:

<CodeGroup>
  ```python Python theme={null}
  from honcho import Honcho

  # Initialize client
  honcho = Honcho()

  # Simple peer filter
  peers = honcho.peers(filters={"peer_id": "alice"})

  # Simple session filter with metadata
  sessions = honcho.sessions(filters={
      "metadata": {"type": "support"}
  })

  # Simple message filter
  messages = honcho.messages(filters={
      "session_id": "support-chat-1",
      "peer_id": "alice"
  })
  ```

  ```typescript TypeScript theme={null}
  import { Honcho } from "@honcho-ai/sdk";

  (async () => {
      // Initialize client
      const honcho = new Honcho({});

      // Simple peer filter
      const peers = await honcho.peers({
        filters: { peer_id: "alice" }
      });

      // Simple session filter with metadata
      const sessions = await honcho.sessions({
        filters: {
          metadata: { type: "support" }
        }
      });

      // Simple message filter
      const messages = await honcho.messages({
        filters: {
          session_id: "support-chat-1",
          peer_id: "alice"
        }
      });
  })();
  ```
</CodeGroup>

## Logical Operators

Combine multiple conditions using logical operators for complex queries:

### AND Operator

Use AND to require all conditions to be true:

<CodeGroup>
  ```python Python theme={null}
  messages = honcho.messages(filters={
      "AND": [
          {"session_id": "chat-1"},
          {"created_at": {"gte": "2024-01-01"}}
      ]
  })
  ```

  ```typescript TypeScript theme={null}
  (async () => {
      const messages = await honcho.messages({
        filters: {
          AND: [
            { session_id: "chat-1" },
            { created_at: { gte: "2024-01-01" } }
          ]
        }
      });
  })();
  ```
</CodeGroup>

### OR Operator

Use OR to match any of the specified conditions:

<CodeGroup>
  ```python Python theme={null}
  # Find messages from either alice or bob
  messages = session.messages(filters={
      "OR": [
          {"peer_id": "alice"},
          {"peer_id": "bob"}
      ]
  })

  # Complex OR with metadata conditions
  sessions = honcho.sessions(filters={
      "OR": [
          {"metadata": {"priority": "high"}},
          {"metadata": {"urgent": True}},
          {"metadata": {"escalated": True}}
      ]
  })
  ```

  ```typescript TypeScript theme={null}
  (async () => {
      // Find messages from either alice or bob
      const messages = await session.messages({
        filters: {
          OR: [
            { peer_id: "alice" },
            { peer_id: "bob" }
          ]
        }
      });

      // Complex OR with metadata conditions
      const sessions = await honcho.sessions({
        filters: {
          OR: [
            { metadata: { priority: "high" } },
            { metadata: { urgent: true } },
            { metadata: { escalated: true } }
          ]
        }
      });
  })();
  ```
</CodeGroup>

### NOT Operator

Use NOT to exclude specific conditions:

<CodeGroup>
  ```python Python theme={null}
  # Find all peers except alice
  peers = honcho.peers(filters={
      "NOT": [
          {"peer_id": "alice"}
      ]
  })

  # Find sessions that are NOT completed
  sessions = honcho.sessions(filters={
      "NOT": [
          {"metadata": {"status": "completed"}}
      ]
  })
  ```

  ```typescript TypeScript theme={null}
  (async () => {
      // Find all peers except alice
      const peers = await honcho.peers({
        filters: {
          NOT: [
            { peer_id: "alice" }
          ]
        }
      });

      // Find sessions that are NOT completed
      const sessions = await honcho.sessions({
        filters: {
          NOT: [
            { metadata: { status: "completed" } }
          ]
        }
      });
  })();
  ```
</CodeGroup>

### Negation and Unset Fields

A field can be unset, and negation has to account for it. `NOT` and the `ne`
comparison operator both **include** rows where the field has no value at all: a
field with no value is not the value you are excluding, so excluding that value
keeps the row.

Positive conditions work the other way around. An unset field matches nothing,
so equality and `contains` never return those rows. To select them, filter on
`null` directly:

| Filter                                                | Rows where the field is unset             |
| ----------------------------------------------------- | ----------------------------------------- |
| `{"field": "x"}`, `{"field": {"contains": "x"}}`      | Excluded                                  |
| `{"NOT": [{"field": "x"}]}`, `{"field": {"ne": "x"}}` | Included                                  |
| `{"field": null}`                                     | Only these                                |
| `{"field": {"ne": null}}`                             | Excluded — the field must have some value |

Of the filterable fields, only a conclusion's `session_id` can be unset: a
conclusion drawn across a whole workspace belongs to no single session. Every
other field is always populated, so none of this affects filters on them. See
[Filtering Conclusions](#filtering-conclusions) for what conclusions are.

<CodeGroup>
  ```python Python theme={null}
  # Every conclusion except the ones in this session — including
  # workspace-level conclusions, which belong to no session at all
  conclusions = peer.conclusions.list(filters={
      "NOT": [
          {"session_id": "session-123"}
      ]
  })

  # Equivalent
  conclusions = peer.conclusions.list(filters={
      "session_id": {"ne": "session-123"}
  })
  ```

  ```typescript TypeScript theme={null}
  (async () => {
      // Every conclusion except the ones in this session — including
      // workspace-level conclusions, which belong to no session at all
      const conclusions = await peer.conclusions.list({
        filters: { NOT: [{ session_id: "session-123" }] }
      });

      // Equivalent
      const same = await peer.conclusions.list({
        filters: { session_id: { ne: "session-123" } }
      });
  })();
  ```
</CodeGroup>

To exclude a value **and** require the field to be set, combine the two with
`AND`:

<CodeGroup>
  ```python Python theme={null}
  # Excludes workspace-level conclusions and `session-123`
  conclusions = peer.conclusions.list(filters={
      "AND": [
          {"session_id": {"ne": "session-123"}},
          {"session_id": {"ne": None}}
      ]
  })
  ```

  ```typescript TypeScript theme={null}
  (async () => {
      // Excludes workspace-level conclusions and `session-123`
      const conclusions = await peer.conclusions.list({
        filters: {
          AND: [
            { session_id: { ne: "session-123" } },
            { session_id: { ne: null } }
          ]
        }
      });
  })();
  ```
</CodeGroup>

### Combining Logical Operators

Create sophisticated queries by combining different logical operators:

<CodeGroup>
  ```python Python theme={null}
  # Find messages from alice OR bob, but NOT where message has archived set to true in metadata
  messages = session.messages(filters={
      "AND": [
          {
              "OR": [
                  {"peer_id": "alice"},
                  {"peer_id": "bob"}
              ]
          },
          {
              "NOT": [
                  {"metadata": {"archived": True}}
              ]
          }
      ]
  })
  ```

  ```typescript TypeScript theme={null}
  (async () => {
      // Find messages from alice OR bob, but NOT where message has archived set to true in metadata
      const messages = await session.messages({
        filters: {
          AND: [
            {
              OR: [
                { peer_id: "alice" },
                { peer_id: "bob" }
              ]
            },
            {
              NOT: [
                { metadata: { archived: true } }
              ]
            }
          ]
        }
      });
  })();
  ```
</CodeGroup>

## Comparison Operators

Use comparison operators for range queries and advanced matching:

### Numeric Comparisons

<CodeGroup>
  ```python Python theme={null}
  # Find sessions created after a specific date
  sessions = honcho.sessions(filters={
      "created_at": {"gte": "2024-01-01"}
  })

  # Find messages within a date range
  messages = session.messages(filters={
      "created_at": {
          "gte": "2024-01-01",
          "lte": "2024-12-31"
      }
  })

  # Metadata numeric comparisons
  sessions = honcho.sessions(filters={
      "metadata": {
          "score": {"gt": 8.5},
          "duration": {"lte": 3600}
      }
  })
  ```

  ```typescript TypeScript theme={null}
  (async () => {
      // Find sessions created after a specific date
      const sessions = await honcho.sessions({
        filters: {
          created_at: { gte: "2024-01-01" }
        }
      });

      // Find messages within a date range
      const messages = await session.messages({
        filters: {
          created_at: {
            gte: "2024-01-01",
            lte: "2024-12-31"
          }
        }
      });

      // Metadata numeric comparisons
      const filteredSessions = await honcho.sessions({
        filters: {
          metadata: {
            score: { gt: 8.5 },
            duration: { lte: 3600 }
          }
        }
      });
  })();
  ```
</CodeGroup>

### List Membership

A bare list is shorthand for `in`, so `{"peer_id": ["alice", "bob"]}` and
`{"peer_id": {"in": ["alice", "bob"]}}` are equivalent:

<CodeGroup>
  ```python Python theme={null}
  # Shorthand: a bare list means "any of these"
  messages = session.messages(filters={
      "peer_id": ["alice", "bob", "charlie"]
  })
  ```

  ```typescript TypeScript theme={null}
  (async () => {
      // Shorthand: a bare list means "any of these"
      const messages = await session.messages({
        filters: { peer_id: ["alice", "bob", "charlie"] }
      });
  })();
  ```
</CodeGroup>

<Warning>
  Bare lists behave differently inside metadata — use `{"in": [...]}` there for OR matching.
</Warning>

The explicit form, plus the other comparison operators:

<CodeGroup>
  ```python Python theme={null}
  # Find messages from specific peers in a session
  messages = session.messages(filters={
      "peer_id": {"in": ["alice", "bob", "charlie"]}
  })

  # Find sessions with specific tags
  sessions = honcho.sessions(filters={
      "metadata": {
          "tag": {"in": ["important", "urgent", "follow-up"]}
      }
  })

  # Not equal comparisons
  peers = honcho.peers(filters={
      "metadata": {
          "status": {"ne": "inactive"}
      }
  })
  ```

  ```typescript TypeScript theme={null}
  (async () => {
      // Find messages from specific peers in a session
      const messages = await session.messages({
        filters: {
          peer_id: { in: ["alice", "bob", "charlie"] }
        }
      });

      // Find sessions with specific tags
      const sessions = await honcho.sessions({
        filters: {
          metadata: {
            tag: { in: ["important", "urgent", "follow-up"] }
          }
        }
      });

      // Not equal comparisons
      const peers = await honcho.peers({
        filters: {
          metadata: {
            status: { ne: "inactive" }
          }
        }
      });
  })();
  ```
</CodeGroup>

## Metadata Filtering

Metadata filtering is particularly powerful in Honcho, supporting nested conditions and complex queries:

### Basic Metadata Filtering

<CodeGroup>
  ```python Python theme={null}
  # Simple metadata equality
  sessions = honcho.sessions(filters={
      "metadata": {
          "type": "customer_support",
          "priority": "high"
      }
  })

  # Nested metadata objects
  peers = honcho.peers(filters={
      "metadata": {
          "profile": {
              "role": "admin",
              "department": "engineering"
          }
      }
  })
  ```

  ```typescript TypeScript theme={null}
  (async () => {
      // Simple metadata equality
      const sessions = await honcho.sessions({
        filters: {
          metadata: {
            type: "customer_support",
            priority: "high"
          }
        }
      });

      // Nested metadata objects
      const peers = await honcho.peers({
        filters: {
          metadata: {
            profile: {
              role: "admin",
              department: "engineering"
            }
          }
        }
      });
  })();
  ```
</CodeGroup>

### Advanced Metadata Queries

<Info>
  If you want to do advanced queries like these, make sure not to create metadata fields that use the same names as the included comparison operators! For example, if you have a metadata field called `contains`, it will conflict with the `contains` operator.
</Info>

<CodeGroup>
  ```python Python theme={null}
  # Metadata with comparison operators
  sessions = honcho.sessions(filters={
      "metadata": {
          "score": {"gte": 4.0, "lte": 5.0},
          "created_by": {"ne": "system"},
          "tags": {"contains": "important"}
      }
  })

  # Complex metadata conditions
  messages = session.messages(filters={
      "AND": [
          {"metadata": {"sentiment": {"in": ["positive", "neutral"]}}},
          {"metadata": {"confidence": {"gt": 0.8}}},
          {"content": {"icontains": "thank"}}
      ]
  })
  ```

  ```typescript TypeScript theme={null}
  (async () => {
      // Metadata with comparison operators
      const sessions = await honcho.sessions({
        filters: {
          metadata: {
            score: { gte: 4.0, lte: 5.0 },
            created_by: { ne: "system" },
            tags: { contains: "important" }
          }
        }
      });

      // Complex metadata conditions
      const messages = await session.messages({
        filters: {
          AND: [
            { metadata: { sentiment: { in: ["positive", "neutral"] } } },
            { metadata: { confidence: { gt: 0.8 } } },
            { content: { icontains: "thank" } }
          ]
        }
      });
  })();
  ```
</CodeGroup>

## Wildcards

Use wildcards (\*) to match any value for a field:

<CodeGroup>
  ```python Python theme={null}
  # Find all sessions with any peer_id (essentially all sessions)
  sessions = honcho.sessions(filters={
      "peer_id": "*"
  })

  # Wildcard in lists - matches everything
  messages = session.messages(filters={
      "peer_id": {"in": ["alice", "bob", "*"]}
  })

  # Metadata wildcards
  sessions = honcho.sessions(filters={
      "metadata": {
          "type": "*",  # Any type
          "status": "active"  # But status must be active
      }
  })
  ```

  ```typescript TypeScript theme={null}
  (async () => {
      // Find all sessions with any peer_id (essentially all sessions)
      const sessions = await honcho.sessions({
        filters: {
          peer_id: "*"
        }
      });

      // Wildcard in lists - matches everything
      const messages = await session.messages({
        filters: {
          peer_id: { in: ["alice", "bob", "*"] }
        }
      });

      // Metadata wildcards
      const filteredSessions = await honcho.sessions({
        filters: {
          metadata: {
            type: "*",  // Any type
            status: "active"  // But status must be active
          }
        }
      });
  })();
  ```
</CodeGroup>

## Resource-Specific Examples

### Filtering Workspaces

<CodeGroup>
  ```python Python theme={null}
  # Find workspaces by name pattern
  workspaces = honcho.workspaces(filters={
      "name": {"contains": "prod"}
  })

  # Filter by metadata
  workspaces = honcho.workspaces(filters={
      "metadata": {
          "environment": "production",
          "team": {"in": ["backend", "frontend", "devops"]}
      }
  })
  ```

  ```typescript TypeScript theme={null}
  (async () => {
      // Find workspaces by name pattern
      const workspaces = await honcho.workspaces({
        filters: {
          name: { contains: "prod" }
        }
      });

      // Filter by metadata
      const workspaces = await honcho.workspaces({
        filters: {
          metadata: {
            environment: "production",
            team: { in: ["backend", "frontend", "devops"] }
          }
        }
      });
  })();
  ```
</CodeGroup>

### Filtering Messages

<CodeGroup>
  ```python Python theme={null}
  # Find error messages from the last week
  from datetime import datetime, timedelta

  week_ago = (datetime.now() - timedelta(days=7)).isoformat()
  messages = session.messages(filters={
      "AND": [
          {"content": {"icontains": "error"}},
          {"created_at": {"gte": week_ago}},
          {"metadata": {"level": {"in": ["error", "critical"]}}}
      ]
  })

  # Find messages in specific sessions with sentiment analysis
  messages = session.messages(filters={
      "AND": [
          {"session_id": {"in": ["support-1", "support-2", "support-3"]}},
          {"metadata": {"sentiment": "negative"}},
          {"metadata": {"confidence": {"gte": 0.7}}}
      ]
  })
  ```

  ```typescript TypeScript theme={null}
  (async () => {
      // Find error messages from the last week
      const weekAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString();
      const messages = await session.messages({
        filters: {
          AND: [
            { content: { icontains: "error" } },
            { created_at: { gte: weekAgo } },
            { metadata: { level: { in: ["error", "critical"] } } }
          ]
        }
      });

      // Find messages in specific sessions with sentiment analysis
      const sentimentMessages = await session.messages({
        filters: {
          AND: [
            { session_id: { in: ["support-1", "support-2", "support-3"] } },
            { metadata: { sentiment: "negative" } },
            { metadata: { confidence: { gte: 0.7 } } }
          ]
        }
      });
  })();
  ```
</CodeGroup>

### Filtering Conclusions

Conclusions belong to an observer/observed peer pair (accessed via
`peer.conclusions` for self-conclusions or `peer.conclusions_of(target)` for
conclusions about another peer). The observer and observed are filled in
automatically by the scope, so the `filters` you pass add to them.

The most useful conclusion-specific field is `level`, the reasoning level:

* `explicit` — extracted directly from messages
* `deductive` / `inductive` / `contradiction` — derived later during dreaming

A common request is to surface only the directly-stated facts and exclude
anything inferred during dreaming — filter `level` to `explicit`:

<CodeGroup>
  ```python Python theme={null}
  # Only conclusions extracted directly from messages (exclude dream-derived)
  explicit = peer.conclusions.list(filters={"level": "explicit"})

  # Only dream-derived conclusions
  derived = peer.conclusions.list(filters={"level": {"in": ["deductive", "inductive"]}})

  # Same filtering on semantic search
  results = peer.conclusions.query(
      "food preferences",
      filters={"level": "deductive"},
  )

  # Conclusions about another peer, explicit only
  bob_explicit = peer.conclusions_of("bob").list(filters={"level": "explicit"})
  ```

  ```typescript TypeScript theme={null}
  (async () => {
      // Only conclusions extracted directly from messages (exclude dream-derived)
      const explicit = await peer.conclusions.list({ filters: { level: "explicit" } });

      // Only dream-derived conclusions
      const derived = await peer.conclusions.list({
        filters: { level: { in: ["deductive", "inductive"] } }
      });

      // Same filtering on semantic search (query, topK, distance, filters)
      const results = await peer.conclusions.query(
        "food preferences",
        10,
        undefined,
        { level: "deductive" }
      );

      // Conclusions about another peer, explicit only
      const bobExplicit = await peer.conclusionsOf("bob").list({
        filters: { level: "explicit" }
      });
  })();
  ```
</CodeGroup>

## Value Types

A filter value has to be usable against the field it targets. Honcho validates
this before running the query and returns a `422` with an explanation when it
doesn't hold, rather than failing mid-query or quietly returning nothing.

| Field                                           | Accepts                                                                |
| ----------------------------------------------- | ---------------------------------------------------------------------- |
| Text — `peer_id`, `session_id`, `id`, `content` | Strings                                                                |
| Numeric — `token_count`                         | Numbers, or numeric strings like `"5"`. Exact for integers of any size |
| Timestamps — `created_at`                       | ISO 8601 strings such as `"2026-01-01"` or `"2026-01-01T12:00:00Z"`    |
| Boolean — `is_active`                           | `true` / `false`                                                       |
| `metadata`                                      | An object, matched by containment — bare or under `contains`           |
| Fields with fixed values — `level`              | One of the documented values                                           |
| Any field                                       | `null`, which matches rows where the field is unset                    |

Three consequences worth knowing:

* **Booleans must be real booleans.** `{"is_active": True}` filters; the string
  `{"is_active": "true"}` is rejected.
* **Fixed-value fields are checked.** `{"level": "explicit"}` filters;
  `{"level": "typo"}` is rejected instead of returning an empty list, so a
  misspelling doesn't look like "no results".
* **`metadata` takes only the two shapes above** — bare, or under `contains`.
  Comparison operators don't apply to the object as a whole, so
  `{"metadata": {"ne": {...}}}` is rejected. To compare *within* metadata, put
  the operator on the key — `{"metadata": {"status": {"ne": "done"}}}`. To negate
  a match, wrap the whole condition in `NOT`. See
  [Metadata Filtering](#metadata-filtering).

For every field other than `metadata`, the same rules apply however the value is
wrapped — bare, under an operator, or inside an `in` list — so
`{"level": "explicit"}`, `{"level": {"ne": "explicit"}}` and
`{"level": {"in": ["explicit"]}}` all validate identically.

An empty `in` list matches nothing:

<CodeGroup>
  ```python Python theme={null}
  # Returns no results — an empty allowlist excludes everything
  messages = session.messages(filters={"peer_id": {"in": []}})
  ```

  ```typescript TypeScript theme={null}
  (async () => {
      // Returns no results — an empty allowlist excludes everything
      const messages = await session.messages({ filters: { peer_id: { in: [] } } });
  })();
  ```
</CodeGroup>

## Scoping Recall to Sessions

The [chat endpoint](/docs/v3/documentation/features/chat) and the representation
endpoint accept a `filters` body too, but a deliberately narrow one: it defines
a **session allowlist**, restricting what the request can recall to the sessions
you name — conclusions on both endpoints, and on chat the messages the agent
reads as well.

This is how you restrict recall to more than one session. The `session_id`
parameter pins a request to exactly one session; an allowlist accepts a set.

Only the `session_id` key is supported here, in three shapes:

```json theme={null}
{"filters": {"session_id": "support-chat-1"}}
{"filters": {"session_id": ["support-chat-1", "support-chat-2"]}}
{"filters": {"session_id": {"in": ["support-chat-1", "support-chat-2"]}}}
```

<CodeGroup>
  ```bash Chat theme={null}
  curl -X POST "$HONCHO_URL/v3/workspaces/my-app/peers/user-123/chat" \
    -H "Authorization: Bearer $HONCHO_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "query": "What did the user ask about billing?",
      "filters": { "session_id": ["support-chat-1", "support-chat-2"] }
    }'
  ```

  ```bash Representation theme={null}
  curl -X POST "$HONCHO_URL/v3/workspaces/my-app/peers/user-123/representation" \
    -H "Authorization: Bearer $HONCHO_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "filters": { "session_id": ["support-chat-1", "support-chat-2"] }
    }'
  ```
</CodeGroup>

Both SDKs expose this as a `sessions` option, which goes on the wire as the
`filters` body above:

<CodeGroup>
  ```python Python theme={null}
  answer = user.chat("What did the user ask about billing?",
                     sessions=["support-chat-1", "support-chat-2"])

  rep = user.representation(sessions=["support-chat-1", "support-chat-2"])
  ```

  ```typescript TypeScript theme={null}
  const answer = await user.chat("What did the user ask about billing?", {
    sessions: ["support-chat-1", "support-chat-2"],
  });

  const rep = await user.representation({
    sessions: ["support-chat-1", "support-chat-2"],
  });
  ```
</CodeGroup>

<Note>
  If the same set of sessions is a boundary you reuse, name it: a
  [scope](/docs/v3/documentation/features/advanced/scopes) is a persistent version of
  this allowlist, and querying a single scope recalls at full depth rather than
  `explicit`-only. `sessions` is the right tool when the set is decided
  per-request.
</Note>

### Rules

Unlike the list endpoints above, this filter **fails closed**: an unrecognized
key or shape is rejected with `422` rather than ignored, because a silently
dropped filter here would widen recall instead of narrowing it.

| Rule                                                                  | Behavior                                                  |
| --------------------------------------------------------------------- | --------------------------------------------------------- |
| Any key other than `session_id`                                       | `422`                                                     |
| A shape other than a string, a list of strings, or `{"in": [...]}`    | `422`                                                     |
| An entry that isn't a well-formed session id — wildcards included     | `422`                                                     |
| More than 1,000 sessions                                              | `422`                                                     |
| `session_id` set alongside `filters`                                  | The `session_id` must appear in the allowlist, else `422` |
| An empty allowlist (`[]`)                                             | Valid, and recalls nothing                                |
| A peer-scoped key naming a session its peer isn't an active member of | `401` on chat — see below                                 |

<Note>
  On chat, a peer-scoped key must be an active member of every session it names —
  the allowlist reaches message recall there — and the request is rejected with
  `401` otherwise. The representation endpoint runs no membership check: key scope
  already confines the caller to its own peer's representation, which an allowlist
  can only narrow.
</Note>

### What Changes Under an Allowlist

Restricting recall by session narrows what the reasoning agent can draw on:

* **Only `explicit` conclusions are recalled.** Dream-derived conclusions
  (`deductive`, `inductive`) are synthesized across sessions, so they can't be
  attributed to one session and are excluded.
* **Reasoning-chain traversal is unavailable**, since it walks into those
  derived conclusions.
* **Message recall is restricted to the allowlisted sessions** across every
  search path — semantic, keyword, and date-range.

<Note>
  Because of this, an allowlisted request answers from directly-stated facts
  rather than higher-order inferences. If you want the full representation, omit
  `filters` and let the agent search everything.
</Note>

## Error Handling

Handle filter errors gracefully:

<CodeGroup>
  ```python Python theme={null}
  from honcho.exceptions import FilterError

  try:
      # Invalid filter - unsupported operator
      messages = session.messages(filters={
          "created_at": {"invalid_operator": "2024-01-01"}
      })
  except FilterError as e:
      print(f"Filter error: {e}")
      # Handle the error appropriately

  try:
      # Invalid column name
      sessions = honcho.sessions(filters={
          "nonexistent_field": "value"
      })
  except FilterError as e:
      print(f"Invalid field: {e}")
  ```

  ```typescript TypeScript theme={null}
  (async () => {
      try {
        // Invalid filter - unsupported operator
        const messages = await session.messages({
          filters: {
            created_at: { invalid_operator: "2024-01-01" }
          }
        });
      } catch (error) {
        if (error.message.includes("filters")) {
          console.error(`Filter error: ${error.message}`);
          // Handle the error appropriately
        }
      }

      try {
        // Invalid column name
        const sessions = await honcho.sessions({
          filters: {
            nonexistent_field: "value"
          }
        });
      } catch (error) {
        console.error(`Invalid field: ${error.message}`);
      }
  })();
  ```
</CodeGroup>

## Conclusion

Honcho's filtering system provides powerful capabilities for querying your conversational data. By understanding how to:

* Use simple equality filters and complex logical operators
* Apply comparison operators for range and pattern matching
* Filter metadata with nested conditions
* Handle wildcards and dynamic filter construction
* Follow best practices for performance and validation

You can build sophisticated applications that efficiently find and process exactly the conversations, messages, and insights you need from your Honcho data.
