/api/* surface gives you opinionated, RPC-style access to the AI features. Underneath, all the state those endpoints manage lives in a single Postgres schema, ai, and PostgREST exposes that schema as a standard REST CRUD surface at /rest/v1/*.
That means everything you can do with /api/agents, /api/sessions, /api/runs, /api/knowledge-bases, etc., you can also do directly against the underlying tables. SQL-style filters, joins, JSONB selectors, pagination via Content-Range, embeds: all the PostgREST machinery that powers public.* access works for ai.* too. This is the right tool when:
- You need filters or joins the typed endpoint doesn’t expose (e.g., “all runs from the last 7 days where the agent’s
system_promptcontains ‘refund’”). - You’re building a dashboard and want one query for usage analytics instead of N round-trips.
- You’re bulk-tagging or migrating sources and don’t want to call
PATCH /api/sources/{id}in a loop. - You want Realtime subscriptions on a state change the typed API doesn’t broadcast.
/api/* for state-changing operations on dynamic state, and PostgREST for reads and for writes on static, user-owned configuration.
How ai.* is exposed
PostgREST is configured to serve four schemas: public, storage, graphql_public, ai. The ai schema sits alongside your own public schema as a fully routed PostgREST surface. Every table follows the standard PostgREST URL pattern:
ai schema instead of public, add the Accept-Profile (read) or Content-Profile (write) header:
Accept-Profile, PostgREST looks in the default public schema and 404s.
Tables in the ai schema
Every state-bearing entity in the AI surface has a corresponding table. The full list (35 tables):
Schemas can grow; consult
GET /rest/v1/ with Accept-Profile: ai for the live OpenAPI spec PostgREST emits.
RLS posture
Everyai.* table has Row Level Security enabled. The default policies are:
service_role: full access on every table (FOR ALL USING (true) WITH CHECK (true)). This is what the platform’s own backend uses.authenticated(any signed-in GoTrue user): read access on every table. Write access on most user-configurable tables (agents, workflows, tools, knowledge_bases, sources, etc.). Per-user filtering on session-shaped tables (agent_sessions,agent_runs,orchestration_sessions,orchestration_runs) viaauth.uid() = user_id.anon: no access. Queryingai.*with the Anon (Publishable) Key returns empty result sets.
See the RLS Model page for how
auth.uid(), auth.jwt(), and the role mapping work, and the RLS Cookbook for concrete tightening patterns.
When to use PostgREST vs /api/*
Anything where the platform runs side-effects (Celery dispatch, LLM calls, file processing) belongs on the typed surface. Everything read-only or static-config belongs anywhere.
Next steps
ai-schema recipes
Four worked examples: hybrid search on chunks, usage analytics on runs, bulk source tagging, cross-KB joins.
RLS Model
How roles, JWT claims, and auth.uid() compose to gate ai.* and public.* access.
PostgREST Reference
Filter operators, embeds, headers, error responses.
Database Access
The three ways into your project’s Postgres — typed API, PostgREST, direct connection.