← Back to dashboard
canvadesignfresh

Canva Integration Guide

Text
 ██████╗ █████╗ ███╗   ██╗██╗   ██╗ █████╗
██╔════╝██╔══██╗████╗  ██║██║   ██║██╔══██╗
██║     ███████║██╔██╗ ██║██║   ██║███████║
██║     ██╔══██║██║╚██╗██║╚██╗ ██╔╝██╔══██║
╚██████╗██║  ██║██║ ╚████║ ╚████╔╝ ██║  ██║
 ╚═════╝╚═╝  ╚═╝╚═╝  ╚═══╝  ╚═══╝  ╚═╝  ╚═╝

Canva Integration Guide

Focus: Programmatic design with the Canva Connect API (REST + OAuth) — create designs, autofill brand templates, upload assets, and export to PNG/PDF/JPG. A Canva MCP connector is also available inside Claude Code for design operations.

Overview

Canva exposes two developer surfaces:

  • Connect API — a REST API (https://api.canva.com/rest/v1/) authenticated with OAuth 2.0. Use it from a server to create/export designs, manage assets, and autofill Brand Templates. This is the codeAmani integration path.
  • Apps SDK — browser-based apps that run inside the Canva editor (@canva/app-ui-kit, @canva/design). Use only if building an in-editor Canva app.

No server SDK is required — the Connect API is plain REST/OAuth (an optional generated TypeScript client exists). In Claude Code, the Canva MCP also offers search-designs, create-design, export-design, upload-asset-from-url, etc.

Here is the big picture — once you see how the pieces connect, the rest is easy:

Official Documentation


1. Authentication (OAuth 2.0)

  1. Create an integration in the Developer portal and note the Client ID / Client Secret.
  2. Run the OAuth Authorization Code (+ PKCE) flow to get a user access token with the scopes you need (e.g. design:content:write, asset:write, design:content:read).
  3. Call the API with Authorization: Bearer <token>. Refresh tokens before expiry.

Store the client secret + tokens server-side only (codeAmani: .env.local / Vercel env).

2. Create a design

Bash
curl -X POST 'https://api.canva.com/rest/v1/designs' \
  -H "Authorization: Bearer $CANVA_TOKEN" -H "Content-Type: application/json" \
  -d '{"type":"type_and_asset","design_type":{"type":"preset","name":"doc"},
       "asset_id":"Msd59349ff","title":"My Holiday Presentation"}'

3. Export a design (async job)

Exports are asynchronous — kick off the job, then poll until it is ready. This little dance is quick to wire up:

Bash
# Start the export job
curl -X POST 'https://api.canva.com/rest/v1/exports' \
  -H "Authorization: Bearer $CANVA_TOKEN" -H "Content-Type: application/json" \
  -d '{"design_id":"DAVZr1z5464","format":{"type":"pdf"}}'
# -> { "job": { "id": "...", "status": "in_progress" } }

Poll GET /rest/v1/exports/{jobId} until status is success; the response urls[] are download links that expire after 24h (failures return an error.code such as license_required).

Brand Template autofill

This is the highest-leverage feature: design a Brand Template once in Canva, then POST /v1/autofills with a data object to mass-produce on-brand graphics from your data. The data keys must match the named fields inside the template, and each value declares a type (text with text, image with an asset_id, or chart with chart_data). Like exports, autofill is an async job — kick it off, then poll until status is success and read the produced design from job.result.design.

Bash
# Start the autofill job — keys (price, hero) must match the template's named fields
curl -X POST 'https://api.canva.com/rest/v1/autofills' \
  -H "Authorization: Bearer $CANVA_TOKEN" -H "Content-Type: application/json" \
  -d '{"brand_template_id":"DAFVztcvd9z","title":"M-Pesa promo - June",
       "data":{
         "price":{"type":"text","text":"KES 499"},
         "hero":{"type":"image","asset_id":"Msd59349ff"}
       }}'
# -> { "job": { "id": "...", "status": "in_progress" } }

Poll GET /rest/v1/autofills/{jobId} until status is success; the new design is at job.result.design (id, plus urls.edit_url / urls.view_url, valid 30 days). Requires the design:content:write scope.

Gotcha: the target design must be a Brand Template (a plain design cannot be autofilled), and every data key must exactly match a named field in that template — unmatched keys are ignored and the template's defaults remain. Image fields take an asset_id (upload first via the asset endpoints), not a URL.

4. Upload an asset

Bash
# Binary upload
curl -X POST 'https://api.canva.com/rest/v1/asset-uploads' \
  -H "Authorization: Bearer $CANVA_TOKEN" -H "Content-Type: application/octet-stream" \
  -H 'Asset-Upload-Metadata: {"name_base64":"TXkgVXBsb2Fk"}' \
  --data-binary '@/path/to/file'

# Or from a public URL (30 req/min/user)
curl -X POST 'https://api.canva.com/rest/v1/url-asset-uploads' \
  -H "Authorization: Bearer $CANVA_TOKEN" -H "Content-Type: application/json" \
  -d '{"name":"my_asset","url":"https://example.com/image.png"}'

codeAmani notes

  • Brand Templates + Autofill are the highest-leverage feature: design once in Canva, then POST /v1/autofills to mass-produce on-brand graphics from data.
  • Security: OAuth client secret and tokens are server-side only; never expose to the browser. Verify webhook signatures if you subscribe to Canva webhooks.
  • Design pairing: marketing assets here can feed the same R2/Vercel pipeline used for the dashboard thumbnails.