Search events
search_eventssearch_events searches your organization's timeline: deterministic full-text over the content and resolution of every event, composed with business filters (status, tags, person, provenance) and time windows (occurrence, deadlines, updates, resolution), paginated with a keyset cursor.
How matching works
The query runs as PostgreSQL full-text search over each event's content and resolution note. Matching is accent-insensitive (a query typed without accents matches accented stored text and vice versa), whole-word and stemmed. Quoted phrases are matched as phrases. It is not a substring search: "dev" does not match "devis".
- Every parameter is optional and all filters compose with AND: a call passing status, tags and due_before returns only events satisfying all three families at once.
- Range pairs form windows: due_before and follow_up_before are exclusive (strictly before that day); every other bound (since/until, *_after, updated_*/resolved_*) is inclusive. Deadline bounds are day strings (YYYY-MM-DD) compared in UTC.
- tags takes comma-separated slugs and matches events carrying ANY of them (union) by default; set match_all_tags to true to require ALL of them (intersection).
- Results are sorted by occurrence date, newest first, with the event id as a stable tiebreaker. One exception: status=needs_followup sorts by last update, stalest first.
- limit defaults to 20 and is capped at 50 per page.
- next_cursor is an opaque keyset token: pass it back as cursor to fetch the next page. It is null on the last page. Unlike OFFSET pagination, rows inserted or deleted between two pages never shift the page boundaries; an update that changes an event's sort timestamp can still move it across pages.
- A call with no filters is valid: it returns the most recent top-level events, newest first. Sub-events are excluded unless exclude_children is set to false.
Parameters
Everything the tool accepts, straight from the live inputSchema. All parameters are optional.
| Parameter | Type | Description |
|---|---|---|
| query | string | Full-text search over event content and resolution. Accent-insensitive, stemmed, multi-word; quote a phrase to match it as a phrase. Not a substring search. |
| status | string | Filter by lifecycle status: open, in_progress, closed, active (open + in_progress) or needs_followup (open or in progress with no update for 7 days). |
| tags | string | Comma-separated tag slugs. Matches events carrying ANY of them, unless match_all_tags is true. |
| match_all_tags | boolean | When true, only events carrying ALL the passed tag slugs match (intersection). Default false = ANY (union). |
| person_name | string | Only events linked to a person whose name contains this text (case- and accent-insensitive substring). Composes with every other filter and holds across all pages. |
| person_id | string | Only events linked to this exact person id (from search_persons or get_person_context) — no name ambiguity. |
| since | string | ISO date: only events that occurred at or after this moment. |
| until | string | ISO date: only events that occurred at or before this moment. |
| origin | "human" | "agent" | Filter by who wrote the event: human or agent. |
| source_type | string (<= 50) | Provenance system to filter on: gmail, calendar, slack, and so on. |
| source_id | string (<= 500) | External id in the source system. Pair with source_type to check whether an external item was already captured — the same idempotency key create_event dedupes on. |
| related_to | string | Only events linked to this event id. |
| exclude_children | boolean | Excludes sub-events. Defaults to true; set it to false to include thread children. |
| days_old | number | With status=needs_followup, overrides the 7-day staleness cutoff. |
| due_after | string | Day (YYYY-MM-DD): only events with a due date on or after this day. Pair with due_before for a window. |
| due_before | string | Day (YYYY-MM-DD): only events with a due date strictly before this day. |
| due_today | boolean | When true, only events whose due date is the current UTC day. |
| follow_up_after | string | Day (YYYY-MM-DD): only events with a follow-up date on or after this day. |
| follow_up_before | string | Day (YYYY-MM-DD): only events with a follow-up date strictly before this day. |
| follow_up_today | boolean | When true, only events whose follow-up date is the current UTC day. |
| updated_since | string | ISO timestamp: only events updated at or after this time — the incremental "what changed since" re-fetch. |
| updated_until | string | ISO timestamp: only events updated at or before this time. |
| resolved_since | string | ISO timestamp: only events resolved (closed) at or after this time. |
| resolved_until | string | ISO timestamp: only events resolved (closed) at or before this time. |
| limit | number | Maximum results per page. Defaults to 20, capped at 50. |
| cursor | string | Opaque pagination cursor from a previous response's next_cursor. Omit it for the first page. |
Response
Each event comes back compact: content, status, occurrence and resolution timestamps, deadlines, provenance, visibility, plus the linked persons, tags and related events in the same payload. next_cursor carries the pagination state.
{
"events": [
{
"id": "6f4d1c2e-8a3b-4f6e-9d2a-1b5c7e9f0a3d",
"content": "Quote DEV-2031 sent - follow up on Friday",
"status": "OPEN",
"occurred_at": "2026-07-12T09:30:00Z",
"resolved_at": null,
"due_date": "2026-07-24",
"follow_up_date": null,
"source_type": null,
"source_id": null,
"origin": "human",
"visibility": "organization",
"children_count": 0,
"persons": [
"Marie Dubois"
],
"tags": [
"quote"
],
"related_events": []
}
],
"next_cursor": "MjAyNi0wNy0xMlQwOTozMDowMFp8NmY0ZDFjMmUt..."
}{
"events": [],
"next_cursor": null
}{
"type": "object",
"properties": {
"events": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"content": {
"type": "string"
},
"status": {
"type": "string"
},
"occurred_at": {
"type": "string"
},
"resolved_at": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"due_date": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"follow_up_date": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"source_type": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"source_id": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"origin": {
"type": "string"
},
"visibility": {
"type": "string"
},
"children_count": {
"type": "number"
},
"persons": {
"type": "array",
"items": {
"type": "string"
}
},
"tags": {
"type": "array",
"items": {
"type": "string"
}
},
"related_events": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"content_preview": {
"type": "string"
},
"status": {
"type": "string"
}
},
"required": [
"id",
"content_preview",
"status"
],
"additionalProperties": false
}
},
"age_days": {
"type": "number"
}
},
"required": [
"id",
"content",
"status",
"occurred_at",
"resolved_at",
"due_date",
"follow_up_date",
"source_type",
"source_id",
"origin",
"visibility",
"children_count",
"persons",
"tags",
"related_events"
],
"additionalProperties": false
}
},
"next_cursor": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
]
}
},
"required": [
"events",
"next_cursor"
],
"$schema": "http://json-schema.org/draft-07/schema#",
"additionalProperties": false
}Usage examples
Find an exact reference
{
"query": "\"DEV-2031\""
}{
"events": [
{
"id": "6f4d1c2e-...",
"content": "Quote DEV-2031 sent - follow up on Friday",
"status": "OPEN",
"occurred_at": "2026-07-12T09:30:00Z",
"persons": [
"Marie Dubois"
],
"tags": [
"quote"
]
}
],
"next_cursor": null
}Filter by person, status and period
{
"person_name": "dubois",
"status": "open",
"since": "2026-05-01",
"until": "2026-06-30"
}{
"events": [
{
"id": "83a9e0b1-...",
"content": "Called Marie Dubois about the delayed delivery",
"status": "OPEN",
"occurred_at": "2026-06-18T14:05:00Z",
"persons": [
"Marie Dubois"
],
"tags": [
"delivery"
]
}
],
"next_cursor": null
}Deadlines between two dates
{
"due_after": "2026-07-20",
"due_before": "2026-08-01"
}{
"events": [
{
"id": "6f4d1c2e-...",
"content": "Quote DEV-2031 sent - follow up on Friday",
"status": "OPEN",
"occurred_at": "2026-07-12T09:30:00Z",
"due_date": "2026-07-24",
"persons": [
"Marie Dubois"
],
"tags": [
"quote"
]
}
],
"next_cursor": null
}What changed since a date
{
"updated_since": "2026-07-15T00:00:00Z"
}{
"events": [
{
"id": "c47d22e8-...",
"content": "Order #1842 picked up in store",
"status": "CLOSED",
"occurred_at": "2026-07-16T10:12:00Z",
"resolved_at": "2026-07-16T10:15:00Z",
"persons": [
"Pierre Morel"
],
"tags": [
"order"
]
}
],
"next_cursor": null
}What was closed over a period
{
"resolved_since": "2026-06-01T00:00:00Z",
"resolved_until": "2026-07-01T00:00:00Z"
}{
"events": [
{
"id": "1d9b47f0-...",
"content": "Size exchange handled for the blue coat",
"status": "CLOSED",
"occurred_at": "2026-06-03T16:40:00Z",
"resolved_at": "2026-06-10T11:00:00Z",
"persons": [
"Julie Bernard"
],
"tags": [
"after-sales"
]
}
],
"next_cursor": null
}Require every tag
{
"tags": "vip,follow-up",
"match_all_tags": true
}{
"events": [
{
"id": "5e2fa6c3-...",
"content": "Reserved the new collection for a fitting session",
"status": "IN_PROGRESS",
"occurred_at": "2026-07-08T15:20:00Z",
"persons": [
"Julie Bernard"
],
"tags": [
"vip",
"follow-up"
]
}
],
"next_cursor": null
}Fetch the next page
{
"query": "delivery",
"cursor": "MjAyNi0wNi0xOFQxNDowNTowMFp8ODNhOWUw..."
}{
"events": [
{
"id": "0b7c91d4-...",
"content": "Delivery slot confirmed for the sideboard",
"status": "OPEN",
"occurred_at": "2026-06-02T09:00:00Z",
"persons": [
"Pierre Morel"
],
"tags": [
"delivery"
]
}
],
"next_cursor": null
}Guarantees and limits
- Both searches are deterministic PostgreSQL full-text search: same query, same data, same results. Nothing semantic, no vector matching — the query matches words, not meanings.
- Visibility is resolved before matching: the server first scopes the query to the records the caller may see, and only then looks for matches.
- A record you cannot see is indistinguishable from a record that does not exist: search never confirms the existence of hidden data.
- Indexes are recomputed in the same transaction as every write: an entry is searchable the instant it is created, with no background indexer and no consistency window.
- Historis runs no AI model to find results. Your agent decides what to search for and ranks or interprets what comes back; the matching itself is plain database search.
Two searches, two jobs
- search_events
- Searches the timeline: what happened, what is due, what changed. Composes full-text with business and time filters, keyset-paginated.
- search_persons
- Searches the contact records and the relationship graph: who someone is, how they are connected, which role links them.