# Filtering events

How search_events conditions join, which fields take a reference, and how to read what comes back.

Canonical: https://chikaraintel.com/docs/mcp/guides/filtering-events

## One condition

A condition is a field, an operator and a value. The operator defaults to `EQUAL`, so the shortest useful filter is one line.

Every move:

```json
{
  "conditions": [
    {
      "field": "EventType",
      "value": "move"
    }
  ],
  "limit": 15
}
```

## Joining conditions

Each condition after the first carries a `join` of `AND` or `OR`, which says how it attaches to the conditions before it. Leave it out and it's `AND`.

Departures from one company:

```json
{
  "conditions": [
    {
      "field": "EventType",
      "value": "move"
    },
    {
      "field": "MoveType",
      "value": "Departure",
      "join": "AND"
    },
    {
      "field": "Organisation",
      "value": "3f2a9c1e-7b4d-4e8a-9c21-5d6e7f8a9b01",
      "join": "AND"
    }
  ],
  "limit": 15
}
```

## Precedence

AND binds tighter than OR, as in SQL. The list below reads as (Hire at Halberd) OR (any Appointment), which is rarely what anyone means.

Binds as (A AND B) OR C:

```json
{
  "conditions": [
    {
      "field": "MoveType",
      "value": "Hire"
    },
    {
      "field": "Organisation",
      "value": "3f2a9c1e-7b4d-4e8a-9c21-5d6e7f8a9b01",
      "join": "AND"
    },
    {
      "field": "MoveType",
      "value": "Appointment",
      "join": "OR"
    }
  ],
  "limit": 15
}
```

There are no brackets, and moving the OR pair to the front doesn't help: AND still binds first. To get hires or appointments at Halberd, repeat the shared condition on each side of the OR.

(Hire AND Halberd) OR (Appointment AND Halberd):

```json
{
  "conditions": [
    {
      "field": "MoveType",
      "value": "Hire"
    },
    {
      "field": "Organisation",
      "value": "3f2a9c1e-7b4d-4e8a-9c21-5d6e7f8a9b01",
      "join": "AND"
    },
    {
      "field": "MoveType",
      "value": "Appointment",
      "join": "OR"
    },
    {
      "field": "Organisation",
      "value": "3f2a9c1e-7b4d-4e8a-9c21-5d6e7f8a9b01",
      "join": "AND"
    }
  ],
  "limit": 15
}
```

> **Try it small first** When a filter mixes AND and OR, run it with `limit` set to 5 and check the events match what you meant. `applied.conditions` in the response echoes exactly what was sent.

## Fields that need a reference

`Organisation`, `Profile` and `Occupation` take a UUID reference, never a name. Resolve the name first with [resolve_search_terms](/docs/mcp/tools/resolve_search_terms) (companies and job titles) or [search_people](/docs/mcp/tools/search_people) (people). Each suggestion comes back with a `field` and a `value` you can drop straight into a condition.

## Date ranges

`PublishedDate` is the only field that takes `>`, `<`, `>=` and `<=`. Two conditions make a range.

CFO moves in August 2026:

```json
{
  "conditions": [
    {
      "field": "EventType",
      "value": "move"
    },
    {
      "field": "Occupation",
      "value": "7d0e1f2a-b4c5-4d6e-9f8a-b9c0d1e2f3d1",
      "join": "AND"
    },
    {
      "field": "PublishedDate",
      "operator": ">=",
      "value": "2026-08-01",
      "join": "AND"
    },
    {
      "field": "PublishedDate",
      "operator": "<=",
      "value": "2026-08-31",
      "join": "AND"
    }
  ],
  "limit": 15
}
```

## Invalid conditions are refused

An unknown field, an ordering operator on a field other than `PublishedDate`, a name where a UUID belongs, or a value outside a field's allowed list comes back as a tool error naming the condition. Nothing is quietly dropped, so a result set always matches every condition you gave.

A refused condition:

```json
{
  "error": "condition 2 (Organisation) needs a UUID, got \"Halberd\". Use resolve_search_terms to turn a company name into its UUID."
}
```

## Reading the response

| Key | What it holds |
| --- | --- |
| `events` | A short projection of each event. Pass `detail: "full"` for whole payloads, or open one with [get_event](/docs/mcp/tools/get_event) |
| `pagination` | `total` and `total_pages`, corrected from the page itself when a short page proves the end |
| `applied.conditions` | The conditions exactly as they were applied |
| `applied.date_window` | Any history window your plan imposes, stated in words |

## Paging

Keep `limit` small (the default is 15, the maximum 50) and page with `page`. Stop when a page returns fewer events than `limit`. On very broad filters the total can be an estimate for the whole table, so a short page is the reliable end signal.

## Related

- https://chikaraintel.com/docs/mcp/tools/search_events.md
- https://chikaraintel.com/docs/concepts/search-and-filtering.md
- https://chikaraintel.com/docs/mcp/tools/resolve_search_terms.md
