[TERMINAL · SKILLS]
> mounting /skills...
> indexing skill manifests...
> linking agents: claude · codex · gemini · cursor
> ready.
[░░░░░░░░░░░░░░░░░░░░░░░░░░░░] 0%
Skills/canva
>

canva

Automate Canva from an agent — via the AI Connector MCP server (generate, find, edit, export designs in natural language) or the Connect REST API (OAuth 2.0 + PKCE, brand template autofill, asset uploads, exports, folders, comments). Use when: connecting an agent to Canva, batch-producing social posts or decks from a brand template and a spreadsheet, exporting designs to PDF/PNG/PPTX/MP4, uploading assets into a Canva account, building a Canva integration, or when the user mentions "Canva", "brand template", "autofill", "Connect API", or "mcp.canva.com".

#canva#design-automation#brand-templates#oauth#mcp
terminal-skillsv1.0.0
Works with:claude-codeopenai-codexgemini-clicursor
Source
Trust Score
100/ 100
7.50×
Impact

Validation

Quality
100/ 100
Does it follow best practices?
6 PASS
Security
Passed
No known issues
Content review + injection scan
Impact
7.50×
3% → 75% agent success
Avg across 3 eval scenarios
Scored 9/4/2026 · skill v1.0.0
agent@terminalskills — playground
full playground →

Prove canva on your task

simulated preview
$
$

real run in an isolated sandbox · files auto-deleted after 7 days · nothing touches your machine

$
✓ Installed canva v1.0.0

Getting Started

  1. Install the skill using the command above
  2. Open your AI coding agent (Claude Code, Codex, Gemini CLI, or Cursor)
  3. Reference the skill in your prompt
  4. The AI will use the skill's capabilities automatically

Example Prompts

  • "Create a responsive landing page layout following our brand guidelines"
  • "Review this component for accessibility issues and suggest fixes"

Documentation

Overview

Canva exposes two automation surfaces, and picking the wrong one wastes an afternoon:

AI Connector MCPConnect REST API
Endpointhttps://mcp.canva.com/mcphttps://api.canva.com/rest/v1/
Authclient-managed OAuth (browser once)OAuth 2.0 + PKCE you implement
Best forinteractive work in a chat/agent sessionbatch jobs, servers, CI, other people's accounts
Editingyes — editing transactionsno (create / autofill / export only)

Use the MCP connector with a human in the loop; use the Connect API for unattended runs, hundred-row fan-outs, or acting for users other than the developer. (A third server, the Dev MCP at canva.dev, only serves docs for building Canva apps — it does not touch designs.)

Instructions

Setup A — AI Connector MCP

bash
claude mcp add --transport http canva https://mcp.canva.com/mcp

Clients without HTTP transport go through the bridge: npx -y mcp-remote@latest https://mcp.canva.com/mcp. The first call opens a browser to authorize the Canva account; the client stores the token. Verify with tools/list.

Tools (rate limit per user per minute in parentheses):

GroupTools
Designsgenerate-design (20), create-design-from-candidate (20), copy-design (20), search-designs (100), get-design (100), get-design-pages (100), get-design-content (100), get-presenter-notes (100), get-export-formats (100), get-design-thumbnail (100)
Editingstart-editing-transaction (20), perform-editing-operations (50), commit-editing-transaction (20), cancel-editing-transaction (20)
Export / importexport-design (20), import-design-from-url (20), resolve-shortlink (unlimited)
Assetsupload-asset-from-url (30), get-assets (100)
Folderscreate-folder (20), list-folder-items (100), search-folders (100), move-item-to-folder (100)
Commentscomment-on-design (100), reply-to-comment (20), list-comments (100), list-replies (100)
Pro+resize-design (20), search-brand-templates (100), list-brand-kits (100), create-design-from-brand-template (20)
Enterpriseautofill-design (60), get-brand-template-dataset (100)

