From miro-pack
Performs CRUD on Miro boards and items including sticky notes, shapes, cards, frames, tags via REST API v2.
npx claudepluginhub jeremylongshore/claude-code-plugins-plus-skills --plugin miro-packThis skill is limited to using the following tools:
The primary workflow for Miro integrations: full CRUD on boards and board items (sticky notes, shapes, cards, frames, tags) using the REST API v2 at `https://api.miro.com/v2/`.
Automates Miro whiteboard tasks: create/manage boards, sticky notes, frames, items, sharing, connectors via Rube MCP (Composio). Requires active Miro connection and tool schema search.
Automates Miro whiteboard operations like listing/creating boards, sticky notes, frames, sharing, connectors via Composio's Rube MCP toolkit. Use for programmatic Miro workflows.
Manages Miro connectors, images, embeds, app cards, and documents via REST API v2. Creates visual workflows and rich board layouts with lines, captions, and styles.
Share bugs, ideas, or general feedback.
The primary workflow for Miro integrations: full CRUD on boards and board items (sticky notes, shapes, cards, frames, tags) using the REST API v2 at https://api.miro.com/v2/.
boards:read and boards:write scopesmiro-hello-world)// POST https://api.miro.com/v2/boards
const board = await miroFetch('/v2/boards', 'POST', {
name: 'Sprint Retro — Week 12',
description: 'Team retrospective board',
teamId: 'your-team-id', // optional — creates in specific team
policy: {
sharingPolicy: {
access: 'private',
inviteToAccountAndBoardLinkAccess: 'no_access',
organizationAccess: 'private',
},
permissionsPolicy: {
collaborationToolsStartAccess: 'all_editors',
copyAccess: 'anyone',
sharingAccess: 'team_members_and_collaborators',
},
},
});
// GET https://api.miro.com/v2/boards/{board_id}
const board = await miroFetch(`/v2/boards/${boardId}`);
// Returns: id, name, description, owner, policy, createdAt, modifiedAt
// GET https://api.miro.com/v2/boards
// Supports filtering by team_id, project_id, query, sort, owner
const boards = await miroFetch('/v2/boards?limit=50&sort=last_modified');
for (const board of boards.data) {
console.log(`${board.id}: ${board.name} (modified: ${board.modifiedAt})`);
}
// PATCH https://api.miro.com/v2/boards/{board_id}
await miroFetch(`/v2/boards/${boardId}`, 'PATCH', {
name: 'Sprint Retro — Week 12 (CLOSED)',
description: 'Archived — action items in Jira',
});
// DELETE https://api.miro.com/v2/boards/{board_id}
await miroFetch(`/v2/boards/${boardId}`, 'DELETE');
// Sticky Note — POST /v2/boards/{board_id}/sticky_notes
const note = await miroFetch(`/v2/boards/${boardId}/sticky_notes`, 'POST', {
data: { content: 'Went well: team communication', shape: 'square' },
style: { fillColor: 'light_green', textAlign: 'center' },
position: { x: -200, y: 0 },
geometry: { width: 199 },
});
// Shape — POST /v2/boards/{board_id}/shapes
const shape = await miroFetch(`/v2/boards/${boardId}/shapes`, 'POST', {
data: { content: 'Decision Point', shape: 'rhombus' },
style: { fillColor: '#ff6b6b', borderColor: '#333333', borderWidth: 2 },
position: { x: 0, y: 200 },
geometry: { width: 200, height: 200 },
});
// Card — POST /v2/boards/{board_id}/cards
const card = await miroFetch(`/v2/boards/${boardId}/cards`, 'POST', {
data: {
title: 'Improve deploy pipeline',
description: 'Reduce deploy time from 15min to 5min',
dueDate: '2025-04-01T00:00:00Z',
assigneeId: 'user-id-123',
},
style: { cardTheme: '#2d9bf0' },
position: { x: 200, y: 0 },
});
// Frame — POST /v2/boards/{board_id}/frames
const frame = await miroFetch(`/v2/boards/${boardId}/frames`, 'POST', {
data: {
title: 'What went well',
format: 'custom', // 'custom' | 'a4' | 'letter' | etc.
type: 'freeform', // 'freeform' | 'heap_map' | etc.
showContent: true,
},
position: { x: -400, y: -200 },
geometry: { width: 600, height: 400 },
});
// Text — POST /v2/boards/{board_id}/texts
const text = await miroFetch(`/v2/boards/${boardId}/texts`, 'POST', {
data: { content: '<strong>Action Items</strong>' },
style: { fontSize: 24, textAlign: 'left' },
position: { x: 0, y: -300 },
geometry: { width: 300 },
});
// GET https://api.miro.com/v2/boards/{board_id}/items/{item_id}
const item = await miroFetch(`/v2/boards/${boardId}/items/${itemId}`);
// Or type-specific:
// GET /v2/boards/{board_id}/sticky_notes/{item_id}
// PATCH https://api.miro.com/v2/boards/{board_id}/sticky_notes/{item_id}
await miroFetch(`/v2/boards/${boardId}/sticky_notes/${noteId}`, 'PATCH', {
data: { content: 'Updated: team communication was excellent' },
style: { fillColor: 'light_blue' },
});
// DELETE https://api.miro.com/v2/boards/{board_id}/items/{item_id}
await miroFetch(`/v2/boards/${boardId}/items/${itemId}`, 'DELETE');
Tags can be attached to sticky notes and cards (up to 8 per item).
// Step 1: Create a tag — POST /v2/boards/{board_id}/tags
const tag = await miroFetch(`/v2/boards/${boardId}/tags`, 'POST', {
title: 'Action Item',
fillColor: 'red', // red | light_green | cyan | yellow | magenta | green | blue | etc.
});
// Step 2: Attach tag to an item — POST /v2/boards/{board_id}/items/{item_id}/tags
await miroFetch(`/v2/boards/${boardId}/items/${noteId}/tags`, 'POST', {
tagId: tag.id,
});
// NOTE: Tag changes via API do NOT appear on the board in realtime.
// Users must refresh the board to see tag updates made via REST API.
// List members — GET /v2/boards/{board_id}/members
const members = await miroFetch(`/v2/boards/${boardId}/members?limit=50`);
// Share board with a user — POST /v2/boards/{board_id}/members
await miroFetch(`/v2/boards/${boardId}/members`, 'POST', {
emails: ['colleague@company.com'],
role: 'commenter', // 'viewer' | 'commenter' | 'editor' | 'coowner'
message: 'Check out our retro board!',
});
async function miroFetch(path: string, method = 'GET', body?: unknown) {
const response = await fetch(`https://api.miro.com${path}`, {
method,
headers: {
'Authorization': `Bearer ${process.env.MIRO_ACCESS_TOKEN}`,
'Content-Type': 'application/json',
},
...(body ? { body: JSON.stringify(body) } : {}),
});
if (!response.ok) {
const error = await response.json().catch(() => ({}));
throw new Error(`Miro ${method} ${path}: ${response.status} ${error.message ?? ''}`);
}
if (response.status === 204) return null; // DELETE returns no body
return response.json();
}
| Error | Status | Cause | Solution |
|---|---|---|---|
boardNotFound | 404 | Board deleted or wrong ID | Verify board ID |
invalidInput | 400 | Missing required field | Check request body per item type |
insufficientPermissions | 403 | Missing boards:write scope | Re-authorize with correct scopes |
itemNotFound | 404 | Item ID wrong or deleted | Re-fetch board items |
duplicateTagTitle | 409 | Tag name already exists on board | Reuse existing tag ID |
For connectors and visual relationships, see miro-core-workflow-b.