Skip to content

Student Zones

Student Zones are personal isolated areas for each participant with privacy controls and permission enforcement. This is a unique feature not available in competing platforms like Miro, Figma, or Concept - specifically designed for EdTech use cases.

What Are Student Zones?

A Student Zone is a frame (rectangular area) assigned to a specific participant or user, where only the zone owner and host can edit content inside. Think of it as a personal desk in a classroom - each student works in their own space while the teacher monitors all progress.

Key Difference: Zones vs Frames

ConceptDescriptionAssignmentPermissions
FrameRectangular area for organizationNone (public)Anyone can edit inside
ZoneFrame + participant assignmentAssigned to user/participantOnly owner + host can edit

In technical terms: A zone is a frame with assigned_participant_id or assigned_user_id set.

Visual Concept

┌──────────────────────────────────────────────────────────────┐
│                    EXAM BOARD (Teacher View)                  │
│                                                               │
│  ┌────────────┐  ┌────────────┐  ┌────────────┐            │
│  │  Alice's   │  │  Bob's     │  │  Charlie's │            │
│  │  Zone 🔒   │  │  Zone 🔒   │  │  Zone 🔒   │            │
│  │            │  │            │  │            │            │
│  │ [Drawing]  │  │ [Drawing]  │  │ [Drawing]  │            │
│  │ [Objects]  │  │ [Objects]  │  │ [Objects]  │            │
│  └────────────┘  └────────────┘  └────────────┘            │
│                                                               │
│  Teacher sees all zones, students see only their own         │
└──────────────────────────────────────────────────────────────┘

Why Student Zones?

The Problem

Traditional whiteboards treat all participants equally - anyone can edit anywhere. This creates issues for educational scenarios:

  1. Exams: Students can see each other's answers
  2. Individual assignments: No way to isolate student work
  3. Assessment: Difficult to compare student work side-by-side
  4. Cheating: No technical enforcement of "stay in your area"

The Solution

Student Zones provide technical enforcement of personal workspaces:

  • Permission model: Only zone owner can edit inside (enforced server-side)
  • Privacy modes: Hide other students' work during exams
  • Teacher oversight: Host sees all zones regardless of privacy mode
  • Violation detection: Real-time alerts when someone tries to edit in another's zone

Competitive Advantage

PlatformPersonal ZonesPermission EnforcementPrivacy Modes
BoardAPI✅ Yes✅ Server-side✅ 3 modes
Miro❌ No--
Figma/FigJam❌ No--
VK Boards❌ No--
Concept (Yandex)❌ No--

We're the only platform in the EdTech space with this feature.

Key Concepts

Zone Assignment

Zones can be assigned to two types of identifiers:

ID TypeDescriptionPersistenceUse Case
Participant IDSession-based UUIDLost on reconnectAnonymous participants, no login
User IDAuthenticated user UUIDPersistent across sessionsRegistered students, LMS integration

Priority: If both IDs are provided, assigned_user_id takes precedence.

Zone Visibility Modes

Zones support three privacy levels:

ModeBehaviorTeacher ViewStudent ViewUse Case
visibleEveryone sees all zones (read-only for non-owners)Sees all zonesSees all zones (can only edit own)Practice sessions, teacher reviews
hiddenStudents see only their zone, host sees allSees all zonesSees only own zoneExams, tests, independent work
host_onlyOnly host sees zone (draft mode)Sees all zonesCannot see any zonesPreparing zones before class

Visibility Examples

Visible Mode (Practice):

┌──────────────────────────────────────────────┐
│  Alice's View:                               │
│  ┌─────────┐  ┌─────────┐  ┌─────────┐      │
│  │ My Zone │  │ Bob's   │  │Charlie's│      │
│  │ (editable│  │ (view   │  │ (view   │      │
│  │  ✏️)     │  │  only)  │  │  only)  │      │
│  └─────────┘  └─────────┘  └─────────┘      │
│                                              │
│  Alice can see everyone's work,              │
│  but can only edit in her own zone           │
└──────────────────────────────────────────────┘

