Application Configuration
Navigate to the Miro dashboard, access your profile settings, and select "Your apps" to initialize a new application. If you are not currently assigned to a workspace, the system will prompt you to create one, typically defaulting to a development team. Define the application name during setup. The "Expire user authorization token" setting provides an extra layer of security by allowing you to invalidate previously issued tokens. The "App Publication Status" controls whether your integration is available to external users; keeping it unpublished is suitable for private tools. Under "Permissions", enable the necessary OAuth scopes such as boards:read and boards:write, then install the app to generate your access token.
Web SDK vs REST API
| Feature / Requirement | Miro Web SDK | Miro REST APIs |
|---|---|---|
| Real-time user interaction via UI extension points (e.g., custom form panels) | Supported | Not Supported |
| Access board items | Supported | Supported |
| Access team-level data | Supported | Supported |
| Access organization-level data | Supported | Supported |
| Third-party integration origin | Launched from Miro | Launched from external service |
| Board state requirement | Must be open/online | Can be offline |
| Backend hosting | Optional | Required |
| Programming language | TypeScript / JavaScript | Any |
API Interaction with Python
Prerequisites
Utilize the requests library for HTTP interactions.
import requests
ACCESS_TOKEN = "YOUR_ACCESS_TOKEN"
api_client = requests.Session()
base_headers = {
"Authorization": f"Bearer {ACCESS_TOKEN}",
"Content-Type": "application/"
}
Connection Management
Leaving HTTP connections open after API calls exhausts system resources, often triggering ConnectionError or Max retries exceeded exceptions. Always use context managers to ensure connections are properly terminated.
with api_client.get(url, headers=base_headers) as resp:
resp.raise_for_status()
data = resp.()
Board Initialization
Method 1: Extract the board identifier directly from the URL of a manually created board: https://miro.com/app/board/{board_identifier}/
Method 2: Programmatically generate a new board.
boards_endpoint = "https://api.miro.com/v2/boards"
with api_client.post(boards_endpoint, headers=base_headers) as resp:
resp.raise_for_status()
board_identifier = resp.()["id"]
App Cards vs Standard Cards
App Cards integrate external third-party data, acting as preview widgets with custom fields and status indicators. Standard Cards are native widgets used to display basic task information like titles and descriptions.
Creating an App Card
app_card_url = f"https://api.miro.com/v2/boards/{board_identifier}/app_cards"
app_card_payload = {
"data": {
"fields": [
{
"fillColor": "#ff5733",
"iconShape": "round",
"textColor": "#ffffff",
"iconUrl": "https://cdn.example.com/icon.png",
"tooltip": "Task priority level",
"value": "Priority: High"
}
],
"description": "External synchronization record",
"title": "Synced Task Item",
"status": "connected"
},
"style": {"fillColor": "#f5f5f5"},
"position": {"x": 500, "y": 500},
"geometry": {"height": 100, "width": 300}
}
with api_client.post(app_card_url, =app_card_payload, headers=base_headers) as resp:
resp.raise_for_status()
print(resp.())
Creating a Standard Card
std_card_url = f"https://api.miro.com/v2/boards/{board_identifier}/cards"
std_card_payload = {
"data": {
"assigneeId": "MEMBER_ID",
"description": "Scheduled maintenance window",
"dueDate": "2024-11-01T10:00:00.000Z",
"title": "Server Maintenance"
},
"style": {"cardTheme": "#2d9bf0"},
"position": {"x": 800, "y": 800},
"geometry": {"height": 80, "width": 350}
}
with api_client.post(std_card_url, =std_card_payload, headers=base_headers) as resp:
resp.raise_for_status()
print(resp.())
Establishing Connectors
connector_url = f"https://api.miro.com/v2/boards/{board_identifier}/connectors"
connector_payload = {
"startItem": {"id": origin_item_id},
"endItem": {"id": destination_item_id},
"captions": [{"content": "Depends on"}]
}
with api_client.post(connector_url, =connector_payload, headers=base_headers) as resp:
resp.raise_for_status()
print(resp.())
Retrieving Item Data
Fetch all items on a board:
items_url = f"https://api.miro.com/v2/boards/{board_identifier}/items"
with api_client.get(items_url, headers=base_headers) as resp:
resp.raise_for_status()
all_items = resp.().get("data", [])
Fetch a specific card by its identifier:
specific_item_url = f"https://api.miro.com/v2/boards/{board_identifier}/cards/{target_item_id}"
with api_client.get(specific_item_url, headers=base_headers) as resp:
resp.raise_for_status()
item_data = resp.()
Modifying Card Attributes
update_url = f"https://api.miro.com/v2/boards/{board_identifier}/cards/{target_card_id}"
update_payload = {"data": {"title": "Updated Task Title"}}
with api_client.patch(update_url, =update_payload, headers=base_headers) as resp:
if resp.status_code == 200:
print("Update successful")
Removing Items
delete_url = f"https://api.miro.com/v2/boards/{board_identifier}/items/{target_item_id}"
with api_client.delete(delete_url, headers=base_headers) as resp:
if resp.status_code == 204:
print("Deletion successful")