> For the complete documentation index, see [llms.txt](https://developers.flipsnack.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developers.flipsnack.com/api-reference/collection.setpermissions.md).

# collection.setPermissions

## Request method

`collection.setPermissions` uses **GET**.

Endpoint:

```
https://api.flipsnack.com/v1/
```

The `permissions` parameter is a JSON string. URL-encode it when sending the request.

## Request parameters

<table><thead><tr><th width="164.61328125">Name</th><th width="144.97265625">Type</th><th width="126.39453125">Required</th><th>Description</th></tr></thead><tbody><tr><td><code>action</code></td><td>string</td><td>Yes</td><td>Must be <code>collection.setPermissions</code>.</td></tr><tr><td><code>apiKey</code></td><td>string</td><td>Yes</td><td>Your Flipsnack API key.</td></tr><tr><td><code>signature</code></td><td>string</td><td>Yes</td><td>Request signature generated with your API secret.</td></tr><tr><td><code>collectionHash</code></td><td>string</td><td>Yes</td><td>The hash of the flipbook.</td></tr><tr><td><code>permissions</code></td><td>JSON string</td><td>Yes</td><td>JSON object with a <code>permissions</code> array. This replaces the existing access list.</td></tr></tbody></table>

## Permissions payload

The `permissions` parameter must be a JSON object with this structure:

```json
{
  "permissions": [
    {
      "type": "teammate",
      "group": "mygroup2",
      "notifyViaEmail": true
    }
  ]
}
```

To remove all private access rows, send an empty array:

```json
{
  "permissions": []
}
```

All permission objects in a non-empty `permissions` array must use the same `type`. For example, multiple `teammate` entries are valid in one request, but combining `teammate` and `otp` entries in the same request returns a bad request error.

## Permission object fields

<table><thead><tr><th width="171.53125">Name</th><th width="132.91015625">Type</th><th width="133.94140625">Required</th><th>Description</th></tr></thead><tbody><tr><td><code>type</code></td><td>string</td><td>Yes</td><td>Permission type. Possible values: <code>teammate</code>, <code>otp</code>, <code>sso</code>.</td></tr><tr><td><code>email</code></td><td>string</td><td>Conditional</td><td>Email address for <code>otp</code> or <code>teammate</code> access. Required for <code>otp</code>. Optional for <code>teammate</code>. Not supported for <code>sso</code>.</td></tr><tr><td><code>group</code></td><td>string</td><td>Optional</td><td>Group name. Available only for <code>teammate</code> and <code>sso</code>. <code>group</code> is a field, not a permission type.</td></tr><tr><td><code>notifyViaEmail</code></td><td>boolean</td><td>No</td><td>If <code>true</code>, sends notification emails for this permission entry during this <code>collection.setPermissions</code> call. If omitted, it is treated as <code>false</code>.</td></tr></tbody></table>

`email` and `group` cannot be used together in the same permission object.

## Permission types

### teammate

Grants access to accepted readers from the workspace.

<table><thead><tr><th width="402.359375">Shape</th><th>Description</th></tr></thead><tbody><tr><td><code>{ "type": "teammate" }</code></td><td>Grants access to all accepted readers from the workspace.</td></tr><tr><td><code>{ "type": "teammate", "email": "reader@example.com" }</code></td><td>Grants access to one accepted reader from the workspace.</td></tr><tr><td><code>{ "type": "teammate", "group": "mygroup2" }</code></td><td>Grants access to accepted readers from the specified workspace group.</td></tr></tbody></table>

If a teammate group name does not exist, that permission entry is skipped.

If a teammate email is not an accepted reader in the workspace, that permission entry is skipped.

### otp

Grants access through one-time password email authentication.

| Shape                                              | Description                                       |
| -------------------------------------------------- | ------------------------------------------------- |
| `{ "type": "otp", "email": "reader@example.com" }` | Grants OTP access to the specified email address. |

`email` is required for `otp`.

### sso

Grants access through SSO authentication.

| Shape                                    | Description                                                               |
| ---------------------------------------- | ------------------------------------------------------------------------- |
| `{ "type": "sso" }`                      | Grants access to all SSO-authenticated viewers for the workspace/profile. |
| `{ "type": "sso", "group": "mygroup1" }` | Grants access to SSO-authenticated viewers from the specified group.      |

`email` is not supported on `sso` permissions. Use `teammate` for per-email workspace reader access.

If an SSO group name does not exist, that permission entry is skipped.

## Notification behavior

`notifyViaEmail` can be used on any permission type:

```json
{
  "type": "teammate",
  "notifyViaEmail": true
}
```

For `{ "type": "teammate", "notifyViaEmail": true }`, all accepted readers from the workspace are notified.

For group or email entries, only the matching group members or email recipient are notified.

`notifyViaEmail` is evaluated for the current `collection.setPermissions` request. If you call `collection.setPermissions` again with `notifyViaEmail: true`, emails can be sent again.

If `notifyViaEmail` is omitted, it is treated as `false`.

`notifyViaEmail` is not returned by `collection.getPermissions`.

## Payload examples

Each request must send permissions of the same `type`.

### Teammate permissions

```json
{
  "permissions": [
    {
      "type": "teammate",
      "group": "mygroup2",
      "notifyViaEmail": true
    },
    {
      "type": "teammate",
      "email": "maya.reader@example.com",
      "notifyViaEmail": true
    },
    {
      "type": "teammate",
      "email": "noah.reader@example.com",
      "notifyViaEmail": false
    }
  ]
}
```

### OTP permissions

```json
{
  "permissions": [
    {
      "type": "otp",
      "email": "olivia.reader@example.com",
      "notifyViaEmail": true
    },
    {
      "type": "otp",
      "email": "ethan.viewer@example.com",
      "notifyViaEmail": false
    }
  ]
}
```

### SSO permissions

```json
{
  "permissions": [
    {
      "type": "sso",
      "group": "mygroup1",
      "notifyViaEmail": true
    }
  ]
}
```

## Request examples

### Set teammate permissions

```bash
PERMISSIONS='{
  "permissions": [
    {
      "type": "teammate",
      "group": "mygroup2",
      "notifyViaEmail": true
    },
    {
      "type": "teammate",
      "email": "maya.reader@example.com",
      "notifyViaEmail": true
    },
    {
      "type": "teammate",
      "email": "noah.reader@example.com",
      "notifyViaEmail": false
    }
  ]
}'

curl -X GET \
     -G \
     https://api.flipsnack.com/v1/ \
     -d action=collection.setPermissions \
     -d apiKey=<YOUR-API-KEY> \
     -d signature=<YOUR-REQUEST-SIGNATURE> \
     -d collectionHash=<COLLECTION-HASH> \
     --data-urlencode "permissions=${PERMISSIONS}"
```

### Set SSO permissions

```bash
curl -X GET \
     -G \
     https://api.flipsnack.com/v1/ \
     -d action=collection.setPermissions \
     -d apiKey=<YOUR-API-KEY> \
     -d signature=<YOUR-REQUEST-SIGNATURE> \
     -d collectionHash=<COLLECTION-HASH> \
     --data-urlencode 'permissions={"permissions":[{"type":"sso","group":"mygroup1","notifyViaEmail":true}]}'
```

### Give access to all accepted workspace readers and notify them

```bash
curl -X GET \
     -G \
     https://api.flipsnack.com/v1/ \
     -d action=collection.setPermissions \
     -d apiKey=<YOUR-API-KEY> \
     -d signature=<YOUR-REQUEST-SIGNATURE> \
     -d collectionHash=<COLLECTION-HASH> \
     --data-urlencode 'permissions={"permissions":[{"type":"teammate","notifyViaEmail":true}]}'
```

### Remove all permissions

```bash
curl -X GET \
     -G \
     https://api.flipsnack.com/v1/ \
     -d action=collection.setPermissions \
     -d apiKey=<YOUR-API-KEY> \
     -d signature=<YOUR-REQUEST-SIGNATURE> \
     -d collectionHash=<COLLECTION-HASH> \
     --data-urlencode 'permissions={"permissions":[]}'
```

## Response examples

The response returns the saved permissions after invalid/skipped entries are removed.

### Teammate response

```json
{
  "code": 20,
  "status": "OK",
  "data": {
    "permissions": [
      {
        "type": "teammate",
        "group": "mygroup2"
      },
      {
        "type": "teammate",
        "email": "maya.reader@example.com"
      }
    ]
  }
}
```

### SSO response

```json
{
  "code": 20,
  "status": "OK",
  "data": {
    "permissions": [
      {
        "type": "sso",
        "group": "mygroup1"
      }
    ]
  }
}
```

After removing all permissions:

```json
{
  "code": 20,
  "status": "OK",
  "data": {
    "permissions": []
  }
}
```

## Skipped entries

Some permission entries are skipped instead of returning an error:

<table><thead><tr><th width="573.43359375">Entry</th><th>Behavior</th></tr></thead><tbody><tr><td><code>teammate</code> with a non-existent <code>group</code></td><td>Skipped.</td></tr><tr><td><code>teammate</code> with an <code>email</code> that is not an accepted workspace reader</td><td>Skipped.</td></tr><tr><td><code>sso</code> with a non-existent <code>group</code></td><td>Skipped.</td></tr></tbody></table>

If all entries are skipped, the flipbook has no saved access rows and the response contains an empty `permissions` array.

## Validation errors

Invalid payload or permission objects return **code `40` (Bad Request)** with details in `data.errors`:

```json
{
  "code": 40,
  "status": "Bad Request",
  "data": {
    "errors": [
      "Permissions cannot combine multiple types"
    ]
  }
}
```

Examples:

| Case                                                              | Behavior                                                                                                 |
| ----------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
| `permissions` is missing or is not valid JSON                     | Code `30` (missing parameter) or code `40` with `Invalid permissions payload`.                           |
| `permissions.permissions` is not an array                         | Code `40` with `Invalid permissions payload`.                                                            |
| Unknown `type` value                                              | Code `40` with `Invalid permission type at index N (got '…')`.                                           |
| Multiple permission `type` values in the same `permissions` array | Code `40` with `Permissions cannot combine multiple types`.                                              |
| `email` and `group` are both set on the same permission           | Code `40` with `Permission at index N cannot have both email and group`.                                 |
| `group` is used with `otp`                                        | Code `40` with `Permission at index N cannot have group (type=otp; group allowed only on teammate/sso)`. |
| `otp` without `email`                                             | Code `40` with `Permission at index N requires an email`.                                                |
| `sso` with `email`                                                | Code `40` with `SSO permission cannot include an email - use teammate or user for per-email access`.     |
| Invalid email format                                              | Code `40` with `Invalid email at index N`.                                                               |
| Duplicate permission entry                                        | Code `40` with `Duplicate permission at index N`.                                                        |
| `notifyViaEmail` is not a boolean                                 | Code `40` with `Invalid permissions format.`.                                                            |
| OTP permissions not on plan                                       | Code `40` with `OTP permissions are not available on your plan`.                                         |
| SSO permissions not on plan                                       | Code `40` with `SSO permissions are not available on your plan`.                                         |
| SSO not enabled for workspace                                     | Code `40` with `SSO is not enabled for this workspace`.                                                  |

## Error responses

All errors return JSON with at least `code` and `status`. Validation failures on this action also include `data.errors` (string array).

### Request, auth, and access errors

<table><thead><tr><th width="113.25390625">Code</th><th width="269.734375">Status</th><th>When</th></tr></thead><tbody><tr><td><code>30</code></td><td>Missing mandatory parameter</td><td><code>apiKey</code>, <code>signature</code>, <code>action</code>, <code>collectionHash</code>, or <code>permissions</code> is missing.</td></tr><tr><td><code>31</code></td><td>Requests per second limit exceeded</td><td>API rate limit exceeded.</td></tr><tr><td><code>32</code></td><td>Requests per minute limit exceeded</td><td>API rate limit exceeded.</td></tr><tr><td><code>41</code></td><td>Invalid credentials</td><td>The API key is invalid.</td></tr><tr><td><code>44</code></td><td>Invalid signature</td><td>The request signature is invalid (Flipsnack/MCP keys).</td></tr><tr><td><code>46</code></td><td>Invalid collection hash</td><td>The collection hash does not exist.</td></tr><tr><td><code>43</code></td><td>Forbidden</td><td>The collection does not belong to the API key workspace, is deleted, or the tenant does not match.</td></tr><tr><td><code>40</code></td><td>Bad Request</td><td>The action was sent with the wrong HTTP method (must be GET).</td></tr></tbody></table>

### Permissions-specific errors

<table><thead><tr><th width="117.5546875">Code</th><th width="284.6875">Status</th><th>When</th></tr></thead><tbody><tr><td><code>48</code></td><td>Permissions are only available for published private flipbooks</td><td>The flipbook is not published, is not private, or has a password set.</td></tr><tr><td><code>38</code></td><td>Collection is in progress. Please try again later</td><td>The flipbook is still publishing; retry after <code>collectionStatus</code> is no longer <code>processing</code>.</td></tr><tr><td><code>40</code></td><td>Bad Request</td><td>Invalid permissions payload or validation/plan/SSO errors (see <code>data.errors</code>).</td></tr><tr><td><code>50</code></td><td>Operation failed</td><td>The permissions service returned an unexpected response.</td></tr></tbody></table>

Example — not eligible:

```json
{
  "code": 48,
  "status": "Permissions are only available for published private flipbooks"
}
```

Example — validation error:

```json
{
  "code": 40,
  "status": "Bad Request",
  "data": {
    "errors": [
      "OTP permissions are not available on your plan"
    ]
  }
}
```