Hidden Mode (Exam):

┌──────────────────────────────────────────────┐
│  Alice's View:                               │
│  ┌─────────┐                                 │
│  │ My Zone │  🚫 Cannot see Bob's/Charlie's  │
│  │ (editable│                                 │
│  │  ✏️)     │                                 │
│  └─────────┘                                 │
│                                              │
│  Teacher View:                               │
│  ┌─────────┐  ┌─────────┐  ┌─────────┐      │
│  │ Alice's │  │ Bob's   │  │Charlie's│      │
│  │ Zone    │  │ Zone    │  │ Zone    │      │
│  └─────────┘  └─────────┘  └─────────┘      │
│                                              │
│  Teacher sees all, students see only own     │
└──────────────────────────────────────────────┘

Permission Model

┌────────────────────────────────────────────┐
│         Zone Permission Hierarchy          │
├────────────────────────────────────────────┤
│                                            │
│  Host (Teacher):                           │
│  ✅ Can view ALL zones                     │
│  ✅ Can edit ALL zones                     │
│  ✅ Can move/resize/delete zones           │
│                                            │
│  Assigned Participant/User (Student):      │
│  ✅ Can view their OWN zone                │
│  ✅ Can edit ONLY in their zone            │
│  ❌ Cannot move/resize zone itself         │
│  ❌ Cannot edit in other zones             │
│                                            │
│  Other Participants:                       │
│  ✅ Can view zones (if visible mode)       │
│  ❌ Cannot edit in any zone                │
│  ❌ Cannot view zones (if hidden mode)     │
│                                            │
└────────────────────────────────────────────┘

Zone Version

Zones use versioning to prevent race conditions when boundaries change:

  • zone_version increments when zone bounds, assignment, or visibility changes
  • Clients pass version when creating/updating objects
  • If version mismatch, request rejected with zoneChanged: true
  • Client refetches zone and retries operation

Use Cases

1. Online Exams

Create isolated exam zones where students cannot see each other's work.

javascript
// Create hidden zones for 3 students
const students = [
  { name: 'Alice', participantId: 'part-uuid-1' },
  { name: 'Bob', participantId: 'part-uuid-2' },
  { name: 'Charlie', participantId: 'part-uuid-3' }
];

for (let i = 0; i < students.length; i++) {
  await fetch('https://api.boardapi.io/api/v1/boards/board-uuid/frames', {
    method: 'POST',
    headers: {
      'X-API-Key': 'your-api-key',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      name: `${students[i].name} - Exam Zone`,
      bounds_x: 1300 * i,
      bounds_y: 0,
      bounds_width: 1200,
      bounds_height: 800,
      assigned_participant_id: students[i].participantId,
      zone_visibility: 'hidden',  // Students can't see each other
      lock_mode: 'hard'            // Can't navigate outside
    })
  });
}

Result:

  • Alice sees only her zone
  • Bob sees only his zone
  • Charlie sees only his zone
  • Teacher sees all three zones for monitoring
  • Hard lock prevents navigation to other zones

2. Practice Sessions (Visible Zones)

Students work independently but can see each other's progress for learning.

javascript
// Create visible zones for practice
for (let i = 0; i < students.length; i++) {
  await fetch('/api/v1/boards/board-uuid/frames', {
    method: 'POST',
    headers: {
      'X-API-Key': 'your-api-key',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      name: `${students[i].name} - Practice`,
      bounds_x: 1300 * i,
      bounds_y: 0,
      bounds_width: 1200,
      bounds_height: 800,
      assigned_participant_id: students[i].participantId,
      zone_visibility: 'visible',  // Everyone sees everyone
      lock_mode: 'soft'             // Can navigate to see others
    })
  });
}

