> ## Documentation Index
> Fetch the complete documentation index at: https://sdk.revise.io/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 a stance the user moves between — drafting, or marking up. A
**role** is a boundary they cannot cross from inside the editor.

```tsx theme={null}
<ReviseEditor
  role="suggester"
  initialDocuments={[
    { id: "contract-1", docx: file },
    { id: "exhibit-a", docx: 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

Settling someone's tracked change is an authoring act. A reviewer who could
accept their own markup would be an editor wearing a different hat, and the
document would lose the property that makes suggest-only useful: that every
change in it was approved by somebody else.

A suggester still navigates review, previews accept/reject non-destructively,
and comments — they just cannot commit.

## What "enforced" means

The role is applied at every surface, not just the ones your UI happens to
render:

* **Typing and the toolbar** — the document sits in a mode the role permits,
  and `setDocumentMode()` clamps rather than obeys. Asking for `"editing"` as
  a suggester yields `"suggesting"`, and the `onDocumentModeChange` callback
  reports what actually happened.
* **The built-in chrome** — the mode toggle disappears when the role has one
  mode; the ribbon is handed navigation without its accept and reject
  commands; the inline accept/reject tooltip on each suggestion is suppressed.
* **The handle** — `review.acceptCurrent()`, `acceptAll()`,
  `acceptSuggestions()`, and their reject counterparts return `false` or a
  decision with every ID `unresolved`
  rather than acting. Comment mutations no-op 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 a suggester
  cannot make. A delegated `agent.run()` is checked the same way, since one
  call is a stream of mutations.

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

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

<Note>
  Direct mode is the one an integration trips over: it is how an agent applies
  a change without leaving a tracked-change trail, so a suggester is refused
  it. Run the same tool without `directMode` and the edit lands as a
  suggestion.
</Note>

## Inspecting 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 constrains what
  this browser can do through the SDK; it does not authenticate anybody. In a
  shared room, participants set their own roles — if a role must be
  authoritative, your server has to say so, and your transport has to refuse
  writes that contradict it. Revise has no server in this picture.
</Warning>
