Skip to content

Frames API Reference

The Frames API provides endpoints for creating and managing rectangular zones on boards. Frames enable presentation/slide mode, viewport control, and breakout rooms functionality. All frame operations require API key authentication.

Overview

Frames are rectangular zones on a board used for spatial organization and navigation control:

  • Presentation/Slide Mode: Sequential navigation through content blocks
  • Viewport Control: Lock student navigation to specific areas (soft/hard lock modes)
  • Breakout Rooms: Group isolation for collaborative work (via frame cloning)

Key Concepts

ConceptDescription
FrameRectangular area with bounds (x, y, width, height)
Lock ModeControls viewport navigation: none, soft, hard
OrderSequential position for presentation mode
VisibilityFrame state: active, hidden, archived
CloneCreate frame copies with offset (for breakout rooms)

Lock Modes

ModeBehaviorUse Case
noneFree navigation (default)Open collaboration
softCan escape with warning indicatorGuided work with flexibility
hardCannot leave frame boundsExams, strict control

Authentication

All Frames API endpoints require API key authentication:

bash
curl -H "X-API-Key: your-api-key" \
  https://api.boardapi.io/api/v1/boards/:boardId/frames

API keys are passed via the X-API-Key header. Get your API key from the Developer Dashboard.

Base URL

https://api.boardapi.io/api/v1

For development:

http://localhost:4000/api/v1

Endpoints Overview

MethodEndpointDescription
POST/boards/:boardId/framesCreate a new frame
GET/boards/:boardId/framesList all frames for a board
GET/boards/:boardId/frames/:idGet a specific frame
PATCH/boards/:boardId/frames/:idUpdate frame properties
DELETE/boards/:boardId/frames/:idArchive a frame
POST/boards/:boardId/frames/:id/cloneClone a frame with offset

Create Frame

Creates a new rectangular zone on a board for spatial organization.

Request

http
POST /boards/:boardId/frames
X-API-Key: your-api-key
Content-Type: application/json

{
  "name": "Introduction",
  "order": 0,
  "bounds_x": 0,
  "bounds_y": 0,
  "bounds_width": 1200,
  "bounds_height": 800,
  "lock_mode": "soft",
  "background_color": "#FFFFFF",
  "visibility": "active"
}

URL Parameters

ParameterTypeDescription
boardIdstringBoard UUID

Request Body Parameters

ParameterTypeRequiredDescription
namestringYesFrame name (max 255 characters), e.g., "Slide 1", "Introduction"
ordernumberNoSequence order for presentations (default: 0, min: 0)
bounds_xnumberNoX coordinate in pixels (default: 0)
bounds_ynumberNoY coordinate in pixels (default: 0)
bounds_widthnumberNoWidth in pixels (default: 1200, min: 100)
bounds_heightnumberNoHeight in pixels (default: 800, min: 100)
lock_modestringNoViewport lock: none, soft, hard (default: none)
background_colorstringNoHex color code (e.g., #FFFFFF)
visibilitystringNoFrame state: active, hidden, archived (default: active)
parent_frame_idstringNoParent frame UUID (for tracking clones in breakout rooms)

Response

Status Code: 201 Created

json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "board_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "name": "Introduction",
  "order": 0,
  "bounds_x": 0,
  "bounds_y": 0,
  "bounds_width": 1200,
  "bounds_height": 800,
  "lock_mode": "soft",
  "background_color": "#FFFFFF",
  "visibility": "active",
  "parent_frame_id": null,
  "created_at": "2025-11-26T10:00:00Z",
  "updated_at": "2025-11-26T10:00:00Z"
}

Response Fields

FieldTypeDescription
idstringFrame UUID
board_idstringParent board UUID
namestringFrame name
ordernumberSequence position
bounds_xnumberX coordinate
bounds_ynumberY coordinate
bounds_widthnumberWidth in pixels
bounds_heightnumberHeight in pixels
lock_modestringViewport control mode
background_colorstringHex color or null
visibilitystringCurrent state
parent_frame_idstringParent frame UUID (for clones) or null
created_atstringISO 8601 creation timestamp
updated_atstringISO 8601 update timestamp

Example Requests

JavaScript/TypeScript:

javascript
const response = await fetch(
  'https://api.boardapi.io/api/v1/boards/a1b2c3d4/frames',
  {
    method: 'POST',
    headers: {
      'X-API-Key': 'your-api-key',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      name: 'Introduction',
      order: 0,
      bounds_x: 0,
      bounds_y: 0,
      bounds_width: 1200,
      bounds_height: 800,
      lock_mode: 'soft',
      background_color: '#F0F8FF'
    })
  }
);

const frame = await response.json();
console.log(`Created frame: ${frame.name} (${frame.id})`);

cURL:

bash
curl -X POST https://api.boardapi.io/api/v1/boards/a1b2c3d4/frames \
  -H "X-API-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Introduction",
    "order": 0,
    "bounds_x": 0,
    "bounds_y": 0,
    "bounds_width": 1200,
    "bounds_height": 800,
    "lock_mode": "soft",
    "background_color": "#F0F8FF"
  }'

PHP:

php
<?php
$ch = curl_init('https://api.boardapi.io/api/v1/boards/a1b2c3d4/frames');

curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => [
        'X-API-Key: your-api-key',
        'Content-Type: application/json'
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'name' => 'Introduction',
        'order' => 0,
        'bounds_x' => 0,
        'bounds_y' => 0,
        'bounds_width' => 1200,
        'bounds_height' => 800,
        'lock_mode' => 'soft',
        'background_color' => '#F0F8FF'
    ]),
    CURLOPT_RETURNTRANSFER => true
]);

$frame = json_decode(curl_exec($ch), true);
echo "Created frame: {$frame['name']}\n";

Use Cases

  • Create sequential slides for lesson presentation
  • Define work zones for group activities
  • Set up exam areas with hard viewport lock
  • Organize board content into logical sections

Possible Errors

Status CodeErrorDescription
400Bad RequestInvalid frame data (e.g., width < 100, invalid lock_mode)
404Not FoundBoard does not exist or parent_frame_id not found
401UnauthorizedInvalid or missing API key

List All Frames

Retrieves all frames for a board, ordered by sequence. By default excludes archived frames.

Request

http
GET /boards/:boardId/frames?includeArchived=false
X-API-Key: your-api-key

URL Parameters

ParameterTypeDescription
boardIdstringBoard UUID

Query Parameters

ParameterTypeRequiredDescription
includeArchivedbooleanNoInclude archived frames (default: false)

Response

Status Code: 200 OK

json
[
  {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "board_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "name": "Introduction",
    "order": 0,
    "bounds_x": 0,
    "bounds_y": 0,
    "bounds_width": 1200,
    "bounds_height": 800,
    "lock_mode": "soft",
    "background_color": "#F0F8FF",
    "visibility": "active",
    "parent_frame_id": null,
    "created_at": "2025-11-26T10:00:00Z",
    "updated_at": "2025-11-26T10:00:00Z"
  },
  {
    "id": "660f9500-f3ac-52e5-b827-557766551111",
    "board_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "name": "Practice",
    "order": 1,
    "bounds_x": 1300,
    "bounds_y": 0,
    "bounds_width": 1200,
    "bounds_height": 800,
    "lock_mode": "none",
    "background_color": null,
    "visibility": "active",
    "parent_frame_id": null,
    "created_at": "2025-11-26T10:05:00Z",
    "updated_at": "2025-11-26T10:05:00Z"
  }
]

Example Requests

JavaScript/TypeScript:

javascript
// Get all active frames
const response = await fetch(
  'https://api.boardapi.io/api/v1/boards/a1b2c3d4/frames',
  {
    headers: {
      'X-API-Key': 'your-api-key'
    }
  }
);

const frames = await response.json();
console.log(`Total frames: ${frames.length}`);

// Include archived frames
const allFrames = await fetch(
  'https://api.boardapi.io/api/v1/boards/a1b2c3d4/frames?includeArchived=true',
  {
    headers: {
      'X-API-Key': 'your-api-key'
    }
  }
).then(r => r.json());

cURL:

bash
# Get active frames only
curl -H "X-API-Key: your-api-key" \
  'https://api.boardapi.io/api/v1/boards/a1b2c3d4/frames'

# Include archived frames
curl -H "X-API-Key: your-api-key" \
  'https://api.boardapi.io/api/v1/boards/a1b2c3d4/frames?includeArchived=true'

PHP:

