Accept-Profile: ai (read) or Content-Profile: ai (write) to target the ai schema instead of the default public.
Use the Service Role key from a trusted backend for these recipes. They all touch project-wide data and are not RLS-safe for direct browser access. If you want to expose any of this to end users, gate it behind your own API.
Recipe 1 — Custom hybrid search over ai.chunks
The typedPOST /api/knowledge-bases/{id}/search runs the platform’s hybrid retriever for you. If you want different filters, different ranking, or to merge results across multiple KBs in one query, drop down to direct PostgREST.
This recipe fetches chunks across two KBs, scoped to a single source’s metadata tag, ordered by an extra score column you’ve populated yourself.
in.(...) filter selects across multiple KBs in one query — no application-side fan-out. The meta->>tag=eq.policy selector reaches into the meta JSONB column with the standard Postgres ->> operator; PostgREST honors that syntax in filter expressions.
Recipe 2 — Usage analytics on agent_runs
Build a dashboard query that shows, for each agent, the run count, average input/output token counts, and last-run timestamp over the last 7 days. The typed/api/agents doesn’t aggregate; PostgREST + a server-side RPC does it in one round-trip.
The simplest path is to define a SQL view or a Postgres function on ai, then call it via POST /rest/v1/rpc/{function}. Here’s the function:
SECURITY DEFINER lets the function run with the owner’s permissions rather than the caller’s — useful here because the aggregate doesn’t need per-user filtering and you want consistent results regardless of which key calls it. Set search_path = ai explicitly to avoid the search-path SECURITY DEFINER pitfall.
Recipe 3 — Bulk-tag sources
You imported 200 sources last month and want to attach a{"tag": "q3-2025"} JSONB key to all of them in one round-trip. The typed PATCH /api/sources/{id} is one-source-per-call; PostgREST supports a single filter+update.
Prefer: return=representation makes PostgREST return the updated rows so you can confirm the count. Beware: the meta write here replaces the whole JSONB column rather than merging. For partial-update semantics, use jsonb_set via an RPC, or read-modify-write per row.
Recipe 4 — Cross-KB analytics with embeds
For a “which sources contribute most to this KB” report, you want eachindexed_sources row joined to its sources row (for the name and file type) and a chunk count. PostgREST’s embed syntax does this in one request:
source:sources(name,file_type) aliases the joined sources row as source in the response. chunks(count) does a count-only embed — PostgREST recognizes count as a special function and emits [{"count": N}] rather than the rows themselves. You can stack embeds, embed embeds, and filter on them — see the PostgREST reference for the full filter and embed grammar.
Next steps
Querying the ai schema
The framing and tables behind these recipes.
RLS Cookbook
Five tightening patterns when “all authenticated users see everything” isn’t what you want.
PostgREST Reference
Filter operators, embeds, headers, and the response/error contract.
Connection Pooling
What changes when you connect through PgBouncer’s transaction-mode pooler instead.