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
| Concept | Description |
|---|---|
| Frame | Rectangular area with bounds (x, y, width, height) |
| Lock Mode | Controls viewport navigation: none, soft, hard |
| Order | Sequential position for presentation mode |
| Visibility | Frame state: active, hidden, archived |
| Clone | Create frame copies with offset (for breakout rooms) |
Lock Modes
| Mode | Behavior | Use Case |
|---|---|---|
none | Free navigation (default) | Open collaboration |
soft | Can escape with warning indicator | Guided work with flexibility |
hard | Cannot leave frame bounds | Exams, strict control |
Authentication
All Frames API endpoints require API key authentication:
curl -H "X-API-Key: your-api-key" \
https://api.boardapi.io/api/v1/boards/:boardId/framesAPI keys are passed via the X-API-Key header. Get your API key from the Developer Dashboard.
Base URL
https://api.boardapi.io/api/v1For development:
http://localhost:4000/api/v1Endpoints Overview
| Method | Endpoint | Description |
|---|---|---|
POST | /boards/:boardId/frames | Create a new frame |
GET | /boards/:boardId/frames | List all frames for a board |
GET | /boards/:boardId/frames/:id | Get a specific frame |
PATCH | /boards/:boardId/frames/:id | Update frame properties |
DELETE | /boards/:boardId/frames/:id | Archive a frame |
POST | /boards/:boardId/frames/:id/clone | Clone a frame with offset |
Create Frame
Creates a new rectangular zone on a board for spatial organization.
Request
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
| Parameter | Type | Description |
|---|---|---|
boardId | string | Board UUID |
Request Body Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Frame name (max 255 characters), e.g., "Slide 1", "Introduction" |
order | number | No | Sequence order for presentations (default: 0, min: 0) |
bounds_x | number | No | X coordinate in pixels (default: 0) |
bounds_y | number | No | Y coordinate in pixels (default: 0) |
bounds_width | number | No | Width in pixels (default: 1200, min: 100) |
bounds_height | number | No | Height in pixels (default: 800, min: 100) |
lock_mode | string | No | Viewport lock: none, soft, hard (default: none) |
background_color | string | No | Hex color code (e.g., #FFFFFF) |
visibility | string | No | Frame state: active, hidden, archived (default: active) |
parent_frame_id | string | No | Parent frame UUID (for tracking clones in breakout rooms) |
Response
Status Code: 201 Created
{
"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
| Field | Type | Description |
|---|---|---|
id | string | Frame UUID |
board_id | string | Parent board UUID |
name | string | Frame name |
order | number | Sequence position |
bounds_x | number | X coordinate |
bounds_y | number | Y coordinate |
bounds_width | number | Width in pixels |
bounds_height | number | Height in pixels |
lock_mode | string | Viewport control mode |
background_color | string | Hex color or null |
visibility | string | Current state |
parent_frame_id | string | Parent frame UUID (for clones) or null |
created_at | string | ISO 8601 creation timestamp |
updated_at | string | ISO 8601 update timestamp |
Example Requests
JavaScript/TypeScript:
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:
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
$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 Code | Error | Description |
|---|---|---|
400 | Bad Request | Invalid frame data (e.g., width < 100, invalid lock_mode) |
404 | Not Found | Board does not exist or parent_frame_id not found |
401 | Unauthorized | Invalid or missing API key |
List All Frames
Retrieves all frames for a board, ordered by sequence. By default excludes archived frames.
Request
GET /boards/:boardId/frames?includeArchived=false
X-API-Key: your-api-keyURL Parameters
| Parameter | Type | Description |
|---|---|---|
boardId | string | Board UUID |
Query Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
includeArchived | boolean | No | Include archived frames (default: false) |
Response
Status Code: 200 OK
[
{
"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:
// 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:
# 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
$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 Code | Error | Description |
|---|---|---|
404 | Not Found | Board does not exist |
401 | Unauthorized | Invalid API key |
Get Frame
Retrieves a specific frame by ID.
Request
GET /boards/:boardId/frames/:id
X-API-Key: your-api-keyURL Parameters
| Parameter | Type | Description |
|---|---|---|
boardId | string | Board UUID |
id | string | Frame UUID |
Response
Status Code: 200 OK
{
"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:
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:
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 Code | Error | Description |
|---|---|---|
404 | Not Found | Frame or board not found |
401 | Unauthorized | Invalid API key |
Update Frame
Updates frame properties such as name, bounds, lock mode, or visibility.
Request
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
| Parameter | Type | Description |
|---|---|---|
boardId | string | Board UUID |
id | string | Frame UUID |
Request Body Parameters
All fields are optional. Only provided fields will be updated.
| Parameter | Type | Description |
|---|---|---|
name | string | New frame name |
order | number | New sequence position |
bounds_x | number | New X coordinate |
bounds_y | number | New Y coordinate |
bounds_width | number | New width (min: 100) |
bounds_height | number | New height (min: 100) |
lock_mode | string | New lock mode: none, soft, hard |
background_color | string | New hex color or null to remove |
visibility | string | New state: active, hidden, archived |
Response
Status Code: 200 OK
{
"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:
// 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:
# 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
$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 Code | Error | Description |
|---|---|---|
400 | Bad Request | Invalid update data (e.g., width < 100, invalid lock_mode) |
404 | Not Found | Frame or board not found |
401 | Unauthorized | Invalid 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
DELETE /boards/:boardId/frames/:id
X-API-Key: your-api-keyURL Parameters
| Parameter | Type | Description |
|---|---|---|
boardId | string | Board UUID |
id | string | Frame UUID |
Response
Status Code: 200 OK
{
"message": "Frame archived successfully"
}Example Requests
JavaScript/TypeScript:
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:
curl -X DELETE \
-H "X-API-Key: your-api-key" \
'https://api.boardapi.io/api/v1/boards/a1b2c3d4/frames/550e8400'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(unlessincludeArchived=true) - Objects within frame are NOT deleted
- Frame can be restored by
PATCHwithvisibility: active
Restoring Archived Frames
// 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 Code | Error | Description |
|---|---|---|
404 | Not Found | Frame or board not found |
401 | Unauthorized | Invalid 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
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
| Parameter | Type | Description |
|---|---|---|
boardId | string | Board UUID |
id | string | Frame UUID to clone |
Request Body Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
offsetX | number | No | Horizontal offset in pixels (default: 0) |
offsetY | number | No | Vertical offset in pixels (default: 0) |
name | string | No | Name for cloned frame (default: original name + " (Copy)") |
Response
Status Code: 201 Created
{
"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:
// 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:
# 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
// 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
// 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 Code | Error | Description |
|---|---|---|
404 | Not Found | Source frame or board not found |
401 | Unauthorized | Invalid API key |
400 | Bad Request | Invalid offset values |
WebSocket Integration
Frames support real-time synchronization via WebSocket events. See WebSocket API for connection details.
Frame Events
| Event | Direction | Description |
|---|---|---|
frame:created | Server → Clients | New frame created |
frame:updated | Server → Clients | Frame properties changed |
frame:deleted | Server → Clients | Frame archived |
frame:navigate | Teacher → Students | Navigate students to frame |
frame:lock | Teacher → Students | Change lock mode in real-time |
participant:location | Client → Server | Track student viewport position |
Navigate Students to Frame
// 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
// Teacher locks students to current frame
socket.emit('frame:lock', {
frameId: '550e8400-e29b-41d4-a716-446655440000',
lockMode: 'hard' // 'none' | 'soft' | 'hard'
});Track Student Location
// 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
| Code | Meaning | Description |
|---|---|---|
200 | OK | Request succeeded |
201 | Created | Frame created successfully |
400 | Bad Request | Invalid frame data or parameters |
401 | Unauthorized | Missing or invalid API key |
404 | Not Found | Frame or board not found |
500 | Internal Server Error | Server error occurred |
Common Use Cases
Create Presentation Mode
// 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
// 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
// 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
- WebSocket API - Real-time frame events
- External Components SDK - Access frames from custom components
- Board API - Parent board operations
- Integration Guide - Complete examples