> ## Documentation Index
> Fetch the complete documentation index at: https://revise.io/developer/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Roles and permissions

> What a participant is allowed to do, enforced everywhere.

A **mode** is something the user switches between: editing or suggesting. A
**role** limits what the user can do. It cannot be changed from inside the
editor.

```tsx theme={null}
<ReviseEditor
  role="suggester"
  initialDocuments={[
    { id: "contract-1", source: file },
    { id: "exhibit-a", source: exhibit, role: "viewer" }, // per document
  ]}
/>
```

| Role | Edits | Direct edits | Accept / reject | Comments |
| - | - | - | - | - |
| `editor` (default) | Yes | Yes | Yes | Yes |
| `suggester` | As tracked suggestions only | No | **No** | Yes |
| `viewer` | No | No | No | No |

## Why a suggester cannot accept

Accepting a tracked change is an edit. If a suggester could accept their own
suggestions, every change could skip review by someone else.

A suggester can still navigate review, preview accept and reject without
changing the document, and comment.

## Enforcement

The role applies to every surface:

* **Typing and the toolbar.** The document stays in a mode the role permits.
  `setDocumentMode()` clamps the requested mode: asking for `"editing"` as a
  suggester sets `"suggesting"`. The `onDocumentModeChange` callback reports
  the resulting mode.
* **The built-in chrome.** The mode toggle is hidden when the role has one
  mode. The ribbon shows review navigation without accept and reject. The
  inline accept and reject tooltip on each suggestion is hidden.
* **The handle.** `review.acceptCurrent()`, `acceptAll()`,
  `acceptSuggestions()`, and the reject counterparts return `false` or a
  decision with every ID `unresolved`. Comment mutations do nothing for a
  viewer.
* **Agent tools.** `tools.execute()` returns
  `{ ok: false, error: { code: "role_not_permitted" } }` for a mutation a
  viewer cannot make, and for any `directMode` call from a suggester. A
  delegated `agent.run()` is checked the same way.

```ts theme={null}
import { ReviseRoleError } from "@reviseio/editor";

try {
  await editor.tools.execute("replace", input, { directMode: true });
} catch (error) {
  if (error instanceof ReviseRoleError) {
    // error.role === "suggester", error.action === "apply changes directly"
  }
}
```

<Note>
  `directMode` applies a change without a tracked change, so a suggester cannot
  use it. Without `directMode`, the same tool call adds the edit as a
  suggestion.
</Note>

## Read the role

```ts theme={null}
editor.getRole();             // active document
editor.getRole("exhibit-a");  // a specific one
```

<Warning>
  A role is a UI and API boundary, not a security control. It limits what this
  browser can do through the SDK. It does not authenticate anyone. In a shared
  room, each participant sets their own role. To make a role authoritative,
  your server must assign it and your transport must reject writes that
  violate it. Revise does not run a server here.
</Warning>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.