php
<?php
$headers = ['X-API-Key: your-api-key'];
$frames = json_decode(
  file_get_contents(
    'https://api.boardapi.io/api/v1/boards/a1b2c3d4/frames',
    false,
    stream_context_create(['http' => ['header' => implode("\r\n", $headers)]])
  ),
  true
);

foreach ($frames as $frame) {
    echo "{$frame['order']}. {$frame['name']} (Lock: {$frame['lock_mode']})\n";
}

Use Cases

  • Display frame navigation sidebar
  • Build presentation timeline
  • Show teacher dashboard with all zones
  • Sync frames to external LMS

Possible Errors

Status CodeErrorDescription
404Not FoundBoard does not exist
401UnauthorizedInvalid API key

Get Frame

Retrieves a specific frame by ID.

Request

http
GET /boards/:boardId/frames/:id
X-API-Key: your-api-key

URL Parameters

ParameterTypeDescription
boardIdstringBoard UUID
idstringFrame UUID

Response

Status Code: 200 OK

json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "board_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "name": "Introduction",
  "order": 0,
  "bounds_x": 0,
  "bounds_y": 0,
  "bounds_width": 1200,
  "bounds_height": 800,
  "lock_mode": "soft",
  "background_color": "#F0F8FF",
  "visibility": "active",
  "parent_frame_id": null,
  "created_at": "2025-11-26T10:00:00Z",
  "updated_at": "2025-11-26T10:00:00Z"
}

Example Requests

JavaScript/TypeScript:

javascript
const frameId = '550e8400-e29b-41d4-a716-446655440000';
const response = await fetch(
  `https://api.boardapi.io/api/v1/boards/a1b2c3d4/frames/${frameId}`,
  {
    headers: {
      'X-API-Key': 'your-api-key'
    }
  }
);

const frame = await response.json();
console.log(`Frame: ${frame.name}`);
console.log(`Bounds: ${frame.bounds_width}x${frame.bounds_height}`);

cURL:

bash
curl -H "X-API-Key: your-api-key" \
  'https://api.boardapi.io/api/v1/boards/a1b2c3d4/frames/550e8400-e29b-41d4-a716-446655440000'

Possible Errors

Status CodeErrorDescription
404Not FoundFrame or board not found
401UnauthorizedInvalid API key

Update Frame

Updates frame properties such as name, bounds, lock mode, or visibility.

Request

http
PATCH /boards/:boardId/frames/:id
X-API-Key: your-api-key
Content-Type: application/json

{
  "name": "Updated Introduction",
  "lock_mode": "hard",
  "background_color": "#FFF8DC"
}

URL Parameters

ParameterTypeDescription
boardIdstringBoard UUID
idstringFrame UUID

Request Body Parameters

All fields are optional. Only provided fields will be updated.

ParameterTypeDescription
namestringNew frame name
ordernumberNew sequence position
bounds_xnumberNew X coordinate
bounds_ynumberNew Y coordinate
bounds_widthnumberNew width (min: 100)
bounds_heightnumberNew height (min: 100)
lock_modestringNew lock mode: none, soft, hard
background_colorstringNew hex color or null to remove
visibilitystringNew state: active, hidden, archived

Response

Status Code: 200 OK

json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "board_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "name": "Updated Introduction",
  "order": 0,
  "bounds_x": 0,
  "bounds_y": 0,
  "bounds_width": 1200,
  "bounds_height": 800,
  "lock_mode": "hard",
  "background_color": "#FFF8DC",
  "visibility": "active",
  "parent_frame_id": null,
  "created_at": "2025-11-26T10:00:00Z",
  "updated_at": "2025-11-26T10:30:00Z"
}

Example Requests

JavaScript/TypeScript:

javascript
// Change lock mode to hard (exam mode)
const response = await fetch(
  'https://api.boardapi.io/api/v1/boards/a1b2c3d4/frames/550e8400',
  {
    method: 'PATCH',
    headers: {
      'X-API-Key': 'your-api-key',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      lock_mode: 'hard',
      name: 'Exam Area - No Navigation'
    })
  }
);

const updated = await response.json();
console.log(`Lock mode: ${updated.lock_mode}`);

cURL:

bash
# Resize frame
curl -X PATCH https://api.boardapi.io/api/v1/boards/a1b2c3d4/frames/550e8400 \
  -H "X-API-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "bounds_width": 1600,
    "bounds_height": 900
  }'