Result:

  • Alice can see Bob's and Charlie's zones (read-only)
  • Bob can see Alice's and Charlie's zones (read-only)
  • Each student can only edit in their own zone
  • Teacher can edit in all zones (for corrections)

3. Assessment Comparison

Teacher views all student work side-by-side for grading.

javascript
// After exam, switch to visible mode for grading
const zones = await fetch(
  '/api/v1/boards/board-uuid/frames?assigned=true',
  {
    headers: { 'X-API-Key': 'your-api-key' }
  }
).then(r => r.json());

for (const zone of zones) {
  await fetch(`/api/v1/boards/board-uuid/frames/${zone.id}`, {
    method: 'PATCH',
    headers: {
      'X-API-Key': 'your-api-key',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      zone_visibility: 'visible',  // Show all zones for review
      lock_mode: 'none'             // Remove navigation locks
    })
  });
}

Teacher View:

┌───────────┬───────────┬───────────┬───────────┐
│ Alice's   │ Bob's     │ Charlie's │ Dana's    │
│ Answer    │ Answer    │ Answer    │ Answer    │
│           │           │           │           │
│ ✅ Correct│ ❌ Error  │ ✅ Correct│ ⚠️ Partial│
│           │           │           │           │
│ Score: 10 │ Score: 7  │ Score: 10 │ Score: 8  │
└───────────┴───────────┴───────────┴───────────┘

4. Group Workspaces with Privacy

Multiple students assigned to one zone for group work.

javascript
// Create group zone with multiple assignments
const groupZone = await fetch('/api/v1/boards/board-uuid/frames', {
  method: 'POST',
  headers: {
    'X-API-Key': 'your-api-key',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    name: 'Blue Team Workspace',
    bounds_x: 0,
    bounds_y: 0,
    bounds_width: 1400,
    bounds_height: 1000,
    zone_visibility: 'hidden',  // Other groups can't see
    lock_mode: 'soft',
    background_color: '#E3F2FD'
  })
}).then(r => r.json());

// Assign multiple students (bulk assign)
await fetch('/api/v1/boards/board-uuid/zones/bulk-assign', {
  method: 'POST',
  headers: {
    'X-API-Key': 'your-api-key',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    assignments: [
      { frame_id: groupZone.id, participant_id: 'student-1' },
      { frame_id: groupZone.id, participant_id: 'student-2' },
      { frame_id: groupZone.id, participant_id: 'student-3' }
    ]
  })
});

Note: Current API supports one assignment per zone. Group zones are on the roadmap (EPIC-046).

API Reference

Create Zone

http
POST /api/v1/boards/{boardUuid}/frames
Content-Type: application/json
X-API-Key: your-api-key

{
  "name": "Alice's Zone",
  "bounds_x": 0,
  "bounds_y": 0,
  "bounds_width": 1200,
  "bounds_height": 800,
  "assigned_participant_id": "part-uuid-123",
  "zone_visibility": "hidden",
  "lock_mode": "hard"
}

Response:

json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "board_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "name": "Alice's Zone",
  "bounds_x": 0,
  "bounds_y": 0,
  "bounds_width": 1200,
  "bounds_height": 800,
  "assigned_participant_id": "part-uuid-123",
  "assigned_user_id": null,
  "zone_visibility": "hidden",
  "zone_version": 1,
  "lock_mode": "hard",
  "visibility": "active",
  "created_at": "2025-11-26T10:00:00Z",
  "updated_at": "2025-11-26T10:00:00Z"
}

List Zones

http
GET /api/v1/boards/{boardUuid}/frames?assigned=true
X-API-Key: your-api-key

Query parameter:

  • assigned=true - Filter for zones only (frames with assignments)

Response: Array of zone objects.

Update Zone Visibility

http
PATCH /api/v1/boards/{boardUuid}/frames/{frameId}
Content-Type: application/json
X-API-Key: your-api-key

{
  "zone_visibility": "visible"
}

