Skip to main content
Once a few plugins are connected, corsair_entities holds your Slack messages, GitHub issues, Notion pages, and Gmail threads in one table with one shape. That’s a corpus you can search. Ask “what’s happening with the Peterson report?” and you can look across every synced service at once, then hand the matches to a model as grounding, instead of calling six search APIs and reconciling six response formats. Pattern: search .db across entity types, rank in your code, then feed matches to the model as context.

Searching one entity type

Use the typed client when you know where to look. String fields support contains, which compiles to a SQL LIKE '%value%':
Filters combine with AND, so you can narrow by more than text:

Searching across services

The typed clients are scoped to one entity type at a time. To sweep the whole corpus, fan out and merge:
src/server/search.ts
For a genuinely open-ended sweep, query corsair_entities directly with your own ORM. It’s one table, so a single statement covers every service and entity type at once, which is useful when you don’t want to enumerate the plugins up front:
Scope that query to the tenant’s account_id values. The typed clients do this for you; raw SQL does not.

Grounding a model

Retrieval is the whole trick. Pass matches in as context and require citations back to the source URL:
Because rows are kept current by API calls and webhooks, answers reflect the present state rather than whenever you last built an index.

Filling the corpus

Search only finds what’s been synced. Two ways rows get there:
  1. Backfill. Call .api list endpoints once per source. Every response is upserted, so a paginated crawl populates the table.
  2. Stay fresh. Register webhooks so upstream changes update the same rows in place. See Webhooks.

Know the limits

contains is substring matching, not semantic search. It won’t match synonyms, handle typos, or rank by relevance.
That’s fine for names, IDs, and known phrases. For conceptual questions, keep embeddings in your own table keyed to corsair_entities.id, and use Corsair search to fetch the current text for whatever your vector query returns. The row your embedding points at keeps updating itself, so you re-embed on change rather than rebuilding from scratch. One more constraint. search filters top-level data fields only, and has no sort option. Order results in your code, or use SQL.

Checklist

  • Reads go through .db, never .api, so questions don’t burn rate limit.
  • Raw corsair_entities queries are filtered to the tenant’s accounts.
  • Answers cite source URLs from the entity data.
  • The corpus is backfilled once and maintained by webhooks.
  • Semantic needs are handled with embeddings alongside, not by contains.

What’s next

Database

The entity table, and how to join it to your own.

Give it to an agent

Let the model search and then act on what it finds.

Webhooks

Keep the corpus current without polling.

Multi-tenancy

Keep each customer’s corpus isolated.