Skip to main content
miro-deploy-integration Deploy Miro REST API v2 integrations to Vercel, Fly.io, and Cloud Run
with proper OAuth token management and webhook configuration.
Trigger with phrases like "deploy miro", "miro Vercel",
"miro production deploy", "miro Cloud Run", "miro Fly.io".
跳到安装 Skills Marketplace 发现并探索由社区构建的 Agent Skills
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/jeremylongshore/claude-code-plugins-plus-skills --skill miro-deploy-integration命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
下载 Zip 下载中... jeremylongshore
jeremylongshore/claude-code-plugins-plus-skills
打开 GitHub 仓库 name miro-deploy-integration description Deploy Miro REST API v2 integrations to Vercel, Fly.io, and Cloud Run
with proper OAuth token management and webhook configuration.
Trigger with phrases like "deploy miro", "miro Vercel",
"miro production deploy", "miro Cloud Run", "miro Fly.io".
allowed-tools Read, Write, Edit, Bash(vercel:*), Bash(fly:*), Bash(gcloud:*) version 1.6.0 license MIT author Jeremy Longshore <jeremy@intentsolutions.io> tags ["saas","miro","deployment","cloud"] compatibility Designed for Claude Code
Miro Deploy Integration
Overview
Deploy Miro REST API v2 integrations to popular platforms with proper OAuth 2.0 token management, webhook endpoint setup, and health monitoring.
Prerequisites
Miro app configured with production OAuth credentials
Access token with required scopes
Platform CLI installed (vercel, fly, or gcloud)
Vercel Deployment
Environment Variables
vercel env add MIRO_CLIENT_ID production
vercel env add MIRO_CLIENT_SECRET production
vercel env add MIRO_ACCESS_TOKEN production
vercel env add MIRO_WEBHOOK_SECRET production
API Route: Webhook Handler
import crypto from 'crypto' ;
export const config = { api : { bodyParser : false } };
export default async function handler (req, res ) {
if (req.method !== 'POST' ) return res.status (405 ).end ();
const chunks : Buffer [] = [];
for await (const chunk of req) chunks.push (chunk);
const rawBody = Buffer .concat (chunks);
signature = req. [ ] ;
expected = crypto. ( , process. . !)
. (rawBody). ( );
(!signature || !crypto. ( . (signature), . (expected))) {
res. ( ). ({ : });
}
event = . (rawBody. ());
(event. ) {
:
. ( );
;
}
res. ( ). ({ : });
}
const
headers
'x-miro-signature'
as
string
const
createHmac
'sha256'
env
MIRO_WEBHOOK_SECRET
update
digest
'hex'
if
timingSafeEqual
Buffer
from
Buffer
from
return
status
401
json
error
'Invalid signature'
const
JSON
parse
toString
switch
event
case
'board_subscription_changed'
console
log
`Board ${event.boardId} : item ${event.item?.type } ${event.type } `
break
status
200
json
received
true
API Route: OAuth Callback
export default async function handler (req, res ) {
const { code } = req.query ;
const tokenResponse = await fetch ('https://api.miro.com/v1/oauth/token' , {
method : 'POST' ,
headers : { 'Content-Type' : 'application/x-www-form-urlencoded' },
body : new URLSearchParams ({
grant_type : 'authorization_code' ,
client_id : process.env .MIRO_CLIENT_ID !,
client_secret : process.env .MIRO_CLIENT_SECRET !,
code : code as string ,
redirect_uri : `${process.env.VERCEL_URL} /api/auth/miro/callback` ,
}),
});
const tokens = await tokenResponse.json ();
res.redirect ('/dashboard?connected=miro' );
}
vercel.json {
"functions" : {
"api/webhooks/miro.ts" : { "maxDuration" : 10 } ,
"api/auth/miro/callback.ts" : { "maxDuration" : 10 }
} ,
"headers" : [
{
"source" : "/api/health" ,
"headers" : [ { "key" : "Cache-Control" , "value" : "no-store" } ]
}
]
}
Fly.io Deployment
fly.toml app = "my-miro-integration"
primary_region = "iad"
[env]
NODE_ENV = "production"
MIRO_API_BASE = "https://api.miro.com/v2"
[http_service]
internal_port = 3000
force_https = true
auto_stop_machines = "suspend"
auto_start_machines = true
min_machines_running = 1
[[http_service.checks]]
grace_period = "10s"
interval = "30s"
method = "GET"
path = "/health"
timeout = "5s"
Deploy
fly secrets set MIRO_CLIENT_ID=your_client_id
fly secrets set MIRO_CLIENT_SECRET=your_client_secret
fly secrets set MIRO_ACCESS_TOKEN=your_token
fly secrets set MIRO_WEBHOOK_SECRET=your_webhook_secret
fly deploy
fly ssh console -C "curl -s http://localhost:3000/health | jq '.miro'"
Google Cloud Run
Deploy Script #!/bin/bash
set -euo pipefail
PROJECT_ID="${GOOGLE_CLOUD_PROJECT} "
SERVICE_NAME="miro-integration"
REGION="us-central1"
echo -n "$MIRO_CLIENT_SECRET " | gcloud secrets create miro-client-secret --data-file=-
echo -n "$MIRO_ACCESS_TOKEN " | gcloud secrets create miro-access-token --data-file=-
echo -n "$MIRO_WEBHOOK_SECRET " | gcloud secrets create miro-webhook-secret --data-file=-
gcloud run deploy $SERVICE_NAME \
--source . \
--region $REGION \
--platform managed \
--allow-unauthenticated \
--min-instances 1 \
--set-env-vars "MIRO_CLIENT_ID=$MIRO_CLIENT_ID ,MIRO_API_BASE=https://api.miro.com/v2" \
--set-secrets "MIRO_CLIENT_SECRET=miro-client-secret:latest,MIRO_ACCESS_TOKEN=miro-access-token:latest,MIRO_WEBHOOK_SECRET=miro-webhook-secret:latest"
Health Check Endpoint
export async function healthCheck ( ): Promise <HealthResponse > {
const checks : Record <string , unknown > = {};
const start = Date .now ();
try {
const response = await fetch ('https://api.miro.com/v2/boards?limit=1' , {
headers : { 'Authorization' : `Bearer ${process.env.MIRO_ACCESS_TOKEN} ` },
signal : AbortSignal .timeout (5000 ),
});
checks.miro = {
status : response.ok ? 'healthy' : 'degraded' ,
latencyMs : Date .now () - start,
rateLimitRemaining : response.headers .get ('X-RateLimit-Remaining' ),
httpStatus : response.status ,
};
} catch (err) {
checks.miro = { status : 'unhealthy' , error : err.message };
}
return {
status : Object .values (checks).every ((c : any ) => c.status === 'healthy' ) ? 'healthy' : 'degraded' ,
services : checks,
timestamp : new Date ().toISOString (),
};
}
Webhook URL Registration via API After deploying, register your webhook endpoint programmatically:
const subscription = await fetch (
'https://api.miro.com/v2-experimental/webhooks/board_subscriptions' ,
{
method : 'POST' ,
headers : {
'Authorization' : `Bearer ${process.env.MIRO_ACCESS_TOKEN} ` ,
'Content-Type' : 'application/json' ,
},
body : JSON .stringify ({
boardId : 'your-board-id' ,
callbackUrl : 'https://your-app.com/api/webhooks/miro' ,
status : 'enabled' ,
}),
}
);
Error Handling Issue Cause Solution Webhook delivery fails URL not HTTPS Ensure force_https is enabled Token expires in production No refresh logic Implement scheduled token refresh Cold start misses webhook Min instances = 0 Set min_machines_running = 1 Secret rotation breaks deploy Old secret cached Restart service after secret update
Resources
Next Steps For webhook handling patterns, see miro-webhooks-events.