# Change visibility to hidden
curl -X PATCH https://api.boardapi.io/api/v1/boards/a1b2c3d4/frames/550e8400 \
  -H "X-API-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{"visibility": "hidden"}'

PHP:

php
<?php
$ch = curl_init('https://api.boardapi.io/api/v1/boards/a1b2c3d4/frames/550e8400');

curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'PATCH',
    CURLOPT_HTTPHEADER => [
        'X-API-Key: your-api-key',
        'Content-Type: application/json'
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'lock_mode' => 'hard',
        'name' => 'Exam Area'
    ]),
    CURLOPT_RETURNTRANSFER => true
]);

$updated = json_decode(curl_exec($ch), true);
echo "Updated: {$updated['name']}\n";

Use Cases

  • Switch to hard lock mode for exams
  • Resize frames after content changes
  • Reorder slides by updating order
  • Temporarily hide frames with visibility: hidden
  • Update frame names for clarity

Possible Errors

Status CodeErrorDescription
400Bad RequestInvalid update data (e.g., width < 100, invalid lock_mode)
404Not FoundFrame or board not found
401UnauthorizedInvalid API key

Delete Frame

Soft deletes a frame by setting visibility to archived. Archived frames are hidden from the default list but can be restored by updating visibility back to active.

Request

http
DELETE /boards/:boardId/frames/:id
X-API-Key: your-api-key

URL Parameters

ParameterTypeDescription
boardIdstringBoard UUID
idstringFrame UUID

Response

Status Code: 200 OK

json
{
  "message": "Frame archived successfully"
}

Example Requests

JavaScript/TypeScript:

javascript
const response = await fetch(
  'https://api.boardapi.io/api/v1/boards/a1b2c3d4/frames/550e8400',
  {
    method: 'DELETE',
    headers: {
      'X-API-Key': 'your-api-key'
    }
  }
);

const result = await response.json();
console.log(result.message); // "Frame archived successfully"

cURL:

bash
curl -X DELETE \
  -H "X-API-Key: your-api-key" \
  'https://api.boardapi.io/api/v1/boards/a1b2c3d4/frames/550e8400'

PHP:

php
<?php
$ch = curl_init('https://api.boardapi.io/api/v1/boards/a1b2c3d4/frames/550e8400');

curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'DELETE',
    CURLOPT_HTTPHEADER => ['X-API-Key: your-api-key'],
    CURLOPT_RETURNTRANSFER => true
]);

$result = json_decode(curl_exec($ch), true);
echo $result['message'];

Behavior

  • Frame visibility changes to archived
  • Frame is hidden from GET /frames (unless includeArchived=true)
  • Objects within frame are NOT deleted
  • Frame can be restored by PATCH with visibility: active

Restoring Archived Frames

javascript
// Restore archived frame
await fetch('https://api.boardapi.io/api/v1/boards/a1b2c3d4/frames/550e8400', {
  method: 'PATCH',
  headers: {
    'X-API-Key': 'your-api-key',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({ visibility: 'active' })
});

Possible Errors

Status CodeErrorDescription
404Not FoundFrame or board not found
401UnauthorizedInvalid API key

Clone Frame

Creates a copy of a frame with optional position offset. Useful for creating breakout rooms where each group needs an identical workspace.

Request

http
POST /boards/:boardId/frames/:id/clone
X-API-Key: your-api-key
Content-Type: application/json

{
  "offsetX": 1300,
  "offsetY": 0,
  "name": "Group 1"
}

URL Parameters

ParameterTypeDescription
boardIdstringBoard UUID
idstringFrame UUID to clone

Request Body Parameters

ParameterTypeRequiredDescription
offsetXnumberNoHorizontal offset in pixels (default: 0)
offsetYnumberNoVertical offset in pixels (default: 0)
namestringNoName for cloned frame (default: original name + " (Copy)")

Response

Status Code: 201 Created

json
{
  "id": "770fa600-g4bd-63f6-c938-668877662222",
  "board_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "name": "Group 1",
  "order": 0,
  "bounds_x": 1300,
  "bounds_y": 0,
  "bounds_width": 1200,
  "bounds_height": 800,
  "lock_mode": "soft",
  "background_color": "#F0F8FF",
  "visibility": "active",
  "parent_frame_id": "550e8400-e29b-41d4-a716-446655440000",
  "created_at": "2025-11-26T11:00:00Z",
  "updated_at": "2025-11-26T11:00:00Z"
}

What Gets Cloned

Copied:

  • All frame properties (bounds, lock_mode, background_color, etc.)
  • Original frame ID stored in parent_frame_id

Not Copied:

  • Frame ID (new UUID is generated)
  • Objects within the frame (clone only copies frame structure)
  • name (uses provided name or original + " (Copy)")

Example Requests

JavaScript/TypeScript:

javascript
// Create 3 breakout rooms with horizontal spacing
const sourceFrameId = '550e8400-e29b-41d4-a716-446655440000';
const groups = ['Group 1', 'Group 2', 'Group 3'];

for (let i = 0; i < groups.length; i++) {
  const response = await fetch(
    `https://api.boardapi.io/api/v1/boards/a1b2c3d4/frames/${sourceFrameId}/clone`,
    {
      method: 'POST',
      headers: {
        'X-API-Key': 'your-api-key',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        offsetX: 1300 * (i + 1), // Space groups horizontally
        offsetY: 0,
        name: groups[i]
      })
    }
  );

  const clone = await response.json();
  console.log(`Created ${clone.name} at x=${clone.bounds_x}`);
}

