From canva-pack
Configures Canva Connect API across dev, staging, prod with per-environment OAuth credentials, redirect URIs, and TypeScript config loader. For multi-env Canva deployments.
How this skill is triggered — by the user, by Claude, or both
Slash command
/canva-pack:canva-multi-env-setupThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
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.
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.
| 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.
// 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;
}
# .env.local (git-ignored)
CANVA_CLIENT_ID=OCA_dev_xxxxxxxx
CANVA_CLIENT_SECRET=dev_xxxxxxxx
# 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"
# 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"
// 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
// ...
}
// 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!,
});
}
api.canva.com/rest/v1. Use separate Canva accounts for dev/staging.| 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 |
For observability setup, see canva-observability.
npx claudepluginhub jeremylongshore/claude-code-plugins-plus-skills --plugin canva-packDeploys Canva Connect API integrations to Vercel, Fly.io, and Cloud Run. Manages OAuth secrets, configures platform files, and sets up server-side token exchange.
Sets up Figma API with per-environment tokens, file keys, cache TTLs, and configs to isolate dev, staging, and prod resources.
Configures Miro REST API v2 for dev, staging, prod with separate OAuth apps, isolated test boards, TypeScript configs, and secret management.