Skip to main content

Auth & scopes

MCP accepts two credentials: a sign-in from a chat app (ChatGPT, Claude, Gemini), or the same scoped agx_ API key as the REST API. Both use the same scope model.

Signing in from a chat app​

When someone connects Expedify from a chat app, they sign in with their Expedify account and approve the app on a consent screen.

  • The consent screen is the API key scope picker. Every scope below is shown. Scopes the person's role doesn't allow are greyed out and can't be ticked, and nothing is ticked in advance. Select all ticks only the allowed ones.
  • The app works as that person. Records are limited the same way as in the Expedify app. Someone who sees only their own contacts in Expedify sees only their own contacts through ChatGPT or Claude.
  • Permissions are checked on every call against what was ticked and what the person's role allows today. A demotion, a removed permission or leaving the organisation takes effect on the next call, without reconnecting.
  • The tool list is trimmed to what was ticked, so the app isn't offered tools it can't use. (For API keys and anonymous callers, tools/list stays the full catalog; see below.)
  • One organisation per connection. The general address /v1/mcp asks which organisation to connect. An organisation's own address, /v1/mcp/<org-slug>, connects that organisation directly. A sign-in for one organisation is refused on another organisation's address.
  • Sessions: access renews automatically every hour. A connection unused for 30 days needs a fresh sign-in.
  • Ending access: Settings → Connected Apps → Revoke. Owners and admins can revoke anyone's connection in their organisation.

Owners and admins can tick every scope, which equals an API key with all scopes, but the app still acts as that one person.

The key​

  • Sent as Authorization: Bearer agx_<key> on POST /v1/mcp (bare agx_… also accepted — see Connect).
  • Issued per organization in Settings → API Keys. The key carries that org's tenant binding — it can never touch another org's data.
  • Hash-only, shown once at creation. Rotate/revoke any time; the client is cut off instantly.

Scopes gate everything — default-deny​

A key, or a sign-in, holds a set of scopes. No scope → no access at execution time. The split is:

  1. Discovery (tools/list) is public — NOT scope-filtered. It returns the full static catalog — the same tools for everyone, key or no key — so an agent can see what's possible. Authenticating adds your org's exposed workflow tools; it does not remove any static tool.
  2. Execution (tools/call) is gated. Every call re-checks the required scope (and tenant + ACL) at operations.execute(). A tool you hold no scope for is refused when called — a read-only key can see create_contact in the list but gets a scope error if it tries to call it.
info

Think capabilities vs. authorization: the catalog advertises everything the server can do; your key's scopes decide what it's allowed to do. Discovery is public; execution is enforced.

Scope vocabulary​

Scopes are coarse {resource}:{action}. write / author / run imply read on the same resource — you don't grant the read counterpart separately.

CRM (per entity: contacts, companies, deals, tasks, notes, activities, products, deal_payments, deal_payment_schedules)​

ScopeGrants
<entity>:readView and list; search tools, get_…, list_… associations
<entity>:writeCreate / update / delete; link / unlink (implies read)

Association tools require the scope on both sides — e.g. link_contact_to_company needs contacts:write and companies:write.

The entity-agnostic tools — search_records, get_record, create_record, update_record, delete_record, link_records, and batch_create/update/delete_records, which take the entity as an argument — use one coarse pair instead:

ScopeGrants
records:readSearch/get any CRM record of any entity type
records:writeCreate/update/delete/link any record, including batch writes (implies read)

Workflows​

ScopeGrants
workflows:readList nodes, read/list workflows, validate, read executions
workflows:authorCreate, edit, import, activate/deactivate, delete (implies read)
workflows:runRun and test workflows and single nodes (implies read)

author and run are deliberately separate, so a key can be "build but not run" or "run but not edit". Execution is additionally confirm-gated (human-in-the-loop) — see Recipes.

Knowledge Base​

ScopeGrants
knowledge:readList KBs, read documents, search and ask (RAG)
knowledge:writeCreate/update/delete KBs and documents (implies read)

Analytics​

ScopeGrants
analytics:readList/read datasets, charts, dashboards; run queries/charts; NL→SQL; inspect schema
analytics:writeCreate/update/delete datasets, charts, dashboards (implies read)

Channels​

ScopeGrants
templates:read / templates:writeMarketing templates — list/read; create, update, delete
assets:read / assets:writeMarketing assets — list/read; upload, update, delete
segments:read / segments:writeSegments — list/read, evaluate & preview audiences; create, update, delete
campaigns:read / campaigns:writeCampaigns — list/read; create, update, cancel
campaigns:sendLaunch a campaign — real, mass, billable outbound (implies read)
communications:readRead voice calls, WhatsApp conversations & messages, SMS logs

campaigns:send is separate from campaigns:write so an agent can draft campaigns it can't fire. Sends are additionally confirm-gated.

Settings​

ScopeGrants
integrations:readBrowse the catalog; list/read connected integrations (never exposes credentials)
integrations:writeCreate an integration and run live connection tests — a real outbound call with stored credentials (implies read)
webhooks:read / webhooks:writeIncoming + outgoing webhooks, logs, event types; create/update/delete/test

Automation (Blueprints)​

ScopeGrants
blueprints:readRead/list a workflow's visual Blueprint and validate it
blueprints:writeCreate, replace, and clear a Blueprint (implies read)
ScopeGrants
search:readCross-entity find-by-name over workflows, charts, dashboards, and datasets

Mapping a tool to its scope​

Every tool in the catalog lists its Required scope. Rule of thumb:

  • search_… / get_… / list_… → …:read
  • create_… / update_… / delete_… / link_… / run_… / edit_… → the resource's write / author / run
Least privilege

Grant the narrowest set that gets the job done. A reporting agent needs only …:read scopes; a data-entry agent adds the relevant …:write. This is the same posture the REST API enforces — the Scopes reference is shared between both surfaces.