Agent Skills: Canva Core Workflow A — Design Creation & Export

|

UncategorizedID: jeremylongshore/claude-code-plugins-plus-skills/canva-core-workflow-a

Install this agent skill to your local

pnpm dlx add-skill https://github.com/jeremylongshore/claude-code-plugins-plus-skills/tree/HEAD/plugins/saas-packs/canva-pack/skills/canva-core-workflow-a

Skill Files

Browse the full folder contents for canva-core-workflow-a.

Download Skill

Loading file tree…

plugins/saas-packs/canva-pack/skills/canva-core-workflow-a/SKILL.md

Skill Metadata

Name
canva-core-workflow-a
Description
|

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

// POST https://api.canva.com/rest/v1/designs
// Rate limit: 20 req/min per user
// Scope: design:content:write

interface CreateDesignRequest {
  design_type:
    | { type: 'preset'; name: 'doc' | 'whiteboard' | 'presentation' }
    | { type: 'custom'; width: number; height: number }; // 40-8000 px
  title?: string;     // 1-255 characters
  asset_id?: string;  // Image asset to insert
}

// Create a social media post (custom dimensions)
const { design } = await canvaAPI('/designs', token, {
  method: 'POST',
  body: JSON.stringify({
    design_type: { type: 'custom', width: 1080, height: 1080 },
    title: 'Instagram Post — Q1 Campaign',
  }),
});

// design.id — unique identifier for all future operations
// design.urls.edit_url — redirect user here to edit (expires 30 days)
// design.urls.view_url — read-only link (expires 30 days)
// design.thumbnail.url — preview image (expires 15 minutes)

Note: Blank designs are auto-deleted after 7 days if never edited.

Step 2: Redirect User to Edit

// Redirect the user to Canva's editor
// The edit_url is user-specific and expires after 30 days
res.redirect(design.urls.edit_url);

Step 3: Get Design Metadata

// GET https://api.canva.com/rest/v1/designs/{designId}
// Rate limit: 100 req/min per user
// Scope: design:meta:read

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

// POST https://api.canva.com/rest/v1/exports
// Rate limits:
//   Per user: 75 exports/5min, 500/24hr
//   Per integration: 750 exports/5min, 5000/24hr
//   Per document: 75 exports/5min
// Scope: design:content:read

// Export as high-quality PDF
const { job } = await canvaAPI('/exports', token, {
  method: 'POST',
  body: JSON.stringify({
    design_id: designId,
    format: {
      type: 'pdf',
      size: 'a4',          // a4 | a3 | letter | legal (Docs only)
      export_quality: 'pro', // regular | pro
    },
  }),
});

// Export as PNG with transparent background
const { job: pngJob } = await canvaAPI('/exports', token, {
  method: 'POST',
  body: JSON.stringify({
    design_id: designId,
    format: {
      type: 'png',
      width: 1200,                // 40-25000 px
      transparent_background: true,
      lossless: true,
      as_single_image: false,     // true = merge all pages into one image
    },
  }),
});

// Export specific pages as JPG
const { job: jpgJob } = await canvaAPI('/exports', token, {
  method: 'POST',
  body: JSON.stringify({
    design_id: designId,
    format: {
      type: 'jpg',
      quality: 85,               // 1-100
      pages: [1, 2],             // specific page numbers
    },
  }),
});

Step 5: Poll for Export Completion

// GET https://api.canva.com/rest/v1/exports/{exportId}
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; // Array of download URLs, valid 24 hours
    }

    if (job.status === 'failed') {
      // Error codes: license_required | approval_required | internal_failure
      throw new Error(`Export failed: ${job.error.code} — ${job.error.message}`);
    }

    await new Promise(r => setTimeout(r, 2000)); // Poll every 2 seconds
  }

  throw new Error('Export timed out');
}

const downloadUrls = await waitForExport(job.id, 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.