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

# Access control

> How API-key scopes, bucket restrictions, company boundaries, and step-up authentication combine into an access model.

Access to the developer API is the product of several independent gates. A request must pass all of them. Understanding the layering makes authorization failures easier to diagnose and keeps a leaked key from becoming a wide-open door.

## The gates, in order

| # | Gate | Failure |
| - | - | - |
| 1 | Key authentication (`X-Public-Key` + `X-Secret-Key`) | `401` |
| 2 | Key is active: not revoked, not expired | `401` |
| 3 | Account is a company account | `403 API_COMPANY_ONLY` |
| 4 | Request targets the storage surface | `403 API_KEY_SURFACE_DENIED` |
| 5 | Billing state permits the operation | `402 SUBSCRIPTION_REQUIRED` or trial limit `403` |
| 6 | Bucket restriction allows the target bucket | `403 API_BUCKET_SCOPE_DENIED` |
| 7 | The key has the required scopes | `403` (no stable code) |
| 8 | The account context is allowed to act on the resource | `403` or `404` |

Gates that run before the handler — 1 through 7 — do not consume trial API quota. That matters when you are debugging: a burst of `403`s is not the same as a burst of successful calls.

## Company boundary

The REST storage API is company-scoped. A key authenticates as the company workspace and can only reach that company's buckets, folders, and files.

Within a company workspace, file reads are company-visible by design: any team member of the company can read the company drive's files, and a download decrypts content with the file owner's key envelope. Mutating company storage (upload, update, move, copy, delete, restore) stays restricted to the company owner. If your model requires per-member read isolation inside one workspace, that is not what this boundary provides — use separate company workspaces with separate keys.

Individual EverSpark accounts are rejected at gate 3. The one first-party exception is a device-bound backup key used by the enrolled desktop agent. It is not a general-purpose path for third-party integrations and it cannot be created through the public API.

## Scopes

Scopes are named capabilities, not HTTP verbs. The set is small on purpose:

| Scope | Grants |
| - | - |
| `buckets:read` | Read ceiling for file and folder operations; bucket list and bucket size |
| `buckets:write` | Write operations within a bucket, including delete |
| `files:read` | List and inspect files, download files, read versions |
| `files:write` | Upload, update, move, copy, restore |
| `files:delete` | Delete a file |
| `folders:read` | List and read folder metadata |
| `folders:write` | Create, update, move, copy, restore |
| `folders:delete` | Delete a folder |
| `mcp` | Use the MCP endpoint |

The bucket scope is a **ceiling**. A key with `files:read` but no `buckets:read` still fails on a file read. In practice, storage scopes come in pairs:

| Operation | Required scopes |
| - | - |
| List accessible buckets, read bucket size | `buckets:read` |
| Read or download a file | `buckets:read` + `files:read` |
| List folder content | `buckets:read` + `folders:read` |
| Upload or update a file | `buckets:write` + `files:write` |
| Move, copy, or restore a file | `buckets:write` + `files:write` |
| Delete a file | `buckets:write` + `files:delete` |
| Create, rename, or move a folder | `buckets:write` + `folders:write` |
| Delete a folder | `buckets:write` + `folders:delete` |

> [!WARNING]
>
> Delete always requires `buckets:write` in addition to the matching delete scope. A "read plus delete" key is not a valid configuration for deletion, because delete is a write to the bucket.

Scopes connect to the encryption model directly: a download performed with an API key is an owner-authorized operation. The account owner created the key, chose its scopes and bucket restrictions, and is responsible for them — content served to that key is decrypted by design, not an exception. A key can never do more than the owner it acts for.

## Bucket restriction

A key can be unrestricted or limited to explicit bucket IDs.

* **Unrestricted**: the key can target any bucket in the company, subject to scopes.
* **Restricted**: the key can target only the listed buckets.

The restriction is enforced before the operation runs and before trial accounting. A restricted key cannot enumerate or reach other buckets: out-of-scope attempts return `403 API_BUCKET_SCOPE_DENIED`.

This is the single most effective way to limit blast radius. Prefer a per-service key restricted to the buckets that service actually needs.

## Company roles versus API scopes

Do not confuse them.

| Layer | Applies to | Grants |
| - | - | - |
| Company roles and permissions | Human users in the dashboard | UI actions, administration, sharing, billing |
| API scopes | Machine keys on the REST API | Storage operations only |

An API key cannot create or delete buckets, manage sharing, change billing, or administer the company, regardless of scopes. See [Scopes and permissions](https://docs.everabyte.com/authentication/scopes-and-permissions.md).

## Step-up authentication

Sensitive account actions require the human user to re-verify themselves — with an email verification code, or the account password for account deletion:

* creating an API key,
* rotating an API key,
* changing password or email,
* deleting the account (password confirmation).

These actions happen in the dashboard with a browser session, not over the developer API. There is no public endpoint to create a key, so step-up cannot be bypassed programmatically.

## Session authentication is separate

The dashboard uses browser sessions and JWTs. Those credentials do not authenticate the REST API. Conversely, an API key does not authenticate dashboard routes.

This separation is intentional: a stolen browser cookie does not become a machine credential, and a leaked API key does not become a dashboard login.

## Operational guidance

| Do | Do not |
| - | - |
| One key per service, restricted to its buckets | One "Full Access" key shared across everything |
| Start from the smallest scope set that works | Grant `buckets:write` because a read failed |
| Rotate on a schedule | Wait for expiry to break production |
| Revoke first when a leak is suspected | Investigate before revoking |
| Log the key prefix only | Log headers or secrets |

## Diagnosing a `403`

1. Read the response body. If it has a `code`, the gate is explicit.
2. No code usually means a missing scope. Compare required scopes for the operation.
3. `API_BUCKET_SCOPE_DENIED` means the key is restricted to other buckets. Check the bucket ID.
4. `API_KEY_SURFACE_DENIED` means the route family is not part of the storage surface.
5. `API_COMPANY_ONLY` means the account is individual, not a company workspace.

## Related

> **[Scopes and permissions](https://docs.everabyte.com/authentication/scopes-and-permissions.md)**
>
> Full scope reference and presets.

> **[API keys](https://docs.everabyte.com/authentication/api-keys.md)**
>
> Creation, rotation, and revocation.

> **[Error codes](https://docs.everabyte.com/errors/error-codes.md)**
>
> What each stable code means.

> **[Encryption architecture](https://docs.everabyte.com/security/encryption-architecture.md)**
>
> What encryption does and does not protect.


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