Skills Development Miro API Integration: Board Operations

Miro API Integration: Board Operations

v20260423
miro-hello-world
A comprehensive guide and working example demonstrating how to programmatically interact with the Miro REST API v2 using TypeScript. This skill covers the full board lifecycle, allowing users to create boards, add diverse items (sticky notes, shapes), connect elements with connectors, and list all board contents. Ideal for testing new Miro integrations or automating workflow processes.
Get Skill
81 downloads
Overview

Miro Hello World

Overview

Minimal working example: create a board, add a sticky note, add a shape, connect them, and read the results back — all using the Miro REST API v2.

Prerequisites

  • Completed miro-install-auth setup
  • Valid access token with boards:read and boards:write scopes
  • @mirohq/miro-api installed

Instructions

Step 1: Create a Board

import { MiroApi } from '@mirohq/miro-api';

const api = new MiroApi(process.env.MIRO_ACCESS_TOKEN!);

async function createBoard() {
  // POST https://api.miro.com/v2/boards
  const response = await api.createBoard({
    name: 'Hello World Board',
    description: 'Created via REST API v2',
    policy: {
      sharingPolicy: {
        access: 'private',          // 'private' | 'view' | 'comment' | 'edit'
        inviteToAccountAndBoardLinkAccess: 'no_access',
      },
      permissionsPolicy: {
        collaborationToolsStartAccess: 'all_editors',
        copyAccess: 'anyone',
        sharingAccess: 'owners_and_coowners',
      },
    },
  });

  const boardId = response.body.id;
  console.log(`Board created: ${boardId}`);
  console.log(`View at: https://miro.com/app/board/${boardId}/`);
  return boardId;
}

Step 2: Add a Sticky Note

async function addStickyNote(boardId: string) {
  // POST https://api.miro.com/v2/boards/{board_id}/sticky_notes
  const response = await fetch(
    `https://api.miro.com/v2/boards/${boardId}/sticky_notes`,
    {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${process.env.MIRO_ACCESS_TOKEN}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        data: {
          content: 'Hello from the API!',
          shape: 'square',          // 'square' | 'rectangle'
        },
        style: {
          fillColor: 'light_yellow', // light_yellow | light_green | light_blue | light_pink | etc.
          textAlign: 'center',       // 'left' | 'center' | 'right'
          textAlignVertical: 'middle',
        },
        position: { x: 0, y: 0 },
        geometry: { width: 200 },
      }),
    }
  );

  const note = await response.json();
  console.log(`Sticky note created: ${note.id} (type: ${note.type})`);
  return note.id;
}

Step 3: Add a Shape

async function addShape(boardId: string) {
  // POST https://api.miro.com/v2/boards/{board_id}/shapes
  const response = await fetch(
    `https://api.miro.com/v2/boards/${boardId}/shapes`,
    {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${process.env.MIRO_ACCESS_TOKEN}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        data: {
          content: 'Next Step',
          shape: 'round_rectangle',  // rectangle | circle | triangle | rhombus | round_rectangle | etc.
        },
        style: {
          fillColor: '#4262ff',
          fontFamily: 'arial',
          fontSize: 14,
          textAlign: 'center',
          borderColor: '#1a1a2e',
          borderWidth: 2,
          borderStyle: 'normal',     // 'normal' | 'dashed' | 'dotted'
        },
        position: { x: 400, y: 0 },
        geometry: { width: 200, height: 100 },
      }),
    }
  );

  const shape = await response.json();
  console.log(`Shape created: ${shape.id} (type: ${shape.type})`);
  return shape.id;
}

Step 4: Connect Items with a Connector

async function connectItems(boardId: string, startId: string, endId: string) {
  // POST https://api.miro.com/v2/boards/{board_id}/connectors
  const response = await fetch(
    `https://api.miro.com/v2/boards/${boardId}/connectors`,
    {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${process.env.MIRO_ACCESS_TOKEN}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        startItem: { id: startId },
        endItem: { id: endId },
        captions: [{ content: 'leads to' }],
        style: {
          strokeColor: '#1a1a2e',
          strokeWidth: 2,
          startStrokeCap: 'none',
          endStrokeCap: 'stealth',   // none | stealth | arrow | filled_triangle | etc.
        },
      }),
    }
  );

  const connector = await response.json();
  console.log(`Connector created: ${connector.id}`);
  return connector.id;
}

Step 5: List All Items on the Board

async function listBoardItems(boardId: string) {
  // GET https://api.miro.com/v2/boards/{board_id}/items
  // Returns cursor-paginated results
  const response = await fetch(
    `https://api.miro.com/v2/boards/${boardId}/items?limit=50`,
    {
      headers: {
        'Authorization': `Bearer ${process.env.MIRO_ACCESS_TOKEN}`,
      },
    }
  );

  const result = await response.json();
  console.log(`Board has ${result.data.length} items:`);
  for (const item of result.data) {
    console.log(`  - ${item.type}: ${item.id} (${item.data?.content ?? 'no content'})`);
  }

  // Handle pagination
  if (result.cursor) {
    console.log(`More items available. Next cursor: ${result.cursor}`);
  }
}

Step 6: Run the Complete Flow

async function main() {
  const boardId = await createBoard();
  const noteId = await addStickyNote(boardId);
  const shapeId = await addShape(boardId);
  await connectItems(boardId, noteId, shapeId);
  await listBoardItems(boardId);
  console.log('\nDone! Open the board in Miro to see your items.');
}

main().catch(console.error);

Miro REST API v2 Item Types

Type Create Endpoint Key Properties
sticky_note /v2/boards/{id}/sticky_notes content, shape, fillColor
shape /v2/boards/{id}/shapes content, shape, fillColor, borderStyle
card /v2/boards/{id}/cards title, description, dueDate, assigneeId
text /v2/boards/{id}/texts content, fontSize
frame /v2/boards/{id}/frames title, showContent, childrenIds
image /v2/boards/{id}/images url or data (base64)
document /v2/boards/{id}/documents url
embed /v2/boards/{id}/embeds url
app_card /v2/boards/{id}/app_cards title, description, fields, status
connector /v2/boards/{id}/connectors startItem, endItem, captions

All create endpoints require boards:write scope. All GET endpoints require boards:read.

Error Handling

Error HTTP Status Cause Solution
boardNotFound 404 Invalid board_id Verify board exists and token has access
insufficientPermissions 403 Missing boards:write Add scope in app settings
invalidInput 400 Bad request body Check required fields per item type
rateLimitExceeded 429 Too many requests Implement backoff (see miro-rate-limits)

Resources

Next Steps

Proceed to miro-local-dev-loop for development workflow setup, or miro-core-workflow-a for board management patterns.

Info
Category Development
Name miro-hello-world
Version v20260423
Size 7.6KB
Updated At 2026-04-28
Language