cURL:

bash
# Clone frame for first breakout room
curl -X POST \
  https://api.boardapi.io/api/v1/boards/a1b2c3d4/frames/550e8400/clone \
  -H "X-API-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "offsetX": 1300,
    "offsetY": 0,
    "name": "Group 1"
  }'

# Clone with vertical offset
curl -X POST \
  https://api.boardapi.io/api/v1/boards/a1b2c3d4/frames/550e8400/clone \
  -H "X-API-Key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "offsetX": 0,
    "offsetY": 1000,
    "name": "Group 2"
  }'

PHP:

php
<?php
// Create 3 breakout rooms
$sourceFrameId = '550e8400-e29b-41d4-a716-446655440000';
$groups = ['Group 1', 'Group 2', 'Group 3'];

foreach ($groups as $i => $groupName) {
    $ch = curl_init(
        "https://api.boardapi.io/api/v1/boards/a1b2c3d4/frames/{$sourceFrameId}/clone"
    );

    curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_HTTPHEADER => [
            'X-API-Key: your-api-key',
            'Content-Type: application/json'
        ],
        CURLOPT_POSTFIELDS => json_encode([
            'offsetX' => 1300 * ($i + 1),
            'offsetY' => 0,
            'name' => $groupName
        ]),
        CURLOPT_RETURNTRANSFER => true
    ]);

    $clone = json_decode(curl_exec($ch), true);
    echo "Created {$clone['name']} at x={$clone['bounds_x']}\n";
    curl_close($ch);
}

Use Cases

  • Breakout Rooms: Create identical workspaces for student groups
  • Parallel Activities: Clone exercise zones for multiple teams
  • Templates: Duplicate frame layouts for consistency
  • Testing: Create sandbox copies of production frames

Breakout Room Example

javascript
// Complete breakout room setup
async function createBreakoutRooms(boardId, templateFrameId, groupNames) {
  const rooms = [];

  for (let i = 0; i < groupNames.length; i++) {
    const clone = await fetch(
      `https://api.boardapi.io/api/v1/boards/${boardId}/frames/${templateFrameId}/clone`,
      {
        method: 'POST',
        headers: {
          'X-API-Key': 'your-api-key',
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          offsetX: 1300 * (i + 1),
          offsetY: 0,
          name: groupNames[i]
        })
      }
    ).then(r => r.json());

    rooms.push(clone);
  }

  return rooms;
}

// Usage
const rooms = await createBreakoutRooms(
  'a1b2c3d4',
  '550e8400',
  ['Blue Team', 'Red Team', 'Green Team']
);

console.log(`Created ${rooms.length} breakout rooms`);

Possible Errors

Status CodeErrorDescription
404Not FoundSource frame or board not found
401UnauthorizedInvalid API key
400Bad RequestInvalid offset values

WebSocket Integration

Frames support real-time synchronization via WebSocket events. See WebSocket API for connection details.

Frame Events