Editing is transactional. start-editing-transactionperform-editing-operations (repeatable) → commit-editing-transaction. Nothing lands in the design until the commit; cancel explicitly rather than dropping an open transaction.

Setup B — Connect API (OAuth 2.0 + PKCE)

Create an integration at canva.com/developers, set the redirect URI and scopes. The flow is PKCE-mandatory:

bash
# 1. verifier (43–128 chars) + S256 challenge
VERIFIER=$(openssl rand -base64 96 | tr -d '\n=+/' | cut -c1-96)
CHALLENGE=$(printf %s "$VERIFIER" | openssl dgst -binary -sha256 | basenc --base64url | tr -d '=')

# 2. send the user here (scopes space-separated, state = CSRF nonce)
https://www.canva.com/api/oauth/authorize?response_type=code&client_id=$CLIENT_ID\
&code_challenge=$CHALLENGE&code_challenge_method=S256&state=$NONCE\
&scope=design:content:write%20design:meta:read%20asset:write

# 3. exchange the code — Basic auth is base64("client_id:client_secret")
curl -X POST https://api.canva.com/rest/v1/oauth/token \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  -d grant_type=authorization_code -d code="$CODE" -d code_verifier="$VERIFIER"

Access tokens last 4 hours; refresh with grant_type=refresh_token. Each refresh token is single-use — the response returns a new one, and losing it means re-consent, so persist the rotation atomically before using the new access token.

Scopes — nothing is implied

asset:read · asset:write · design:meta:read · design:content:read · design:content:write · brandtemplate:meta:read · brandtemplate:content:read · brandtemplate:content:write · folder:read · folder:write · folder:permission:write · comment:read · comment:write · collaboration:event · profile:read · openid · profile · email

asset:write does not grant asset:read. Request each one you call.

The async job pattern — exports, autofills, uploads, imports, resizes, merges

Every heavy Connect operation is a job: the POST returns {"job": {"id", "status": "in_progress"|"success"|"failed"}}, and the result exists only once a GET on the job returns success. Never treat the POST response as the result.

OperationPOSTPoll
Export/v1/exports/v1/exports/{exportId}
Autofill/v1/autofills/v1/autofills/{jobId}
Asset upload/v1/asset-uploads/v1/asset-uploads/{jobId}
URL asset upload/v1/url-asset-uploads/v1/url-asset-uploads/{jobId}
Design import/v1/imports · /v1/url-importssame path + /{jobId}
Resize/v1/resizes/v1/resizes/{jobId}

Poll on a back-off (1s → 2s → 4s, cap ~5s). Tight loops burn the 100 req/min read budget long before the job finishes.

Endpoint map (Connect API)

  • DesignsPOST /v1/designs (design_type preset doc|email|presentation|whiteboard or custom 40–8000 px, max 25 MP area; or from design_id / brand_template_id), GET /v1/designs (query, ownership=any|owned|shared, sort_by, limit 1–100, continuation), GET /v1/designs/{id}, /pages, /export-formats, /dataset
  • AssetsPOST /v1/asset-uploads with Content-Type: application/octet-stream, the raw bytes as the body, and the name in a header: Asset-Upload-Metadata: {"name_base64":"<base64 of a ≤50-char name>"}. GET/PATCH/DELETE /v1/assets/{id}
  • AutofillPOST /v1/autofills with brand_template_id + data keyed by the template's field names
  • Brand templatesGET /v1/brand-templates, /{id}, /{id}/dataset (the dataset is the field list autofill expects — read it, never guess field names)
  • ExportsPOST /v1/exports, formats pdf, png, jpg (quality 1–100 required), gif, pptx, mp4 (quality required), csv, html_bundle, html_standalone
  • FoldersPOST /v1/folders, GET /v1/folders/{id}/items, POST /v1/folders/move
  • Comments — threads and replies on a design; Analytics — views, viewers, links
  • UserGET /v1/users/me, /me/profile, /me/capabilities (check tier here before calling a gated endpoint)