Common updates:

  • Switch between exam and practice mode (hiddenvisible)
  • Prepare zones before class (host_onlyvisible)
  • Resize zones (updates bounds, increments zone_version)

Assign Participant to Zone

http
POST /api/v1/boards/{boardUuid}/frames/{frameId}/assign
Content-Type: application/json
X-API-Key: your-api-key

{
  "participant_id": "part-uuid-123"
}

Or assign authenticated user:

json
{
  "user_id": "user-uuid-456"
}

Bulk Assign

http
POST /api/v1/boards/{boardUuid}/zones/bulk-assign
Content-Type: application/json
X-API-Key: your-api-key

{
  "assignments": [
    { "frame_id": "zone-1-uuid", "participant_id": "part-uuid-1" },
    { "frame_id": "zone-2-uuid", "participant_id": "part-uuid-2" },
    { "frame_id": "zone-3-uuid", "participant_id": "part-uuid-3" }
  ]
}

Response:

json
{
  "assigned": 3,
  "failed": 0,
  "results": [
    { "frame_id": "zone-1-uuid", "success": true, "zone_version": 2 },
    { "frame_id": "zone-2-uuid", "success": true, "zone_version": 1 },
    { "frame_id": "zone-3-uuid", "success": true, "zone_version": 1 }
  ]
}

Limit: Max 50 assignments per request.

Delete Zone

http
DELETE /api/v1/boards/{boardUuid}/frames/{frameId}
X-API-Key: your-api-key

Behavior:

  • Sets visibility to archived
  • Objects within zone are preserved (student work not lost)
  • Assignment removed (assigned_participant_id set to null)
  • Objects become "unassigned" and editable by everyone

Full API Reference →

WebSocket Events

Zone Assignment

javascript
// Server broadcasts when zone assigned
socket.on('zone:assigned', (data) => {
  console.log(`Zone ${data.frameId} assigned to ${data.participantName}`);
  // Update UI to show assignment
  updateZoneIndicator(data.frameId, data.participantName);
});

Payload:

json
{
  "frameId": "550e8400-e29b-41d4-a716-446655440000",
  "participantId": "part-uuid-123",
  "participantName": "Alice",
  "userId": null,
  "zoneVersion": 2,
  "timestamp": "2025-11-26T10:00:00Z"
}

Visibility Changes

javascript
// Listen for visibility mode changes
socket.on('zone:visibility-changed', async (data) => {
  if (data.visibility === 'hidden') {
    // Exam mode activated
    await filterBoardSnapshot();  // Hide other zones
    console.log('Exam mode: Only your zone visible');
  } else if (data.visibility === 'visible') {
    // Practice mode activated
    await refetchBoardSnapshot();  // Show all zones
    console.log('Practice mode: All zones visible');
  }
});

Payload:

json
{
  "frameId": "550e8400-e29b-41d4-a716-446655440000",
  "visibility": "hidden",
  "zoneVersion": 4,
  "timestamp": "2025-11-26T10:10:00Z"
}

Permission Violations

javascript
// Student tries to edit in another's zone
socket.on('zone:violation', (data) => {
  console.warn(`Violation: ${data.reason}`);
  // Revert change
  undoLastAction(data.objectId);
  // Show toast
  showToast('You can only draw in your assigned zone', 'error');
});

Payload:

json
{
  "objectId": "obj-uuid-789",
  "reason": "Cannot draw in another student's zone",
  "operation": "create",
  "timestamp": "2025-11-26T10:15:00Z"
}

Zone Version Conflicts

javascript
// When creating object, pass zone version
socket.emit('object:create', {
  ...objectData,
  zoneVersion: myZone.zone_version
});

// Handle version conflict
socket.on('object:create:result', async (result) => {
  if (result.error && result.zoneChanged) {
    // Zone boundaries changed, refetch
    console.warn('Zone version outdated, refetching...');
    myZone = await refetchMyZone();
    // Retry with new version
    retryObjectCreation(objectData, myZone.zone_version);
  }
});

