Canva Core Workflow A — Design Creation & Export
Overview
The primary Canva integration workflow: create designs via the REST API, let users edit them in Canva's editor, then export finished designs as PDF/PNG/JPG for downstream use (email campaigns, social posts, print orders).
Prerequisites
- Completed
canva-install-auth setup with valid access token
- Scopes:
design:content:write, design:content:read, design:meta:read
Instructions
Step 1: Create a Design
interface CreateDesignRequest {
design_type:
| { type: 'preset'; name: 'doc' | 'whiteboard' | 'presentation' }
| { type: 'custom'; width: number; height: number };
title?: string;
asset_id?: string;
}
const { design } = await canvaAPI('/designs', token, {
method: 'POST',
body: JSON.stringify({
design_type: { type: 'custom', width: 1080, height: 1080 },
title: 'Instagram Post — Q1 Campaign',
}),
});
Note: Blank designs are auto-deleted after 7 days if never edited.
Step 2: Redirect User to Edit
res.redirect(design.urls.edit_url);
Step 3: Get Design Metadata
const { design: meta } = await canvaAPI(`/designs/${designId}`, token);
console.log(`Title: ${meta.title}`);
console.log(`Pages: ${meta.page_count}`);
console.log(`Created: ${new Date(meta.created_at * 1000).toISOString()}`);
console.log(`Updated: ${new Date(meta.updated_at * 1000).toISOString()}`);
console.log(`Owner: user=${meta.owner.user_id}, team=${meta.owner.team_id}`);
Step 4: Export the Finished Design
const { job } = await canvaAPI('/exports', token, {
method: 'POST',
body: JSON.stringify({
design_id: designId,
format: {
type: 'pdf',
size: 'a4',
export_quality: 'pro',
},
}),
});
const { job: pngJob } = await canvaAPI('/exports', token, {
method: 'POST',
body: JSON.stringify({
design_id: designId,
format: {
type: 'png',
width: 1200,
transparent_background: true,
lossless: true,
as_single_image: ,
},
}),
});
{ : jpgJob } = (, token, {
: ,
: .({
: designId,
: {
: ,
: ,
: [, ],
},
}),
});
Step 5: Poll for Export Completion
async function waitForExport(
exportId: string,
token: string,
maxWaitMs = 60000
): Promise<string[]> {
const start = Date.now();
while (Date.now() - start < maxWaitMs) {
const { job } = await canvaAPI(`/exports/${exportId}`, token);
if (job.status === 'success') {
return job.urls;
}
if (job.status === 'failed') {
throw new Error(`Export failed: ${job.error.code} — ${job.error.message}`);
}
await new Promise(r => setTimeout(r, 2000));
}
throw new Error('Export timed out');
}
downloadUrls = (job., token);
Supported Export Formats
| Format | Type | Key Options |
|---|
| PDF | pdf | size, export_quality, pages |
| PNG | png | width, height, transparent_background, lossless, as_single_image |
| JPG | jpg | quality (1-100), width, height |
| PPTX | pptx | pages |
| GIF | gif | width, height, export_quality |
| MP4 | mp4 | quality (horizontal_480p, 720p, 1080p, 4k) |
Error Handling
| Error | Cause | Solution |
|---|
| 400 Bad Request | Invalid dimensions or format | Check min/max values |
| 401 Unauthorized | Token expired | Refresh via OAuth |
| 403 Forbidden | Missing scope | Enable design:content:write |
| 404 Not Found | Design deleted or not owned | Verify design ID |
| 429 Rate Limited | Too many exports | Respect Retry-After header |
license_required | Design uses premium elements | User needs Canva Pro |
Resources
Next Steps
For asset management and brand template autofill, see canva-core-workflow-b.