Claude-code-plugins-plus-skills canva-prod-checklist
install
source · Clone the upstream repo
git clone https://github.com/jeremylongshore/claude-code-plugins-plus-skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/jeremylongshore/claude-code-plugins-plus-skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/plugins/saas-packs/canva-pack/skills/canva-prod-checklist" ~/.claude/skills/jeremylongshore-claude-code-plugins-plus-skills-canva-prod-checklist && rm -rf "$T"
manifest:
plugins/saas-packs/canva-pack/skills/canva-prod-checklist/SKILL.mdsource content
Canva Production Checklist
Overview
Complete checklist for deploying Canva Connect API integrations to production, covering OAuth configuration, security, error handling, monitoring, and Canva's integration review process.
Pre-Deployment
OAuth & Security
- Client ID and secret stored in secret manager (not env files)
- Redirect URIs use HTTPS and match production domains
- Only required OAuth scopes requested (least privilege)
- Access tokens stored encrypted at rest
- Refresh token rotation handled (single-use tokens)
- Token revocation implemented for user disconnect
- No client secrets in frontend code
API Integration
- All API calls use
endpointsapi.canva.com/rest/v1/* - Rate limits respected with exponential backoff (see
)canva-rate-limits - Export polling implemented with timeout (don't poll forever)
- 429 responses handled with
headerRetry-After - 401 responses trigger automatic token refresh
- Error responses parsed and logged (without tokens)
- Blank designs auto-delete warning handled (7-day window)
- Export download URLs consumed within 24-hour window
Webhook Security
- Webhook endpoint uses HTTPS
- JWK signature verification implemented (see
)canva-webhooks-events - Webhook handler returns 200 immediately
- Heavy processing done asynchronously
- Idempotency keys prevent duplicate processing
Data Handling
- No access tokens in log output
- User design metadata treated as sensitive
- Temporary URLs (thumbnails, exports) not cached beyond expiry
- Thumbnail URLs expire in 15 minutes — refresh as needed
- Edit/view URLs expire in 30 days — regenerate via API
Production Readiness Verification
#!/bin/bash # canva-prod-verify.sh echo "=== Canva Production Readiness ===" # 1. Verify API connectivity from production HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \ -H "Authorization: Bearer $CANVA_ACCESS_TOKEN" \ "https://api.canva.com/rest/v1/users/me") echo "[$([ $HTTP_CODE = 200 ] && echo 'PASS' || echo 'FAIL')] API connectivity: HTTP $HTTP_CODE" # 2. Test design creation DESIGN=$(curl -s -X POST "https://api.canva.com/rest/v1/designs" \ -H "Authorization: Bearer $CANVA_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{"design_type":{"type":"custom","width":100,"height":100},"title":"Prod Test"}') DESIGN_ID=$(echo "$DESIGN" | python3 -c "import sys,json; print(json.load(sys.stdin)['design']['id'])" 2>/dev/null) echo "[$([ -n "$DESIGN_ID" ] && echo 'PASS' || echo 'FAIL')] Design creation: $DESIGN_ID" # 3. Test export if [ -n "$DESIGN_ID" ]; then EXPORT=$(curl -s -X POST "https://api.canva.com/rest/v1/exports" \ -H "Authorization: Bearer $CANVA_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d "{\"design_id\":\"$DESIGN_ID\",\"format\":{\"type\":\"png\"}}") EXPORT_ID=$(echo "$EXPORT" | python3 -c "import sys,json; print(json.load(sys.stdin)['job']['id'])" 2>/dev/null) echo "[$([ -n "$EXPORT_ID" ] && echo 'PASS' || echo 'FAIL')] Export job: $EXPORT_ID" fi echo "" echo "=== Done ==="
Canva Integration Review
For public integrations (available to all Canva users), you must pass Canva's review:
- Submit your integration for review in the Canva developer portal
- Canva reviews security, OAuth implementation, and UX
- Preview features (e.g., webhooks) are not allowed in public integrations
- Fix any issues and resubmit
Private integrations (your organization only) do not require review.
Health Check Endpoint
app.get('/health', async (req, res) => { const start = Date.now(); let canvaStatus = 'unknown'; try { const me = await fetch('https://api.canva.com/rest/v1/users/me', { headers: { 'Authorization': `Bearer ${getServiceToken()}` }, signal: AbortSignal.timeout(5000), }); canvaStatus = me.ok ? 'healthy' : `error:${me.status}`; } catch { canvaStatus = 'unreachable'; } res.json({ status: canvaStatus === 'healthy' ? 'healthy' : 'degraded', services: { canva: { status: canvaStatus, latencyMs: Date.now() - start } }, timestamp: new Date().toISOString(), }); });
Monitoring Alerts
| Alert | Condition | Severity |
|---|---|---|
| Auth failures | 401 errors > 0 | P1 |
| Rate limited | 429 errors > 5/min | P2 |
| Export failures | or | P3 |
| API unreachable | Connection timeout | P1 |
| Token refresh fails | Refresh returns error | P1 |
Error Handling
| Issue | Cause | Solution |
|---|---|---|
| Token refresh loop | Revoked refresh token | Re-authorize user |
Export stuck | Backend delay | Timeout after 120s, retry |
| Webhook URL rejected | HTTP not HTTPS | Use HTTPS endpoint |
| Review rejection | Using preview features | Remove preview-only features |
Resources
Next Steps
For version upgrades, see
canva-upgrade-migration.