EventDirectionDescription
frame:createdServer → ClientsNew frame created
frame:updatedServer → ClientsFrame properties changed
frame:deletedServer → ClientsFrame archived
frame:navigateTeacher → StudentsNavigate students to frame
frame:lockTeacher → StudentsChange lock mode in real-time
participant:locationClient → ServerTrack student viewport position
javascript
// Teacher broadcasts navigation to all students
socket.emit('frame:navigate', {
  frameId: '550e8400-e29b-41d4-a716-446655440000',
  smooth: true // Smooth scroll animation
});

Lock/Unlock Frame Viewport

javascript
// Teacher locks students to current frame
socket.emit('frame:lock', {
  frameId: '550e8400-e29b-41d4-a716-446655440000',
  lockMode: 'hard' // 'none' | 'soft' | 'hard'
});

Track Student Location

javascript
// Student reports viewport position
socket.emit('participant:location', {
  frameId: '550e8400-e29b-41d4-a716-446655440000', // or null if outside
  viewportBounds: {
    x: 100,
    y: 50,
    width: 1920,
    height: 1080
  }
});

Response Status Codes

CodeMeaningDescription
200OKRequest succeeded
201CreatedFrame created successfully
400Bad RequestInvalid frame data or parameters
401UnauthorizedMissing or invalid API key
404Not FoundFrame or board not found
500Internal Server ErrorServer error occurred

Common Use Cases

Create Presentation Mode

javascript
// Create 5 sequential slides
async function createPresentation(boardId, slideNames) {
  const slides = [];

  for (let i = 0; i < slideNames.length; i++) {
    const frame = await fetch(
      `https://api.boardapi.io/api/v1/boards/${boardId}/frames`,
      {
        method: 'POST',
        headers: {
          'X-API-Key': 'your-api-key',
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          name: slideNames[i],
          order: i,
          bounds_x: 1300 * i, // Horizontal layout
          bounds_y: 0,
          bounds_width: 1200,
          bounds_height: 800,
          lock_mode: 'soft'
        })
      }
    ).then(r => r.json());

    slides.push(frame);
  }

  return slides;
}

// Usage
const slides = await createPresentation('a1b2c3d4', [
  'Introduction',
  'Key Concepts',
  'Practice Exercise',
  'Discussion',
  'Summary'
]);

Setup Breakout Rooms with Groups

javascript
// Complete breakout room workflow
async function setupBreakoutRooms(boardId, templateFrameId, groups) {
  const rooms = [];

  for (let i = 0; i < groups.length; i++) {
    // Clone frame for each group
    const room = await fetch(
      `https://api.boardapi.io/api/v1/boards/${boardId}/frames/${templateFrameId}/clone`,
      {
        method: 'POST',
        headers: {
          'X-API-Key': 'your-api-key',
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          offsetX: 1300 * (i + 1),
          offsetY: 0,
          name: groups[i].name
        })
      }
    ).then(r => r.json());

    rooms.push(room);

    // Navigate students to their rooms via WebSocket
    groups[i].students.forEach(studentSocket => {
      studentSocket.emit('frame:navigate', {
        frameId: room.id,
        smooth: true
      });
    });
  }

  return rooms;
}

// Usage
const groups = [
  { name: 'Blue Team', students: [socket1, socket2] },
  { name: 'Red Team', students: [socket3, socket4] },
  { name: 'Green Team', students: [socket5, socket6] }
];

await setupBreakoutRooms('a1b2c3d4', 'template-frame-id', groups);

Switch to Exam Mode

javascript
// Lock all frames to hard mode for exam
async function enableExamMode(boardId) {
  const frames = await fetch(
    `https://api.boardapi.io/api/v1/boards/${boardId}/frames`,
    {
      headers: { 'X-API-Key': 'your-api-key' }
    }
  ).then(r => r.json());

  for (const frame of frames) {
    await fetch(
      `https://api.boardapi.io/api/v1/boards/${boardId}/frames/${frame.id}`,
      {
        method: 'PATCH',
        headers: {
          'X-API-Key': 'your-api-key',
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({ lock_mode: 'hard' })
      }
    );
  }

  console.log(`Locked ${frames.length} frames for exam mode`);
}

Architecture

For detailed technical architecture including database schema, viewport control algorithms, and integration patterns, see:

Next Steps