The chat copilot (/ai)
This is the in-app assistant — the thing that’s aware of whatever screen you’re on and can act on the CRM, not just describe it. A request carries page_context (which page, which deal/account ID, what’s visibly on screen) so the model can resolve “this deal” or “the account” without you re-stating an ID every turn.
It’s read and write. Alongside read tools (list accounts, contacts, deals, leads, activities, files), it can commit real changes on the caller’s behalf:
- Create an account —
POST /ai/accounts/commit, optionally with a batch of contacts attached. The create-account chat flow can also enrich a bare company name or domain into a full profile (via Apollo, falling back to model inference) before the user confirms. - Log an activity —
POST /ai/activities/commit(call, email, meeting, note, or task). - Create a deal —
POST /ai/deals/commit, with existing or newly-created contacts linked to it.
deals:manage for a deal commit), not a side effect of the chat endpoint’s own scope — the model proposes, but the underlying write still has to clear the same authorization a direct API call would. Every turn — including tool calls and their inputs/outputs — is persisted to a conversation history for audit purposes.
Smaller helper endpoints round this out: deal name suggestions, deal description polishing, and company identity resolution (/ai/resolve-company) — the same routine the create-account flow and the manual account form both use, so a company resolves to the same name/domain/industry regardless of entry point.
The analytics assistant (/assistant)
This is a completely different feature that happens to share the same SSE contract, so a single stream reader works against both. It’s a guarded text-to-SQL loop: Claude is given the allowlisted schema for the org’s CRM data and a single run_sql tool, writes one read-only SELECT, and the backend validates and executes it as a least-privilege assistant_ro Postgres role — row-level-security-scoped to the caller’s org, row-capped, and timed out. There is no write path here at all.
It exists to answer ad-hoc business questions — pipeline health, win rate, rep performance, account health — in plain language, rendering the result as a table, chart, or KPI widget. The system prompt deliberately keeps the experience non-technical: it never surfaces SQL, table/column names, or the fact that a database is involved.
Why two features, not one
The copilot’s job is to take actions a user is already allowed to take, faster — it’s scoped to whatever CRM manage permissions the caller already has, one commit endpoint at a time. The analytics assistant’s job is open-ended read access across the org’s business data — necessarily a much wider surface — so it’s deliberately walled off from any write capability and run under its own restricted DB role rather than the caller’s. Merging them would mean either the copilot inherits broad ad-hoc SQL, or the analytics assistant inherits write access — neither is a trade worth making. Both require an active organization on the token. The copilot needs a CRM read scope at minimum (more, per commit endpoint); the analytics assistant needs thedata:access scope specifically — see Scopes.
See the AI section of the APIs tab for /ai/* endpoint-level detail, and the Assistant section for /assistant/*.