HEX
Server: Apache/2.4.46 (Win64) OpenSSL/1.1.1j PHP/8.4.25
System: Windows NT DESKTOP-4TAV2RJ 10.0 build 19045 (Windows 10) AMD64
User: fred (0)
PHP: 8.4.25
Disabled: NONE
Upload Files
File: C:/Users/fred/.codex/.tmp/plugins/plugins/zoom/skills/oauth/concepts/scopes-architecture.md
# Scopes Architecture

Zoom OAuth uses scopes to limit API access. Understanding Classic vs Granular scopes is critical.

## Scope Types

| Type | Format | Example | Status |
|------|--------|---------|--------|
| **Classic** | `resource:level` | `meeting:write:admin` | Active |
| **Granular** | `service:action:data_claim:access` | `meeting:write:meeting:admin` | Active (newer) |

## Classic Scopes

### Format

```
{resource}:{action}:{level}
```

**Examples:**
- `meeting:read` - Read user's own meetings
- `meeting:write:admin` - Create/update meetings for all account users
- `recording:read:master` - Read recordings across all sub-accounts

### Scope Levels

| Level | Access | Who Can Authorize | Example |
|-------|--------|-------------------|---------|
| **(none)** | Own data only | Any user | `meeting:read` |
| `:admin` | Account-wide | Admin role required | `meeting:write:admin` |
| `:master` | Multi-account | Account owner only | `user:master` |

### Common Classic Scopes

```
meeting:read               # View own meetings
meeting:write              # Create/edit own meetings
meeting:write:admin        # Manage all account meetings

user:read                  # View own profile
user:write:admin           # Manage account users

recording:read             # View own recordings
recording:write:admin      # Manage account recordings

webinar:read               # View own webinars
webinar:write:admin        # Manage account webinars

imchat:bot                 # Team Chat bot access
```

## Granular Scopes

### Format

```
{service}:{action}:{data_claim}:{access_level}
```

**Examples:**
- `meeting:read:meeting:user` - Read user's own meetings
- `meeting:write:invite_links:admin` - Create invite links for account
- `recording:delete:recording_file:admin` - Delete recording files account-wide

### Components

1. **service**: API category (meeting, user, recording, etc.)
2. **action**: Operation (read, write, delete, etc.)
3. **data_claim**: Specific data type (meeting, participant, invite_links, etc.)
4. **access_level**: Scope of access (user, admin, account, etc.)

### Access Levels (Granular)

| Level | Access | Example |
|-------|--------|---------|
| `user` | Own data | `meeting:read:meeting:user` |
| `admin` | Account-wide | `meeting:write:meeting:admin` |
| `account` | Account settings | `account:read:settings:account` |

## Classic vs Granular Comparison

### Meetings Scope Example

| Classic | Granular Equivalent |
|---------|---------------------|
| `meeting:read` | `meeting:read:meeting:user` + `meeting:read:list_meetings:user` |
| `meeting:write:admin` | `meeting:write:meeting:admin` + `meeting:write:settings:admin` + more |

### Why Granular Scopes Exist

**Classic scopes** are broad:
- `meeting:write:admin` grants ALL meeting write permissions account-wide
- Includes create, update, delete, settings, etc.

**Granular scopes** are specific:
- `meeting:write:meeting:admin` - Only create/update meetings
- `meeting:delete:meeting:admin` - Only delete meetings
- `meeting:write:settings:admin` - Only update settings

**Principle of Least Privilege:** Request only the granular scopes you need.

## Choosing Between Classic and Granular

### Use Classic When:
- You need broad access (e.g., full meeting management)
- Simpler scope management preferred
- Legacy app migration

### Use Granular When:
- You need specific permissions only
- Implementing principle of least privilege
- Building security-sensitive apps

### Can You Mix?

✅ **Yes**, you can request both Classic and Granular scopes in the same app.

```
scope=meeting:read user:write:admin meeting:write:invite_links:admin
```

## Requesting Scopes

### During App Creation (S2S OAuth, Chatbot)

Scopes are configured in Zoom Marketplace:
1. Go to your app in https://marketplace.zoom.us
2. Click "Scopes" tab
3. Select required scopes (Classic or Granular)
4. Click "Continue"

Token will include all configured scopes.

### During Authorization (User OAuth, Device Flow)

Scopes are requested in authorization URL:

```javascript
const authURL = new URL('https://zoom.us/oauth/authorize');
authURL.searchParams.set('response_type', 'code');
authURL.searchParams.set('client_id', CLIENT_ID);
authURL.searchParams.set('redirect_uri', REDIRECT_URI);

// Request specific scopes (space-separated)
authURL.searchParams.set('scope', 'meeting:read user:read recording:read');

// User sees consent screen listing these scopes
```

## Scope Consent Screen

When user authorizes your app, they see:

```
[Your App Name] wants to:

✓ View your meetings (meeting:read)
✓ View your profile (user:read)
✓ View your recordings (recording:read)

[Deny] [Authorize]
```

## Scope Errors

### Error 4711: Scope Mismatch

**Cause:** Token's scopes don't include required scope for API endpoint.

**Example:**
```javascript
// Token has: meeting:read
// API requires: meeting:write
await axios.post('https://api.zoom.us/v2/users/me/meetings', {...}, {
  headers: { Authorization: `Bearer ${token}` }
});
// Error 4711: Insufficient scope
```

**Solution:**
1. Add required scope in Zoom Marketplace (S2S/Chatbot)
2. OR request scope in authorization URL (User/Device)
3. Re-authorize user to grant new scopes

## Checking Token Scopes

### Decode Access Token Scopes

```javascript
// S2S OAuth: scopes returned in token response
const { access_token, scope } = tokenResponse.data;
console.log('Scopes:', scope); // "meeting:read user:read recording:write"

// User OAuth: scopes returned during token exchange
const { access_token, scope } = tokenResponse.data;
console.log('Granted scopes:', scope.split(' ')); // ['meeting:read', 'user:read', ...]
```

### API to Get Token Scopes

```bash
curl -H "Authorization: Bearer {access_token}" \
  https://zoom.us/oauth/token
```

## Best Practices

### 1. Request Minimum Scopes Needed

```javascript
// ❌ AVOID: Requesting broad admin access when not needed
scope: "meeting:write:admin user:write:admin recording:write:admin"

// ✅ PREFER: Request only what you need
scope: "meeting:read user:read"
```

### 2. Use Granular Scopes for Specific Operations

```javascript
// ❌ Classic (broad)
scope: "meeting:write:admin" // Includes create, update, delete, settings, etc.

// ✅ Granular (specific)
scope: "meeting:write:meeting:admin" // Only create/update meetings
```

### 3. Document Required Scopes

```javascript
/**
 * Create a meeting for a user
 * Required scope: meeting:write:admin (Classic) or meeting:write:meeting:admin (Granular)
 */
async function createMeeting(userId, meetingData) {
  // ...
}
```

## Reference Documentation

- **Classic Scopes** → [../references/classic-scopes.md](../references/classic-scopes.md)
- **Granular Scopes** → [../references/granular-scopes.md](../references/granular-scopes.md)
- **Scope Errors** → [../troubleshooting/scope-issues.md](../troubleshooting/scope-issues.md)