# Access and permissions

Source: https://docs.havilo.ai/mcp/permissions

An MCP connection grants access to one workspace. It does not create a second permission system and it does not turn the OAuth scope into blanket authority.

## Three checks apply

Every request is constrained by all three of these boundaries:

1. **Connection profile.** The registered customer client can enter only the workspace MCP surface.
2. **Workspace grant.** The grant stores the exact workspace and membership chosen during authorization. An [API key](https://docs.havilo.ai/mcp/api-keys.md) is bound to its workspace and acts through its creator's membership.
3. **Live permissions.** The called tool checks the permissions attached to that membership's current role and permission sets. Through an API key, a permission counts only when the key grants it too.

The result is an intersection, not an escalation. A valid token cannot exceed the bound workspace or the person's current Havilo permissions.

## The catalog stays stable

The customer server registers a stable tool catalog. Permission changes do not add or remove tools from it. Instead, Havilo evaluates the relevant permission when a tool is called.

For example, someone may still see `crm_notes_upsert` after `NOTE_UPDATE` is removed. A subsequent update attempt will fail authorization. `workspace_capabilities` shows the effective permissions currently available to the connection.

## Workspace isolation

Customer tool schemas do not accept `workspace_id`. Havilo injects the workspace stored on the OAuth grant or API key into every data, AI, import, and job operation.

Entity, relationship, Note, and job identifiers are checked inside that workspace. Knowing an identifier from another workspace does not make it readable through the connection.

If the user, membership, workspace, OAuth client, or grant becomes inactive, authentication fails. If only a granular permission changes, affected tool calls fail while still-authorized calls continue to work.

## CRM field visibility

Workspace CRM reads apply the caller's current role restrictions as well as `ENTITY_READ`.
`crm_records_get` and `crm_records_search` omit hidden scalar and list fields, including fields on
linked records. Search does not match against hidden fields, and `crm_schema_get` omits their
definitions, options, and value counts. This also applies to the in-app agent's CRM tools.
Entity-type, field, and relationship-type previews also filter any returned field definitions.

Record write previews and field suggestions cannot inspect a hidden field's current value. Requests
targeting an inaccessible field are rejected before preview or application. Workspace owners and
Administrators retain their existing field access; the Name field remains visible.

## Preview and apply permissions

Consequential tools use `apply: false` by default. Preview usually requires read permission so an agent can inspect the proposed target and result. Apply requires the corresponding write permissions and can therefore fail even when preview succeeded.

Examples:

* previewing an Entity upsert requires `ENTITY_READ`;
* updating Entities by `record_id` requires `ENTITY_UPDATE`, or `ENTITY_UPDATE_LINKED` when every record is linked to the caller through a `UserEntityLink`;
* filing a field suggestion with `crm_fields_suggest` requires `ENTITY_READ`;
* creating an Entity requires `ENTITY_CREATE` and `ENTITY_UPDATE`;
* previewing a note upsert requires `NOTE_READ`;
* creating a note requires `NOTE_CREATE` and `NOTE_UPDATE`;
* applying a CSV import requires `BULK_OPERATIONS`, `ENTITY_CREATE`, and `ENTITY_UPDATE`.

Reasoning requires `AI_FEATURE_ACCESS`. Reach is not a tool argument on this surface — `ai_plan` and `ai_run` always work within the connected workspace — so `VIEW_GLOBAL_NETWORK` is only reachable through the `xray` and `global_search` modes of `ai_runs_start`.

See [Tool reference](https://docs.havilo.ai/mcp/tools.md) for the exact requirement on every mutation.