Paginate with continuation: pass the token from the previous response until it is absent.

Examples

Example 1: 240 localized social posts from one brand template

"Fill our 'Q3 Promo' brand template with every row in offers.csv and export PNGs."

bash
# 1. Read the template's real field names — never guess them
curl -H "Authorization: Bearer $TOKEN" \
  https://api.canva.com/rest/v1/brand-templates/DAFxyz123/dataset
# → { "dataset": { "headline": {"type":"text"}, "price": {"type":"text"},
#                  "hero": {"type":"image"} } }

# 2. Product shot → asset (poll the job, capture asset.id)
curl -X POST https://api.canva.com/rest/v1/asset-uploads \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Asset-Upload-Metadata: {"name_base64":"aGVyby1hdXR1bW4ucG5n"}' \
  -H "Content-Type: application/octet-stream" --data-binary @hero-autumn.png

# 3. One autofill job per row (60/min — throttle the loop)
curl -X POST https://api.canva.com/rest/v1/autofills \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"brand_template_id":"DAFxyz123","title":"Autumn promo  Berlin",
       "data":{"headline":{"type":"text","text":"30% off through October"},
               "price":{"type":"text","text":"€24.99"},
               "hero":{"type":"image","asset_id":"MSGxyz789"}}}'
# → {"job":{"id":"af_01H...","status":"in_progress"}}

# 4. Poll → success gives job.result.design.id → POST /v1/exports → poll → job.urls[]

Scopes needed: brandtemplate:meta:read, asset:write, design:content:write. Autofill is Enterprise-only — on a lower tier this returns permission_denied, and the fallback is create-design-from-brand-template (Pro) followed by manual editing.

Example 2: Same job, one prompt, over MCP

"Find our Q3 Promo brand template, make a version for the Berlin offer with this product shot, and export it as a PNG."

search-brand-templates("Q3 Promo")        → template id
get-brand-template-dataset(id)            → headline / price / hero  (Enterprise)
upload-asset-from-url("https://cdn.acme.dev/hero-autumn.png")  → asset id
autofill-design(id, { headline, price, hero })                 → design id + edit_url
export-design(design_id, format: "png")   → download URL (valid 24 h)

Give the user the edit_url — it lasts 30 days and opens in Canva, while the export URL dies in 24 hours. If autofill-design is missing from tools/list, the account is not Enterprise: say so instead of retrying.

Guidelines

  • Every URL expires, at a different rate. Thumbnails ~15 min, export downloads 24 h, edit_url / view_url 30 days. Download export bytes at once; hand humans the edit_url, never a signed export link.
  • Read the dataset before autofilling. GET /v1/brand-templates/{id}/dataset (or get-brand-template-dataset) returns the exact field names and types. A guessed key is silently ignored, producing a design with empty placeholders rather than an error.
  • Tier gates are hard. Resize and brand templates need Pro; autofill and brand kits need Enterprise. Check GET /v1/users/me/capabilities before a batch and read permission_denied as "wrong plan", not "wrong request".
  • Limits are per user, per minute: creates and exports 20, autofill 60, reads 100. A 240-row batch is a throttled queue, not a Promise.all. Back off on 429.
  • Jobs are not results. A 200 on POST /v1/exports means accepted. Poll until success, and handle failed by reading job.error.code rather than re-submitting.
  • Blank designs vanish. A design created via the API and never edited is deleted after 7 days. Autofill or edit it in the same run if it needs to persist.
  • Refresh tokens are single-use. Store the rotated token before the next call; a lost rotation means the user must re-consent through the full PKCE flow.
  • PKCE is mandatory; the code_verifier and client_secret stay server-side. A Canva integration cannot be driven safely from client-side code.
  • Design ids (DAF…), asset ids (MSG…) and job ids are scoped to the authorized account; a 404 on a valid-looking id usually means it belongs to a different team.