WebSocket API Reference →

Complete Example: Exam Setup

javascript
// Complete workflow for online exam setup
async function setupExamBoard(boardId, students, apiKey) {
  // 1. Create hidden zones for each student
  const zones = [];
  for (let i = 0; i < students.length; i++) {
    const zone = await fetch(
      `https://api.boardapi.io/api/v1/boards/${boardId}/frames`,
      {
        method: 'POST',
        headers: {
          'X-API-Key': apiKey,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          name: `${students[i].name} - Exam`,
          bounds_x: 1300 * i,
          bounds_y: 0,
          bounds_width: 1200,
          bounds_height: 800,
          assigned_participant_id: students[i].participantId,
          zone_visibility: 'hidden',  // Isolation
          lock_mode: 'hard',           // No escaping
          background_color: '#FFF3E0'
        })
      }
    ).then(r => r.json());

    zones.push(zone);
  }

  console.log(`Created ${zones.length} exam zones`);

  // 2. Navigate each student to their zone
  for (let i = 0; i < students.length; i++) {
    students[i].socket.emit('frame:navigate', {
      boardId: boardId,
      frameId: zones[i].id,
      smooth: false  // Instant navigation for exams
    });
  }

  // 3. Setup violation monitoring
  socket.on('zone:violation', (data) => {
    console.warn(`Cheating attempt: ${data.participantName} tried to edit in ${data.zoneName}`);
    sendNotification('TEACHER', `Alert: Possible cheating by ${data.participantName}`);
  });

  // 4. After exam, switch to visible for grading
  async function endExamAndGrade() {
    for (const zone of zones) {
      await fetch(`/api/v1/boards/${boardId}/frames/${zone.id}`, {
        method: 'PATCH',
        headers: {
          'X-API-Key': apiKey,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          zone_visibility: 'visible',  // Teacher can compare
          lock_mode: 'none'             // Remove locks
        })
      });
    }
    console.log('Exam ended. All zones visible for grading.');
  }

  return { zones, endExamAndGrade };
}

// Usage:
const students = [
  { name: 'Alice', participantId: 'part-1', socket: socket1 },
  { name: 'Bob', participantId: 'part-2', socket: socket2 },
  { name: 'Charlie', participantId: 'part-3', socket: socket3 }
];

const { zones, endExamAndGrade } = await setupExamBoard(
  'board-uuid',
  students,
  'your-api-key'
);

// Later, after exam time expires:
await endExamAndGrade();

Integration with Other Features

Zones + Frames

Zones are built on top of Frames, so all Frame features work:

  • Clone zones for parallel groups (same assignment logic)
  • Lock modes control viewport (soft/hard lock still applies)
  • Background colors for visual organization

Learn more about Frames →

Zones + Presenter Modes

Spectate Mode with Zones:

javascript
// Teacher spectates student
socket.emit('spectate:start', {
  boardId: 'board-uuid',
  targetParticipantId: 'student-part-id',
  stealthMode: true  // Student unaware
});

// Teacher sees which zone student is in
socket.on('spectate:viewport', (data) => {
  const zone = getZoneAtPosition(data.scrollX, data.scrollY);
  if (zone && zone.assigned_participant_id === data.targetId) {
    console.log(`Student is in their assigned zone: ${zone.name}`);
  } else {
    console.warn(`Student navigated outside their zone!`);
  }
});

Learn more about Presenter Modes →

Zones + Custom Components

Custom components respect zone permissions:

javascript
// Custom quiz component checks zone permissions
class QuizComponent {
  async submitAnswer(answer) {
    // Pass zone version for conflict detection
    const result = await this.sdk.board.createObject({
      type: 'quiz-answer',
      data: { answer },
      zoneVersion: this.currentZone.zone_version
    });

    if (result.error && result.zoneChanged) {
      // Zone moved, refetch and retry
      this.currentZone = await this.sdk.board.getMyZone();
      return this.submitAnswer(answer);
    }
  }
}

