---
title: "Permissions — React"
description: "A document can be opened with less than full power. Every plugin tells you what the user may do, so your UI only offers what works."
framework: "React"
source: "https://www.cloudpdf.com/docs/headless/react/concepts/permissions"
---

# Permissions

A document can be opened with less than full power: a link that lets people read and comment, a
signing session that only fills in fields, a reviewer who may not download. The engine refuses
everything else. This page is about building a UI that **shows only what works**, instead of buttons
that fail.

## Every verb has a check

Every call that can be refused has a check next to it, named `can` and the verb. It answers one
question: would this work right now, for this user?

```ts
annotation.canCreate(); // may they draw new annotations?
annotation.canUpdate(ref); // may they change this one?
selection.canCopy(); // may selected text leave the document?
form.canFill(ref); // may they fill in this field?
documents.canDownload(); // may they download the file?
```

A check already includes everything its call looks at: the user's permissions, the flags on the
document (a locked annotation, a read-only field) and what's selected. You never combine them
yourself, and you never read permission names: which permission a call needs is the plugin's
business, not yours.

> **The rule:** show a button when its check is true. Read the check while you render, like any
> other state: the answer changes when the selection changes or the user gets new permissions.

**`annotation-actions.tsx`**

```tsx
import { useAnnotation, type AnnotationRef } from '@embedpdf/react/annotation';
import { DrawingTools } from './drawing-tools';

export function AnnotationActions({ annotationRef }: { annotationRef: AnnotationRef }) {
  const annotation = useAnnotation();

  return (
    <>
      {annotation.canCreate() && <DrawingTools />}
      <button disabled={!annotation.canDelete(annotationRef)}>Delete</button>
    </>
  );
}
```

## One answer per annotation

Checks that take a `ref` answer for that one annotation, field or comment. In a shared document, a
user may hold `annotations:update:self`: change their own annotations, and only look at the rest.
You don't handle that yourself. Someone else's annotation answers `canUpdate(ref) === false`, and
shows the way a locked one does: it can be selected and commented on, but has no handles. Forms work
the same: a field the user may not fill shows as read-only.

## Calling anyway is safe

Checks are there to keep your UI honest; the engine doesn't rely on them. A refused call rejects,
the same way in every plugin:

**`permission-denied.tsx`**

```tsx
import { isPluginError } from '@embedpdf/react/runtime';
import { toFieldRef, useForm } from '@embedpdf/react/form';

export function SetTotalButton() {
  const form = useForm();

  const setTotal = async () => {
    try {
      await form.setValue(toFieldRef('total'), { value: '120.00' });
    } catch (error) {
      if (isPluginError(error, 'permission-denied')) {
        console.log(error.permission); // what was missing, such as 'doc.forms.fill'
      }
    }
  };

  return <button onClick={setTotal}>Set the total</button>;
}
```

The local and the cloud engine refuse the same way, so this one check covers both.

What people do with the mouse fails quietly instead: without permission to create, drawing with a
tool paints nothing and saves nothing.

## Where the limits come from

A document gets its permissions when it opens, together with who the user is.

On CloudPDF, the user's token carries both, so your server decides and the viewer takes nothing. See
[What a token carries](https://www.cloudpdf.com/docs/engine/authentication#what-a-token-carries).

## Every check

Each plugin's page has a Permissions section with the permission behind each check.

| Plugin                                                  | Checks                                                                                                                                   |
| :------------------------------------------------------ | :--------------------------------------------------------------------------------------------------------------------------------------- |
| [Documents](../documents/saving#permissions)            | `canDownload()` `canPrint()`                                                                                                             |
| [Rendering](../viewing/render#permissions)              | `canRender()`                                                                                                                            |
| [Page editing](../documents/pages#permissions)          | `canEdit()`, one answer for every page change; `canExtract()` for copying pages out                                                      |
| [Metadata](../documents/metadata#permissions)           | `canUpdate()`                                                                                                                            |
| [Selection](../text/selection#permissions)              | `canSelect()` `canCopy()`                                                                                                                |
| [Search](../text/search#permissions)                    | `canSearch()`, and `canSearch({ snippets: true })` for the words around each match                                                       |
| [Annotations](../annotations/with-code#permissions)     | `canRead()` `canCreate()` `canUpdate(ref)` `canDelete(ref)`; `selection.canGroup()` `selection.canUngroup()`                             |
| [Comments](../annotations/comments#permissions)         | `canReply(ref)` `canSetText(ref)` `canSetStatus(ref)` `canSetMarked(ref)` `canDelete(ref)` `canDeleteThread(ref)`                        |
| [Stamps](../annotations/stamps#permissions)             | `canPlace()` `canCreateFromAnnotations()`                                                                                                |
| [Measurements](../annotations/measurements#permissions) | `canMeasure(page)` `canCalibrate()`                                                                                                      |
| [Redaction](../annotations/redaction#permissions)       | `canMark()` `canUnmark(ref)` `canUpdateLabel(ref)` `canApply()`                                                                          |
| [Forms](../forms#permissions)                           | `canRead()` `canFill(ref)`; [building forms](../forms/building#permissions): `canDesign()` `canRestoreAttribution()` `canWriteScripts()` |
| [Signatures](../forms/signatures#permissions)           | `canSign(field)` `canCertify()` `canFill(field)` `canReadRevision()`                                                                     |

A [command](../ui/commands)'s `enabled` is where these checks go, so every toolbar button and
shortcut follows them.
