What this skill does
Generates Miro board content in two modes:
- API mode — Python code using the Miro REST API v2 to programmatically create a board with frames, sticky notes, shapes, connectors, and text.
- Prompt mode — A structured natural-language prompt ready to paste into Miro AI (or to use as a manual build guide) when API access is not available.
Supports the following analyst board types:
| Board type |
Use case |
| User story map |
Epics across top, stories in columns below, personas as swim lanes |
| Process flow |
AS-IS or TO-BE swim-lane diagrams |
| Stakeholder map |
Influence × interest grid |
| Gap analysis |
Current state / gap / target state columns |
| Impact/effort matrix |
2×2 prioritisation quadrant |
| Retrospective |
Start / Stop / Continue (or 4Ls, Mad-Sad-Glad, etc.) |
| Affinity map |
Clustered sticky notes from research data |
When to use it
- User wants to create or populate a Miro board from existing content (stories, process notes, stakeholder list, etc.).
- User asks to "make a Miro board", "visualise this in Miro", or "create a [board type] in Miro".
- User has content from another skill output (e.g.,
write-user-story, stakeholder-map) and wants it on a board.
- User wants a Miro AI prompt to generate a board without writing API code.
Prerequisites (API mode)
- A Miro account with a board created (or the skill will create one).
- A Miro personal access token or OAuth token: developers.miro.com.
- Python with
requests installed (uv pip install requests).
Instructions
Step 1 — Identify the board type and content
Ask the user (if not stated):
- Which board type from the list above?
- What content to populate it with? (stories, process steps, stakeholder names, etc.)
- Which mode — API or prompt?
Step 2 — Choose the output mode
API mode — use when:
- User has a Miro access token.
- Content is structured and repeated (many sticky notes, large story maps).
- User wants a reproducible, scriptable board.
Prompt mode — use when:
- User wants to use Miro AI or build manually.
- No API token is available.
- Content is small enough to describe in text.
Step 3 — Generate the output
Follow the board-type instructions below, then produce the output using the format for the chosen mode.
Board type instructions
User story map
- Create a frame for the entire board.
- Add epics as a horizontal row of shapes at the top (one shape per epic, left to right in priority order).
- Under each epic, add user stories as a vertical column of sticky notes (blue by default).
- Add persona rows as a left-margin label if multiple personas are in scope.
- Add a release line (horizontal connector) to separate MVP stories from later releases.
Process flow (swim-lane)
- Create one frame per swim lane (actor or system).
- Add process steps as shapes (rectangles for tasks, diamonds for decisions) within each lane.
- Connect steps with connectors to show flow.
- Use colour to distinguish: happy path (green), exception path (orange), manual step (yellow).
- Add a start (circle) and end (circle with border) node.
Stakeholder map
- Create a 2×2 frame with axes: Influence (Y) × Interest (X).
- Quadrant labels: Manage closely (high/high), Keep satisfied (high/low), Keep informed (low/high), Monitor (low/low).
- Add each stakeholder as a sticky note in their quadrant.
- Colour-code by group (business, technical, external).
Gap analysis board
- Create three column frames: Current State | Gap | Target State.
- Add sticky notes per topic row (process, data, technology, people).
- Use red for gaps, green for target state items, grey for current state.
- Add a priority indicator (high/medium/low label) to each gap.
Impact/effort matrix
- Create a 2×2 frame with axes: Impact (Y) × Effort (X).
- Quadrant labels: Quick wins (high/low), Major projects (high/high), Fill-ins (low/low), Thankless tasks (low/high).
- Add each item as a sticky note in the appropriate quadrant.
Retrospective
- Create column frames per category (Start / Stop / Continue, or chosen format).
- Add sticky notes per observation.
- Use voting dots (circles) to indicate team consensus items.
- Add an action items frame at the right with owner and due date per action.
Affinity map
- Create an unsorted frame for raw input sticky notes.
- Create cluster frames for each theme (label at top).
- Move sticky notes into clusters.
- Add a theme label shape above each cluster.
Output format — API mode
"""
Miro board creator — [Board type]: [Title]
Requirements: pip install requests
Set MIRO_TOKEN env variable before running.
"""
import os
import requests
TOKEN = os.environ["MIRO_TOKEN"]
HEADERS = {
"Authorization": f"Bearer {TOKEN}",
"Content-Type": "application/json",
}
BASE_URL = "https://api.miro.com/v2"
def create_board(name: str) -> str:
"""Create a new Miro board and return its ID."""
response = requests.post(
f"{BASE_URL}/boards",
headers=HEADERS,
json={"name": name, "policy": {"permissionsPolicy": {"collaborationToolsStartAccess": "all_editors"}}},
)
response.raise_for_status()
board_id = response.json()["id"]
print(f"Board created: {response.json()['viewLink']}")
return board_id
def add_sticky(board_id: str, content: str, x: float, y: float, color: str = "yellow") -> str:
"""Add a sticky note to the board."""
response = requests.post(
f"{BASE_URL}/boards/{board_id}/sticky_notes",
headers=HEADERS,
json={
"data": {"content": content, "shape": "square"},
"style": {"fillColor": color},
"position": {"x": x, "y": y, "origin": "center"},
},
)
response.raise_for_status()
return response.json()["id"]
def add_frame(board_id: str, title: str, x: float, y: float, width: float, height: float) -> str:
"""Add a frame (container) to the board."""
response = requests.post(
f"{BASE_URL}/boards/{board_id}/frames",
headers=HEADERS,
json={
"data": {"title": title, "format": "custom", "type": "freeform"},
"position": {"x": x, "y": y, "origin": "center"},
"geometry": {"width": width, "height": height},
},
)
response.raise_for_status()
return response.json()["id"]
def add_text(board_id: str, content: str, x: float, y: float, font_size: int = 14) -> str:
"""Add a text label to the board."""
response = requests.post(
f"{BASE_URL}/boards/{board_id}/texts",
headers=HEADERS,
json={
"data": {"content": content},
"style": {"fontSize": str(font_size), "textAlign": "center"},
"position": {"x": x, "y": y, "origin": "center"},
},
)
response.raise_for_status()
return response.json()["id"]
def add_connector(board_id: str, start_id: str, end_id: str) -> str:
"""Add a connector between two items."""
response = requests.post(
f"{BASE_URL}/boards/{board_id}/connectors",
headers=HEADERS,
json={
"startItem": {"id": start_id},
"endItem": {"id": end_id},
"style": {"strokeColor": "#333333", "strokeWidth": "2"},
},
)
response.raise_for_status()
return response.json()["id"]
# ── Board content ──────────────────────────────────────────────────────────────
def build_board():
board_id = create_board("[Board title]")
# [Board-type-specific creation code goes here]
# Example: add_frame, add_sticky, add_text, add_connector calls
# with x/y coordinates laid out on a grid
print("Done.")
if __name__ == "__main__":
build_board()
The skill fills in build_board() with board-type-specific calls using the content provided by the user.
Output format — Prompt mode
## Miro AI prompt: [Board type] — [Title]
> Paste this prompt into Miro AI (board → AI assistant → "Generate board").
---
Create a [board type] Miro board titled "[Title]".
[Board-type-specific layout description]
Content to include:
[Structured list of items, epics, stakeholders, steps, etc.]
Formatting:
- Use [colour] sticky notes for [category].
- Use [colour] sticky notes for [category].
- Group related items in labelled frames.
- Add connectors between [items] to show [relationship].
Examples
Example 1 — User story map from epic content
Input: User provides an epic with 6 stories across 2 personas.
Expected output (API mode): Python script that creates a board, adds the epic as a header frame, adds 6 sticky notes in columns beneath it, adds 2 persona label texts on the left margin, and draws a release line after story 3.
Example 2 — Stakeholder map (prompt mode)
Input: "Create a Miro stakeholder map for these 8 people: [list with influence/interest ratings]."
Expected output (prompt mode): A Miro AI prompt that describes the 2×2 grid, places each person in the correct quadrant, and colour-codes by team.
Example 3 — Gap analysis board
Input: "I have a gap analysis output from the gap-analysis skill. Put it on a Miro board."
Expected output: Three-column board (Current State / Gap / Target State) with one row per topic, sticky notes colour-coded by priority, and a legend frame.
Notes
- Miro API coordinates use pixels; (0, 0) is the board centre. Lay items out on a grid with consistent spacing (e.g., 250px between sticky notes, 400px between columns).
- Sticky note colour values accepted by the API:
"yellow", "light_yellow", "orange", "light_green", "cyan", "light_pink", "violet", "red", "light_blue", "blue", "dark_blue", "black", "gray", "dark_gray", "white".
- The Miro API rate limit is 100 requests per second per token. For large boards, add a small delay between calls.
- Prompt mode output quality depends on the Miro AI model version in use; review and adjust positions manually after generation.
- Board sharing policy defaults to "all editors can collaborate" — adjust the
permissionsPolicy in create_board() if the board is sensitive.
1---2name: miro-board3description: Generates Miro board content in two modes:4---56## What this skill does78Generates Miro board content in two modes:910- **API mode** — Python code using the Miro REST API v2 to programmatically create a board with frames, sticky notes, shapes, connectors, and text.11- **Prompt mode** — A structured natural-language prompt ready to paste into Miro AI (or to use as a manual build guide) when API access is not available.1213Supports the following analyst board types:1415| Board type | Use case |16| --- | --- |17| User story map | Epics across top, stories in columns below, personas as swim lanes |18| Process flow | AS-IS or TO-BE swim-lane diagrams |19| Stakeholder map | Influence × interest grid |20| Gap analysis | Current state / gap / target state columns |21| Impact/effort matrix | 2×2 prioritisation quadrant |22| Retrospective | Start / Stop / Continue (or 4Ls, Mad-Sad-Glad, etc.) |23| Affinity map | Clustered sticky notes from research data |2425## When to use it2627- User wants to create or populate a Miro board from existing content (stories, process notes, stakeholder list, etc.).28- User asks to "make a Miro board", "visualise this in Miro", or "create a [board type] in Miro".29- User has content from another skill output (e.g., `write-user-story`, `stakeholder-map`) and wants it on a board.30- User wants a Miro AI prompt to generate a board without writing API code.3132## Prerequisites (API mode)3334- A Miro account with a board created (or the skill will create one).35- A Miro personal access token or OAuth token: [developers.miro.com](https://developers.miro.com/docs/rest-api-build-your-first-hello-world-app).36- Python with `requests` installed (`uv pip install requests`).3738## Instructions3940### Step 1 — Identify the board type and content4142Ask the user (if not stated):431. Which board type from the list above?442. What content to populate it with? (stories, process steps, stakeholder names, etc.)453. Which mode — API or prompt?4647### Step 2 — Choose the output mode4849**API mode** — use when:50- User has a Miro access token.51- Content is structured and repeated (many sticky notes, large story maps).52- User wants a reproducible, scriptable board.5354**Prompt mode** — use when:55- User wants to use Miro AI or build manually.56- No API token is available.57- Content is small enough to describe in text.5859### Step 3 — Generate the output6061Follow the board-type instructions below, then produce the output using the format for the chosen mode.6263---6465## Board type instructions6667### User story map6869- Create a **frame** for the entire board.70- Add **epics** as a horizontal row of shapes at the top (one shape per epic, left to right in priority order).71- Under each epic, add **user stories** as a vertical column of sticky notes (blue by default).72- Add **persona rows** as a left-margin label if multiple personas are in scope.73- Add a **release line** (horizontal connector) to separate MVP stories from later releases.7475### Process flow (swim-lane)7677- Create one **frame** per swim lane (actor or system).78- Add **process steps** as shapes (rectangles for tasks, diamonds for decisions) within each lane.79- Connect steps with **connectors** to show flow.80- Use **colour** to distinguish: happy path (green), exception path (orange), manual step (yellow).81- Add a **start** (circle) and **end** (circle with border) node.8283### Stakeholder map8485- Create a **2×2 frame** with axes: Influence (Y) × Interest (X).86- Quadrant labels: Manage closely (high/high), Keep satisfied (high/low), Keep informed (low/high), Monitor (low/low).87- Add each stakeholder as a **sticky note** in their quadrant.88- Colour-code by group (business, technical, external).8990### Gap analysis board9192- Create **three column frames**: Current State | Gap | Target State.93- Add **sticky notes** per topic row (process, data, technology, people).94- Use red for gaps, green for target state items, grey for current state.95- Add a **priority indicator** (high/medium/low label) to each gap.9697### Impact/effort matrix9899- Create a **2×2 frame** with axes: Impact (Y) × Effort (X).100- Quadrant labels: Quick wins (high/low), Major projects (high/high), Fill-ins (low/low), Thankless tasks (low/high).101- Add each item as a **sticky note** in the appropriate quadrant.102103### Retrospective104105- Create **column frames** per category (Start / Stop / Continue, or chosen format).106- Add **sticky notes** per observation.107- Use **voting dots** (circles) to indicate team consensus items.108- Add an **action items** frame at the right with owner and due date per action.109110### Affinity map111112- Create an **unsorted frame** for raw input sticky notes.113- Create **cluster frames** for each theme (label at top).114- Move sticky notes into clusters.115- Add a **theme label shape** above each cluster.116117---118119## Output format — API mode120121```python122"""123Miro board creator — [Board type]: [Title]124Requirements: pip install requests125Set MIRO_TOKEN env variable before running.126"""127128import os129import requests130131TOKEN = os.environ["MIRO_TOKEN"]132HEADERS = {133 "Authorization": f"Bearer {TOKEN}",134 "Content-Type": "application/json",135}136BASE_URL = "https://api.miro.com/v2"137138139def create_board(name: str) -> str:140 """Create a new Miro board and return its ID."""141 response = requests.post(142 f"{BASE_URL}/boards",143 headers=HEADERS,144 json={"name": name, "policy": {"permissionsPolicy": {"collaborationToolsStartAccess": "all_editors"}}},145 )146 response.raise_for_status()147 board_id = response.json()["id"]148 print(f"Board created: {response.json()['viewLink']}")149 return board_id150151152def add_sticky(board_id: str, content: str, x: float, y: float, color: str = "yellow") -> str:153 """Add a sticky note to the board."""154 response = requests.post(155 f"{BASE_URL}/boards/{board_id}/sticky_notes",156 headers=HEADERS,157 json={158 "data": {"content": content, "shape": "square"},159 "style": {"fillColor": color},160 "position": {"x": x, "y": y, "origin": "center"},161 },162 )163 response.raise_for_status()164 return response.json()["id"]165166167def add_frame(board_id: str, title: str, x: float, y: float, width: float, height: float) -> str:168 """Add a frame (container) to the board."""169 response = requests.post(170 f"{BASE_URL}/boards/{board_id}/frames",171 headers=HEADERS,172 json={173 "data": {"title": title, "format": "custom", "type": "freeform"},174 "position": {"x": x, "y": y, "origin": "center"},175 "geometry": {"width": width, "height": height},176 },177 )178 response.raise_for_status()179 return response.json()["id"]180181182def add_text(board_id: str, content: str, x: float, y: float, font_size: int = 14) -> str:183 """Add a text label to the board."""184 response = requests.post(185 f"{BASE_URL}/boards/{board_id}/texts",186 headers=HEADERS,187 json={188 "data": {"content": content},189 "style": {"fontSize": str(font_size), "textAlign": "center"},190 "position": {"x": x, "y": y, "origin": "center"},191 },192 )193 response.raise_for_status()194 return response.json()["id"]195196197def add_connector(board_id: str, start_id: str, end_id: str) -> str:198 """Add a connector between two items."""199 response = requests.post(200 f"{BASE_URL}/boards/{board_id}/connectors",201 headers=HEADERS,202 json={203 "startItem": {"id": start_id},204 "endItem": {"id": end_id},205 "style": {"strokeColor": "#333333", "strokeWidth": "2"},206 },207 )208 response.raise_for_status()209 return response.json()["id"]210211212# ── Board content ──────────────────────────────────────────────────────────────213214def build_board():215 board_id = create_board("[Board title]")216217 # [Board-type-specific creation code goes here]218 # Example: add_frame, add_sticky, add_text, add_connector calls219 # with x/y coordinates laid out on a grid220221 print("Done.")222223224if __name__ == "__main__":225 build_board()226```227228*The skill fills in `build_board()` with board-type-specific calls using the content provided by the user.*229230---231232## Output format — Prompt mode233234```markdown235## Miro AI prompt: [Board type] — [Title]236237> Paste this prompt into Miro AI (board → AI assistant → "Generate board").238239---240241Create a [board type] Miro board titled "[Title]".242243[Board-type-specific layout description]244245Content to include:246247[Structured list of items, epics, stakeholders, steps, etc.]248249Formatting:250- Use [colour] sticky notes for [category].251- Use [colour] sticky notes for [category].252- Group related items in labelled frames.253- Add connectors between [items] to show [relationship].254```255256---257258## Examples259260### Example 1 — User story map from epic content261262**Input:** User provides an epic with 6 stories across 2 personas.263**Expected output (API mode):** Python script that creates a board, adds the epic as a header frame, adds 6 sticky notes in columns beneath it, adds 2 persona label texts on the left margin, and draws a release line after story 3.264265### Example 2 — Stakeholder map (prompt mode)266267**Input:** "Create a Miro stakeholder map for these 8 people: [list with influence/interest ratings]."268**Expected output (prompt mode):** A Miro AI prompt that describes the 2×2 grid, places each person in the correct quadrant, and colour-codes by team.269270### Example 3 — Gap analysis board271272**Input:** "I have a gap analysis output from the gap-analysis skill. Put it on a Miro board."273**Expected output:** Three-column board (Current State / Gap / Target State) with one row per topic, sticky notes colour-coded by priority, and a legend frame.274275## Notes276277- Miro API coordinates use pixels; (0, 0) is the board centre. Lay items out on a grid with consistent spacing (e.g., 250px between sticky notes, 400px between columns).278- Sticky note colour values accepted by the API: `"yellow"`, `"light_yellow"`, `"orange"`, `"light_green"`, `"cyan"`, `"light_pink"`, `"violet"`, `"red"`, `"light_blue"`, `"blue"`, `"dark_blue"`, `"black"`, `"gray"`, `"dark_gray"`, `"white"`.279- The Miro API rate limit is 100 requests per second per token. For large boards, add a small delay between calls.280- Prompt mode output quality depends on the Miro AI model version in use; review and adjust positions manually after generation.281- Board sharing policy defaults to "all editors can collaborate" — adjust the `permissionsPolicy` in `create_board()` if the board is sensitive.