Canva Integration Guide
██████╗ █████╗ ███╗ ██╗██╗ ██╗ █████╗
██╔════╝██╔══██╗████╗ ██║██║ ██║██╔══██╗
██║ ███████║██╔██╗ ██║██║ ██║███████║
██║ ██╔══██║██║╚██╗██║╚██╗ ██╔╝██╔══██║
╚██████╗██║ ██║██║ ╚████║ ╚████╔╝ ██║ ██║
╚═════╝╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═══╝ ╚═╝ ╚═╝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
| Resource | URL |
|---|---|
| Connect API docs | https://www.canva.dev/docs/connect/ |
| API reference | https://www.canva.dev/docs/connect/api-reference/ |
| Authentication (OAuth) | https://www.canva.dev/docs/connect/authentication/ |
| Apps SDK | https://www.canva.dev/docs/apps/ |
| Developer portal | https://www.canva.dev/ |
1. Authentication (OAuth 2.0)
- Create an integration in the Developer portal and note the Client ID / Client Secret.
- 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). - 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
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:
# 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.
# 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
datakey must exactly match a named field in that template — unmatched keys are ignored and the template's defaults remain. Image fields take anasset_id(upload first via the asset endpoints), not a URL.
4. Upload an asset
# 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/autofillsto 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.