'Configure Canva Connect API across development, staging, and production
Scanned 9/2/2026
Install to Claude Code
npx -y skills add jeremylongshore/tons-of-skills-marketplace --skill canva-multi-env-setup --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Canva Multi Env Setup?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/jeremylongshore-canva-multi-env-setup-tons-of-skills-marketplace)More formats (shields.io, HTML) on the badges page.
---
name: canva-multi-env-setup
description: 'Configure Canva Connect API across development, staging, and production
environments.
Use when setting up multi-environment deployments, managing OAuth credentials per
environment,
or implementing environment-specific Canva configurations.
Trigger with phrases like "canva environments", "canva staging",
"canva dev prod", "canva environment setup", "canva config by env".
'
allowed-tools: Read, Write, Edit, Bash(aws:*), Bash(gcloud:*), Bash(vault:*)
version: 1.5.0
license: MIT
author: Jeremy Longshore <jeremy@intentsolutions.io>
tags:
- saas
- design
- canva
compatibility: Designed for Claude Code
---
# Canva Multi-Environment Setup
## Overview
Configure Canva Connect API integrations across development, staging, and production. Each environment needs separate OAuth integrations registered in the Canva developer portal with distinct redirect URIs.
## Prerequisites
- Separate reviewed OAuth clients, redirect URIs, secret stores, tenant/asset scopes, and owners for each environment.
- Synthetic development/staging assets and a deployment process that cannot silently select production configuration.
## Instructions
1. Resolve the environment from an explicit allowlist and fail if its OAuth client, redirect URI, or scope configuration is absent or mismatched.
2. Keep tokens, caches, queues, and webhook endpoints isolated by environment; never share production credentials with lower environments.
3. Verify staging with synthetic assets, then promote immutable configuration through the reviewed release path.
## Environment Strategy
| Environment | Canva Integration | Redirect URI | Data |
|-------------|------------------|--------------|------|
| Development | `my-app-dev` | `http://localhost:3000/auth/canva/callback` | Test account |
| Staging | `my-app-staging` | `https://staging.myapp.com/auth/canva/callback` | Staging account |
| Production | `my-app-prod` | `https://myapp.com/auth/canva/callback` | Real users |
**Important:** Register a separate Canva integration per environment. Each gets its own client ID and secret.
## Configuration
```typescript
// src/config/canva.ts
interface CanvaEnvConfig {
clientId: string;
clientSecret: string;
redirectUri: string;
baseUrl: string; // Always api.canva.com — Canva has no sandbox API
scopes: string[];
debug: boolean;
}
const configs: Record<string, CanvaEnvConfig> = {
development: {
clientId: process.env.CANVA_CLIENT_ID!,
clientSecret: process.env.CANVA_CLIENT_SECRET!,
redirectUri: 'http://localhost:3000/auth/canva/callback',
baseUrl: 'https://api.canva.com/rest/v1', // No sandbox exists
scopes: ['design:content:write', 'design:content:read', 'design:meta:read', 'asset:write', 'asset:read'],
debug: true,
},
staging: {
clientId: process.env.CANVA_CLIENT_ID!,
clientSecret: process.env.CANVA_CLIENT_SECRET!,
redirectUri: process.env.CANVA_REDIRECT_URI!,
baseUrl: 'https://api.canva.com/rest/v1',
scopes: ['design:content:write', 'design:content:read', 'design:meta:read', 'asset:write', 'asset:read'],
debug: false,
},
production: {
clientId: process.env.CANVA_CLIENT_ID!,
clientSecret: process.env.CANVA_CLIENT_SECRET!,
redirectUri: process.env.CANVA_REDIRECT_URI!,
baseUrl: 'https://api.canva.com/rest/v1',
scopes: ['design:content:write', 'design:content:read', 'design:meta:read'],
debug: false,
},
};
export function getCanvaConfig(): CanvaEnvConfig {
const env = process.env.NODE_ENV || 'development';
return configs[env] || configs.development;
}
```
## Secret Management
### Local Development
```bash
# .env.local (git-ignored)
CANVA_CLIENT_ID=OCA_dev_xxxxxxxx
CANVA_CLIENT_SECRET=dev_xxxxxxxx
```
### GitHub Actions / CI
```bash
# Per-environment secrets
gh secret set CANVA_CLIENT_ID --env staging --body "OCA_staging_xxx"
gh secret set CANVA_CLIENT_SECRET --env staging --body "staging_xxx"
gh secret set CANVA_CLIENT_ID --env production --body "OCA_prod_xxx"
gh secret set CANVA_CLIENT_SECRET --env production --body "prod_xxx"
```
### Production — Cloud Secret Managers
```bash
# GCP Secret Manager
gcloud secrets create canva-client-id-prod --data-file=-
gcloud secrets create canva-client-secret-prod --data-file=-
# AWS Secrets Manager
aws secretsmanager create-secret \
--name canva/production/client-id \
--secret-string "OCA_prod_xxx"
# HashiCorp Vault
vault kv put secret/canva/production \
client_id="OCA_prod_xxx" \
client_secret="prod_xxx"
```
## Environment Isolation Guards
```typescript
// Prevent accidental cross-environment operations
function assertEnvironment(expected: string): void {
const actual = process.env.NODE_ENV || 'development';
if (actual !== expected) {
throw new Error(`Expected ${expected} environment, got ${actual}`);
}
}
// Guard destructive operations
async function deleteAllUserDesigns(userId: string, token: string) {
assertEnvironment('development'); // Block in staging/production
// ...
}
```
## Token Storage per Environment
```typescript
// Development: file-based for convenience
// Staging/Production: encrypted database
function getTokenStore(): TokenStore {
const env = process.env.NODE_ENV || 'development';
if (env === 'development') {
return new FileTokenStore('.canva-tokens.json'); // git-ignored
}
return new DatabaseTokenStore({
connectionString: process.env.DATABASE_URL!,
encryptionKey: process.env.TOKEN_ENCRYPTION_KEY!,
});
}
```
## Canva-Specific Considerations
1. **No sandbox API** — Canva has no separate sandbox environment. All environments hit `api.canva.com/rest/v1`. Use separate Canva accounts for dev/staging.
2. **Separate integrations** — Each environment should be a distinct integration in the Canva developer portal to avoid redirect URI conflicts.
3. **Scope differences** — Use broader scopes in dev for testing, minimal scopes in production.
4. **Token isolation** — Never share tokens across environments. Refresh tokens are single-use.
## Output
Environment setup yields an approved environment label, configuration/version receipt, OAuth/redirect validation, and scope-isolation result. It excludes client secrets, tokens, tenant identifiers, and design data.
## Examples
For a staging release, load the dedicated staging client and redirect URI from protected configuration, verify a synthetic design callback, and record the redacted result. If the environment resolves to production or shares a credential, fail the deployment rather than falling back.
## Error Handling
| Issue | Cause | Solution |
|-------|-------|----------|
| Wrong redirect URI | Environment mismatch | Use per-environment integration |
| Missing secret | Not deployed to env | Add via secret manager |
| Token cross-contamination | Shared token store | Isolate by environment prefix |
| Production guard triggered | Wrong NODE_ENV | Set correct environment variable |
## Resources
- [Canva Creating Integrations](https://www.canva.dev/docs/connect/creating-integrations/)
- [Canva Authentication](https://www.canva.dev/docs/connect/authentication/)
- [12-Factor App Config](https://12factor.net/config)
## Next Steps
For observability setup, see `canva-observability`.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!