Learn more about Custom Components →

Best Practices

Zone Layout

Grid Layout (Exams):

[Zone 1] [Zone 2] [Zone 3] [Zone 4]
[Zone 5] [Zone 6] [Zone 7] [Zone 8]
  • Easy teacher navigation
  • All zones visible on screen
  • Use small zone size (1000x700)

Horizontal Strip (Practice):

[Zone 1] [Zone 2] [Zone 3] [Zone 4] [Zone 5]
  • Scroll left/right to see students
  • Use larger zones (1200x900)

Naming Conventions

Good names:

  • "Alice's Exam Zone"
  • "Bob - Practice Area"
  • "Group A Workspace"

Avoid:

  • "Zone 1" (not personalized)
  • "asldfj" (meaningless)

Performance

  • Recommended: 25-30 zones per board (matches typical class size)
  • Maximum: 50 zones (performance tested)
  • Bulk operations: Use bulk-assign endpoint for >10 students
  • Zone version: Always pass version when creating objects (prevents conflicts)

Privacy & Ethics

  • Inform students: Disclose monitoring capabilities in syllabus
  • Parental consent: For minors, get consent for zone monitoring
  • Data retention: Archive zones after semester ends
  • FERPA compliance: Use authenticated user_id (not session participant_id)

Common Issues

Student can't edit in their zone

Check:

  1. Zone assignment is correct (GET zone to verify)
  2. assigned_participant_id matches current participant's ID
  3. Participant hasn't reconnected (session ID changed)
  4. Zone is active (not archived)

Solution for reconnection:

javascript
// Detect reconnection
socket.on('connect', async () => {
  const newParticipantId = await getMyParticipantId();
  // Reassign zones
  await fetch(`/api/v1/boards/${boardId}/frames/${zoneId}/assign`, {
    method: 'POST',
    headers: { 'X-API-Key': apiKey, 'Content-Type': 'application/json' },
    body: JSON.stringify({ participant_id: newParticipantId })
  });
});

Zone version conflicts

Error: "zoneChanged": true

Cause: Zone boundaries changed (teacher moved/resized zone) while student was drawing.

Solution:

javascript
async function createObjectInZone(objectData, zone) {
  try {
    const result = await socket.emit('object:create', {
      ...objectData,
      zoneVersion: zone.zone_version
    });

    if (result.error && result.zoneChanged) {
      // Refetch zone
      zone = await refetchMyZone();
      // Retry with new version
      return createObjectInZone(objectData, zone);
    }

    return result;
  } catch (error) {
    console.error('Object creation failed:', error);
  }
}

Zones overlapping

Problem: Zones created at same position, students can't see their zone.

Solution: Use consistent spacing:

javascript
const ZONE_WIDTH = 1200;
const ZONE_GAP = 100;
const ZONE_SPACING = ZONE_WIDTH + ZONE_GAP; // 1300

for (let i = 0; i < students.length; i++) {
  bounds_x = ZONE_SPACING * i;  // 0, 1300, 2600, ...
}

Permission violations not detected

Check:

  1. Server-side zone middleware is enabled
  2. Client is sending object operations via WebSocket (not REST API)
  3. Object operations include participantId in payload
  4. Zone assignments are loaded in server memory (check cache)

Quota & Limits

PlanMax Zones/BoardBulk Assign BatchZone Assignments/Day
Starter1010100
Professional3030500
Business50502,000
Enterprise100100Unlimited

Note: These limits match typical class sizes (25-30 students).

View Quota API →

Roadmap

Planned Features

  • Group zones: Multiple students assigned to one zone (EPIC-046)
  • Zone templates: Pre-built exam/practice zone layouts
  • Automatic grading: AI analysis of zone content
  • Zone exports: Export individual student work as PDF
  • Parent portal: Parents view child's zone (read-only)

Next Steps


Ready to use Student Zones? View Quick Start →