[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6F6DBT68K8MAZRZCZAF37Y0":3,"skill-same:01M6F6DBT68K8MAZRZCZAF37Y0":77},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":18,"publishedAt":45,"linkOnly":46,"markdownContent":47,"markdownBody":48,"frontMatter":49,"refName":54,"commitSha":55,"skillDirectory":56,"forksCount":57,"repositoryHtmlUrl":58,"repositoryHomepageUrl":6,"ownerHtmlUrl":59,"ownerBlogUrl":60,"ownerDescription":61,"updatedAt":62,"install":63,"resources":76},"01M6F6DBT68K8MAZRZCZAF37Y0","documenso-multi-env-setup",null,"Configure Documenso across multiple environments (dev, staging, production).\nUse when setting up environment-specific configurations, managing API keys,\nor implementing environment promotion workflows.\nTrigger with phrases like \"documenso environments\", \"documenso staging\",\n\"documenso dev setup\", \"multi-environment documenso\".","Dicklesworthstone\u002Fpi_agent_rust","Dicklesworthstone","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F35050222?v=4","01M6CG26AR942CSVMN1M5E3DSE","Jeff Emanuel","01M6CG26EPP71PSHW1VDS71121",1986,"MIT","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Fplugins-community\u002Fplugins\u002Fsaas-packs\u002Fdocumenso-pack\u002Fskills\u002Fdocumenso-multi-env-setup\u002FSKILL.md",[],[19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44],"setup","documenso","documenso multi env setup","multi","env setup","env","across","when","api keys","implementing","promotion","with","trigger","io","dev setup","multi-environment","up","use","environment","edit","write","read","configure","api","dev","workflows","2026-11-10 10:12:30",false,"---\nname: documenso-multi-env-setup\ndescription: |\n  Configure Documenso across multiple environments (dev, staging, production).\n  Use when setting up environment-specific configurations, managing API keys,\n  or implementing environment promotion workflows.\n  Trigger with phrases like \"documenso environments\", \"documenso staging\",\n  \"documenso dev setup\", \"multi-environment documenso\".\nallowed-tools: Read, Write, Edit\nversion: 1.0.0\nlicense: MIT\nauthor: Jeremy Longshore \u003Cjeremy@intentsolutions.io>\n---\n\n# Documenso Multi-Environment Setup\n\n## Overview\nConfigure and manage Documenso integrations across development, staging, and production environments with proper isolation and promotion workflows.\n\n## Prerequisites\n- Documenso accounts for each environment (or self-hosted instances)\n- Environment management infrastructure\n- Secret management solution (Vault, AWS Secrets Manager, etc.)\n\n## Environment Architecture\n\n```\n┌─────────────────────────────────────────────────────────┐\n│                    Development                           │\n│  API: stg-app.documenso.com (staging)                   │\n│  Key: DOCUMENSO_API_KEY_DEV                             │\n│  Purpose: Local development, testing                     │\n└─────────────────────────────────────────────────────────┘\n                          │\n                          ▼\n┌─────────────────────────────────────────────────────────┐\n│                      Staging                             │\n│  API: stg-app.documenso.com                             │\n│  Key: DOCUMENSO_API_KEY_STAGING                         │\n│  Purpose: Integration testing, QA                        │\n└─────────────────────────────────────────────────────────┘\n                          │\n                          ▼\n┌─────────────────────────────────────────────────────────┐\n│                     Production                           │\n│  API: app.documenso.com                                 │\n│  Key: DOCUMENSO_API_KEY_PRODUCTION                      │\n│  Purpose: Live customer traffic                          │\n└─────────────────────────────────────────────────────────┘\n```\n\n## Configuration Files\n\n### Environment-Specific Configs\n\n```typescript\n\u002F\u002F config\u002Fdocumenso.development.json\n{\n  \"baseUrl\": \"https:\u002F\u002Fstg-app.documenso.com\u002Fapi\u002Fv2\u002F\",\n  \"timeout\": 30000,\n  \"debug\": true,\n  \"mockEnabled\": true,\n  \"testRecipients\": [\"dev@yourcompany.com\"],\n  \"cacheOptions\": {\n    \"enabled\": false,\n    \"ttl\": 60\n  }\n}\n\n\u002F\u002F config\u002Fdocumenso.staging.json\n{\n  \"baseUrl\": \"https:\u002F\u002Fstg-app.documenso.com\u002Fapi\u002Fv2\u002F\",\n  \"timeout\": 30000,\n  \"debug\": true,\n  \"mockEnabled\": false,\n  \"testRecipients\": [\"staging-test@yourcompany.com\"],\n  \"cacheOptions\": {\n    \"enabled\": true,\n    \"ttl\": 300\n  }\n}\n\n\u002F\u002F config\u002Fdocumenso.production.json\n{\n  \"baseUrl\": \"https:\u002F\u002Fapp.documenso.com\u002Fapi\u002Fv2\u002F\",\n  \"timeout\": 30000,\n  \"debug\": false,\n  \"mockEnabled\": false,\n  \"testRecipients\": [],\n  \"cacheOptions\": {\n    \"enabled\": true,\n    \"ttl\": 600\n  }\n}\n```\n\n### Configuration Loader\n\n```typescript\n\u002F\u002F src\u002Fconfig\u002Fdocumenso.ts\nimport { z } from \"zod\";\n\nconst EnvironmentSchema = z.enum([\"development\", \"staging\", \"production\"]);\ntype Environment = z.infer\u003Ctypeof EnvironmentSchema>;\n\nconst ConfigSchema = z.object({\n  baseUrl: z.string().url(),\n  timeout: z.number().default(30000),\n  debug: z.boolean().default(false),\n  mockEnabled: z.boolean().default(false),\n  testRecipients: z.array(z.string().email()).default([]),\n  cacheOptions: z.object({\n    enabled: z.boolean(),\n    ttl: z.number(),\n  }),\n});\n\nexport type DocumensoConfig = z.infer\u003Ctypeof ConfigSchema>;\n\nexport function getEnvironment(): Environment {\n  const env = process.env.NODE_ENV ?? \"development\";\n  return EnvironmentSchema.parse(env);\n}\n\nexport function loadConfig(): DocumensoConfig & { apiKey: string } {\n  const env = getEnvironment();\n\n  \u002F\u002F Load file config\n  const fileConfig = require(`..\u002F..\u002Fconfig\u002Fdocumenso.${env}.json`);\n\n  \u002F\u002F Get API key from environment variable\n  const apiKeyVar = `DOCUMENSO_API_KEY_${env.toUpperCase()}`;\n  const apiKey = process.env[apiKeyVar] ?? process.env.DOCUMENSO_API_KEY;\n\n  if (!apiKey) {\n    throw new Error(`Missing API key: ${apiKeyVar}`);\n  }\n\n  \u002F\u002F Allow environment variable overrides\n  const config = {\n    ...ConfigSchema.parse(fileConfig),\n    apiKey,\n    baseUrl: process.env.DOCUMENSO_BASE_URL ?? fileConfig.baseUrl,\n  };\n\n  return config;\n}\n```\n\n## Environment Variables\n\n### .env.development\n\n```bash\nNODE_ENV=development\nDOCUMENSO_API_KEY_DEVELOPMENT=dcs_dev_xxx\n# Or use alias\nDOCUMENSO_API_KEY=${DOCUMENSO_API_KEY_DEVELOPMENT}\n\n# Optional overrides\nDOCUMENSO_BASE_URL=https:\u002F\u002Fstg-app.documenso.com\u002Fapi\u002Fv2\u002F\nDOCUMENSO_WEBHOOK_SECRET=dev-webhook-secret\n```\n\n### .env.staging\n\n```bash\nNODE_ENV=staging\nDOCUMENSO_API_KEY_STAGING=dcs_staging_xxx\nDOCUMENSO_WEBHOOK_SECRET=staging-webhook-secret\n```\n\n### .env.production\n\n```bash\nNODE_ENV=production\nDOCUMENSO_API_KEY_PRODUCTION=dcs_prod_xxx\nDOCUMENSO_WEBHOOK_SECRET=prod-webhook-secret\n```\n\n## Client Factory\n\n```typescript\n\u002F\u002F src\u002Fdocumenso\u002Ffactory.ts\nimport { Documenso } from \"@documenso\u002Fsdk-typescript\";\nimport { loadConfig, getEnvironment } from \"..\u002Fconfig\u002Fdocumenso\";\n\ninterface ClientOptions {\n  forceEnvironment?: \"development\" | \"staging\" | \"production\";\n  mockMode?: boolean;\n}\n\nconst clients = new Map\u003Cstring, Documenso>();\n\nexport function getDocumensoClient(options?: ClientOptions): Documenso {\n  const env = options?.forceEnvironment ?? getEnvironment();\n  const cacheKey = `${env}-${options?.mockMode ?? false}`;\n\n  if (clients.has(cacheKey)) {\n    return clients.get(cacheKey)!;\n  }\n\n  const config = loadConfig();\n\n  \u002F\u002F Use mock client in development if enabled\n  if (options?.mockMode && config.mockEnabled) {\n    console.log(`[Documenso] Using mock client for ${env}`);\n    return createMockClient();\n  }\n\n  const client = new Documenso({\n    apiKey: config.apiKey,\n    serverURL: config.baseUrl,\n    timeoutMs: config.timeout,\n    debugLogger: config.debug ? console : undefined,\n  });\n\n  clients.set(cacheKey, client);\n\n  console.log(`[Documenso] Client initialized for ${env}`);\n  console.log(`[Documenso] Base URL: ${config.baseUrl}`);\n\n  return client;\n}\n\n\u002F\u002F Reset clients (useful for testing)\nexport function resetClients(): void {\n  clients.clear();\n}\n```\n\n## Mock Client for Development\n\n```typescript\n\u002F\u002F src\u002Fdocumenso\u002Fmock.ts\nimport { Documenso } from \"@documenso\u002Fsdk-typescript\";\n\nlet mockDocumentCounter = 0;\n\nexport function createMockClient(): Documenso {\n  \u002F\u002F Create a proxy that intercepts all method calls\n  const mockClient = {\n    documents: {\n      createV0: async (input: any) => {\n        mockDocumentCounter++;\n        console.log(`[MOCK] Creating document: ${input.title}`);\n        return {\n          documentId: `mock_doc_${mockDocumentCounter}`,\n          title: input.title,\n          status: \"DRAFT\",\n        };\n      },\n      getV0: async ({ documentId }: any) => {\n        console.log(`[MOCK] Getting document: ${documentId}`);\n        return {\n          id: documentId,\n          title: \"Mock Document\",\n          status: \"PENDING\",\n          recipients: [],\n        };\n      },\n      findV0: async () => {\n        console.log(`[MOCK] Finding documents`);\n        return { documents: [], totalPages: 0 };\n      },\n      deleteV0: async ({ documentId }: any) => {\n        console.log(`[MOCK] Deleting document: ${documentId}`);\n        return { success: true };\n      },\n    },\n    templates: {\n      getV0: async ({ templateId }: any) => {\n        console.log(`[MOCK] Getting template: ${templateId}`);\n        return {\n          id: templateId,\n          title: \"Mock Template\",\n          recipients: [],\n        };\n      },\n    },\n    \u002F\u002F Add more mock methods as needed\n  };\n\n  return mockClient as unknown as Documenso;\n}\n```\n\n## Environment Promotion\n\n```typescript\n\u002F\u002F scripts\u002Fpromote-templates.ts\n\u002F**\n * Promote templates from staging to production.\n * Templates are recreated in production with same configuration.\n *\u002F\n\nimport { getDocumensoClient } from \"..\u002Fsrc\u002Fdocumenso\u002Ffactory\";\n\ninterface TemplateMapping {\n  stagingId: string;\n  productionId?: string;\n  name: string;\n}\n\nconst TEMPLATE_MAPPINGS: TemplateMapping[] = [\n  { stagingId: \"tmpl_stg_nda\", name: \"NDA Template\" },\n  { stagingId: \"tmpl_stg_contract\", name: \"Contract Template\" },\n];\n\nasync function promoteTemplates() {\n  const stagingClient = getDocumensoClient({ forceEnvironment: \"staging\" });\n  const prodClient = getDocumensoClient({ forceEnvironment: \"production\" });\n\n  console.log(\"Promoting templates from staging to production...\\n\");\n\n  for (const mapping of TEMPLATE_MAPPINGS) {\n    try {\n      \u002F\u002F Get staging template\n      const stagingTemplate = await stagingClient.templates.getV0({\n        templateId: mapping.stagingId,\n      });\n\n      console.log(`Template: ${mapping.name}`);\n      console.log(`  Staging ID: ${mapping.stagingId}`);\n\n      \u002F\u002F Note: In practice, you'd need to download the PDF and recreate\n      \u002F\u002F the template in production. This is a simplified example.\n      console.log(`  Status: Ready for manual creation in production`);\n      console.log(`  Fields: ${stagingTemplate.fields?.length ?? 0}`);\n      console.log(`  Recipients: ${stagingTemplate.recipients?.length ?? 0}`);\n      console.log(\"\");\n    } catch (error: any) {\n      console.error(`  Error: ${error.message}`);\n    }\n  }\n}\n\npromoteTemplates().catch(console.error);\n```\n\n## Webhook Configuration Per Environment\n\n```typescript\n\u002F\u002F src\u002Fwebhooks\u002Fconfig.ts\nimport { getEnvironment } from \"..\u002Fconfig\u002Fdocumenso\";\n\ninterface WebhookConfig {\n  url: string;\n  secret: string;\n  events: string[];\n}\n\nexport function getWebhookConfig(): WebhookConfig {\n  const env = getEnvironment();\n\n  const configs: Record\u003Cstring, WebhookConfig> = {\n    development: {\n      url: \"https:\u002F\u002Fdev.yourapp.com\u002Fwebhooks\u002Fdocumenso\",\n      secret: process.env.DOCUMENSO_WEBHOOK_SECRET_DEV ?? \"\",\n      events: [\"document.created\", \"document.completed\"],\n    },\n    staging: {\n      url: \"https:\u002F\u002Fstaging.yourapp.com\u002Fwebhooks\u002Fdocumenso\",\n      secret: process.env.DOCUMENSO_WEBHOOK_SECRET_STAGING ?? \"\",\n      events: [\n        \"document.created\",\n        \"document.sent\",\n        \"document.signed\",\n        \"document.completed\",\n      ],\n    },\n    production: {\n      url: \"https:\u002F\u002Fapp.yourapp.com\u002Fwebhooks\u002Fdocumenso\",\n      secret: process.env.DOCUMENSO_WEBHOOK_SECRET_PRODUCTION ?? \"\",\n      events: [\n        \"document.created\",\n        \"document.sent\",\n        \"document.opened\",\n        \"document.signed\",\n        \"document.completed\",\n        \"document.rejected\",\n        \"document.cancelled\",\n      ],\n    },\n  };\n\n  return configs[env];\n}\n```\n\n## Testing Across Environments\n\n```typescript\n\u002F\u002F tests\u002Fintegration\u002Fenvironment.test.ts\nimport { describe, it, expect, beforeAll } from \"vitest\";\nimport { getDocumensoClient, resetClients } from \"..\u002F..\u002Fsrc\u002Fdocumenso\u002Ffactory\";\n\ndescribe(\"Multi-Environment Configuration\", () => {\n  beforeAll(() => {\n    resetClients();\n  });\n\n  it(\"connects to staging environment\", async () => {\n    const client = getDocumensoClient({ forceEnvironment: \"staging\" });\n    const result = await client.documents.findV0({ perPage: 1 });\n    expect(result).toBeDefined();\n  });\n\n  it(\"uses mock client in development with mockMode\", async () => {\n    const client = getDocumensoClient({\n      forceEnvironment: \"development\",\n      mockMode: true,\n    });\n    const doc = await client.documents.createV0({ title: \"Test\" });\n    expect(doc.documentId).toMatch(\u002F^mock_doc_\u002F);\n  });\n\n  it(\"production requires production API key\", async () => {\n    \u002F\u002F This test verifies production config is properly isolated\n    const config = loadConfigForEnv(\"production\");\n    expect(config.baseUrl).toBe(\"https:\u002F\u002Fapp.documenso.com\u002Fapi\u002Fv2\u002F\");\n    expect(config.debug).toBe(false);\n  });\n});\n```\n\n## Environment Checklist\n\n### Development\n- [ ] Using staging Documenso API\n- [ ] Mock mode available for offline development\n- [ ] Debug logging enabled\n- [ ] Test recipients configured\n- [ ] Caching disabled for fresh data\n\n### Staging\n- [ ] Separate API key from production\n- [ ] All webhooks configured\n- [ ] Integration tests passing\n- [ ] Template parity with production\n- [ ] Monitoring configured\n\n### Production\n- [ ] Production API key secured\n- [ ] Webhooks verified\n- [ ] Caching enabled\n- [ ] Debug logging disabled\n- [ ] Alerting configured\n\n## Output\n- Environment-specific configurations\n- Isolated API keys per environment\n- Promotion workflow documented\n- Mock client for development\n\n## Error Handling\n| Issue | Cause | Solution |\n|-------|-------|----------|\n| Wrong environment | Missing NODE_ENV | Set explicitly |\n| Key mismatch | Using wrong key | Check env var names |\n| Config not loading | File path wrong | Verify config files |\n| Mock not working | mockEnabled: false | Enable in dev config |\n\n## Resources\n- [12-Factor App Config](https:\u002F\u002F12factor.net\u002Fconfig)\n- [Environment Management](https:\u002F\u002Fdocs.documenso.com\u002Fdevelopers)\n- [Secret Management Best Practices](https:\u002F\u002Fcheatsheetseries.owasp.org\u002Fcheatsheets\u002FSecrets_Management_Cheat_Sheet.html)\n\n## Next Steps\nFor monitoring setup, see `documenso-observability`.\n","# Documenso Multi-Environment Setup\n\n## Overview\nConfigure and manage Documenso integrations across development, staging, and production environments with proper isolation and promotion workflows.\n\n## Prerequisites\n- Documenso accounts for each environment (or self-hosted instances)\n- Environment management infrastructure\n- Secret management solution (Vault, AWS Secrets Manager, etc.)\n\n## Environment Architecture\n\n```\n┌─────────────────────────────────────────────────────────┐\n│                    Development                           │\n│  API: stg-app.documenso.com (staging)                   │\n│  Key: DOCUMENSO_API_KEY_DEV                             │\n│  Purpose: Local development, testing                     │\n└─────────────────────────────────────────────────────────┘\n                          │\n                          ▼\n┌─────────────────────────────────────────────────────────┐\n│                      Staging                             │\n│  API: stg-app.documenso.com                             │\n│  Key: DOCUMENSO_API_KEY_STAGING                         │\n│  Purpose: Integration testing, QA                        │\n└─────────────────────────────────────────────────────────┘\n                          │\n                          ▼\n┌─────────────────────────────────────────────────────────┐\n│                     Production                           │\n│  API: app.documenso.com                                 │\n│  Key: DOCUMENSO_API_KEY_PRODUCTION                      │\n│  Purpose: Live customer traffic                          │\n└─────────────────────────────────────────────────────────┘\n```\n\n## Configuration Files\n\n### Environment-Specific Configs\n\n```typescript\n\u002F\u002F config\u002Fdocumenso.development.json\n{\n  \"baseUrl\": \"https:\u002F\u002Fstg-app.documenso.com\u002Fapi\u002Fv2\u002F\",\n  \"timeout\": 30000,\n  \"debug\": true,\n  \"mockEnabled\": true,\n  \"testRecipients\": [\"dev@yourcompany.com\"],\n  \"cacheOptions\": {\n    \"enabled\": false,\n    \"ttl\": 60\n  }\n}\n\n\u002F\u002F config\u002Fdocumenso.staging.json\n{\n  \"baseUrl\": \"https:\u002F\u002Fstg-app.documenso.com\u002Fapi\u002Fv2\u002F\",\n  \"timeout\": 30000,\n  \"debug\": true,\n  \"mockEnabled\": false,\n  \"testRecipients\": [\"staging-test@yourcompany.com\"],\n  \"cacheOptions\": {\n    \"enabled\": true,\n    \"ttl\": 300\n  }\n}\n\n\u002F\u002F config\u002Fdocumenso.production.json\n{\n  \"baseUrl\": \"https:\u002F\u002Fapp.documenso.com\u002Fapi\u002Fv2\u002F\",\n  \"timeout\": 30000,\n  \"debug\": false,\n  \"mockEnabled\": false,\n  \"testRecipients\": [],\n  \"cacheOptions\": {\n    \"enabled\": true,\n    \"ttl\": 600\n  }\n}\n```\n\n### Configuration Loader\n\n```typescript\n\u002F\u002F src\u002Fconfig\u002Fdocumenso.ts\nimport { z } from \"zod\";\n\nconst EnvironmentSchema = z.enum([\"development\", \"staging\", \"production\"]);\ntype Environment = z.infer\u003Ctypeof EnvironmentSchema>;\n\nconst ConfigSchema = z.object({\n  baseUrl: z.string().url(),\n  timeout: z.number().default(30000),\n  debug: z.boolean().default(false),\n  mockEnabled: z.boolean().default(false),\n  testRecipients: z.array(z.string().email()).default([]),\n  cacheOptions: z.object({\n    enabled: z.boolean(),\n    ttl: z.number(),\n  }),\n});\n\nexport type DocumensoConfig = z.infer\u003Ctypeof ConfigSchema>;\n\nexport function getEnvironment(): Environment {\n  const env = process.env.NODE_ENV ?? \"development\";\n  return EnvironmentSchema.parse(env);\n}\n\nexport function loadConfig(): DocumensoConfig & { apiKey: string } {\n  const env = getEnvironment();\n\n  \u002F\u002F Load file config\n  const fileConfig = require(`..\u002F..\u002Fconfig\u002Fdocumenso.${env}.json`);\n\n  \u002F\u002F Get API key from environment variable\n  const apiKeyVar = `DOCUMENSO_API_KEY_${env.toUpperCase()}`;\n  const apiKey = process.env[apiKeyVar] ?? process.env.DOCUMENSO_API_KEY;\n\n  if (!apiKey) {\n    throw new Error(`Missing API key: ${apiKeyVar}`);\n  }\n\n  \u002F\u002F Allow environment variable overrides\n  const config = {\n    ...ConfigSchema.parse(fileConfig),\n    apiKey,\n    baseUrl: process.env.DOCUMENSO_BASE_URL ?? fileConfig.baseUrl,\n  };\n\n  return config;\n}\n```\n\n## Environment Variables\n\n### .env.development\n\n```bash\nNODE_ENV=development\nDOCUMENSO_API_KEY_DEVELOPMENT=dcs_dev_xxx\n# Or use alias\nDOCUMENSO_API_KEY=${DOCUMENSO_API_KEY_DEVELOPMENT}\n\n# Optional overrides\nDOCUMENSO_BASE_URL=https:\u002F\u002Fstg-app.documenso.com\u002Fapi\u002Fv2\u002F\nDOCUMENSO_WEBHOOK_SECRET=dev-webhook-secret\n```\n\n### .env.staging\n\n```bash\nNODE_ENV=staging\nDOCUMENSO_API_KEY_STAGING=dcs_staging_xxx\nDOCUMENSO_WEBHOOK_SECRET=staging-webhook-secret\n```\n\n### .env.production\n\n```bash\nNODE_ENV=production\nDOCUMENSO_API_KEY_PRODUCTION=dcs_prod_xxx\nDOCUMENSO_WEBHOOK_SECRET=prod-webhook-secret\n```\n\n## Client Factory\n\n```typescript\n\u002F\u002F src\u002Fdocumenso\u002Ffactory.ts\nimport { Documenso } from \"@documenso\u002Fsdk-typescript\";\nimport { loadConfig, getEnvironment } from \"..\u002Fconfig\u002Fdocumenso\";\n\ninterface ClientOptions {\n  forceEnvironment?: \"development\" | \"staging\" | \"production\";\n  mockMode?: boolean;\n}\n\nconst clients = new Map\u003Cstring, Documenso>();\n\nexport function getDocumensoClient(options?: ClientOptions): Documenso {\n  const env = options?.forceEnvironment ?? getEnvironment();\n  const cacheKey = `${env}-${options?.mockMode ?? false}`;\n\n  if (clients.has(cacheKey)) {\n    return clients.get(cacheKey)!;\n  }\n\n  const config = loadConfig();\n\n  \u002F\u002F Use mock client in development if enabled\n  if (options?.mockMode && config.mockEnabled) {\n    console.log(`[Documenso] Using mock client for ${env}`);\n    return createMockClient();\n  }\n\n  const client = new Documenso({\n    apiKey: config.apiKey,\n    serverURL: config.baseUrl,\n    timeoutMs: config.timeout,\n    debugLogger: config.debug ? console : undefined,\n  });\n\n  clients.set(cacheKey, client);\n\n  console.log(`[Documenso] Client initialized for ${env}`);\n  console.log(`[Documenso] Base URL: ${config.baseUrl}`);\n\n  return client;\n}\n\n\u002F\u002F Reset clients (useful for testing)\nexport function resetClients(): void {\n  clients.clear();\n}\n```\n\n## Mock Client for Development\n\n```typescript\n\u002F\u002F src\u002Fdocumenso\u002Fmock.ts\nimport { Documenso } from \"@documenso\u002Fsdk-typescript\";\n\nlet mockDocumentCounter = 0;\n\nexport function createMockClient(): Documenso {\n  \u002F\u002F Create a proxy that intercepts all method calls\n  const mockClient = {\n    documents: {\n      createV0: async (input: any) => {\n        mockDocumentCounter++;\n        console.log(`[MOCK] Creating document: ${input.title}`);\n        return {\n          documentId: `mock_doc_${mockDocumentCounter}`,\n          title: input.title,\n          status: \"DRAFT\",\n        };\n      },\n      getV0: async ({ documentId }: any) => {\n        console.log(`[MOCK] Getting document: ${documentId}`);\n        return {\n          id: documentId,\n          title: \"Mock Document\",\n          status: \"PENDING\",\n          recipients: [],\n        };\n      },\n      findV0: async () => {\n        console.log(`[MOCK] Finding documents`);\n        return { documents: [], totalPages: 0 };\n      },\n      deleteV0: async ({ documentId }: any) => {\n        console.log(`[MOCK] Deleting document: ${documentId}`);\n        return { success: true };\n      },\n    },\n    templates: {\n      getV0: async ({ templateId }: any) => {\n        console.log(`[MOCK] Getting template: ${templateId}`);\n        return {\n          id: templateId,\n          title: \"Mock Template\",\n          recipients: [],\n        };\n      },\n    },\n    \u002F\u002F Add more mock methods as needed\n  };\n\n  return mockClient as unknown as Documenso;\n}\n```\n\n## Environment Promotion\n\n```typescript\n\u002F\u002F scripts\u002Fpromote-templates.ts\n\u002F**\n * Promote templates from staging to production.\n * Templates are recreated in production with same configuration.\n *\u002F\n\nimport { getDocumensoClient } from \"..\u002Fsrc\u002Fdocumenso\u002Ffactory\";\n\ninterface TemplateMapping {\n  stagingId: string;\n  productionId?: string;\n  name: string;\n}\n\nconst TEMPLATE_MAPPINGS: TemplateMapping[] = [\n  { stagingId: \"tmpl_stg_nda\", name: \"NDA Template\" },\n  { stagingId: \"tmpl_stg_contract\", name: \"Contract Template\" },\n];\n\nasync function promoteTemplates() {\n  const stagingClient = getDocumensoClient({ forceEnvironment: \"staging\" });\n  const prodClient = getDocumensoClient({ forceEnvironment: \"production\" });\n\n  console.log(\"Promoting templates from staging to production...\\n\");\n\n  for (const mapping of TEMPLATE_MAPPINGS) {\n    try {\n      \u002F\u002F Get staging template\n      const stagingTemplate = await stagingClient.templates.getV0({\n        templateId: mapping.stagingId,\n      });\n\n      console.log(`Template: ${mapping.name}`);\n      console.log(`  Staging ID: ${mapping.stagingId}`);\n\n      \u002F\u002F Note: In practice, you'd need to download the PDF and recreate\n      \u002F\u002F the template in production. This is a simplified example.\n      console.log(`  Status: Ready for manual creation in production`);\n      console.log(`  Fields: ${stagingTemplate.fields?.length ?? 0}`);\n      console.log(`  Recipients: ${stagingTemplate.recipients?.length ?? 0}`);\n      console.log(\"\");\n    } catch (error: any) {\n      console.error(`  Error: ${error.message}`);\n    }\n  }\n}\n\npromoteTemplates().catch(console.error);\n```\n\n## Webhook Configuration Per Environment\n\n```typescript\n\u002F\u002F src\u002Fwebhooks\u002Fconfig.ts\nimport { getEnvironment } from \"..\u002Fconfig\u002Fdocumenso\";\n\ninterface WebhookConfig {\n  url: string;\n  secret: string;\n  events: string[];\n}\n\nexport function getWebhookConfig(): WebhookConfig {\n  const env = getEnvironment();\n\n  const configs: Record\u003Cstring, WebhookConfig> = {\n    development: {\n      url: \"https:\u002F\u002Fdev.yourapp.com\u002Fwebhooks\u002Fdocumenso\",\n      secret: process.env.DOCUMENSO_WEBHOOK_SECRET_DEV ?? \"\",\n      events: [\"document.created\", \"document.completed\"],\n    },\n    staging: {\n      url: \"https:\u002F\u002Fstaging.yourapp.com\u002Fwebhooks\u002Fdocumenso\",\n      secret: process.env.DOCUMENSO_WEBHOOK_SECRET_STAGING ?? \"\",\n      events: [\n        \"document.created\",\n        \"document.sent\",\n        \"document.signed\",\n        \"document.completed\",\n      ],\n    },\n    production: {\n      url: \"https:\u002F\u002Fapp.yourapp.com\u002Fwebhooks\u002Fdocumenso\",\n      secret: process.env.DOCUMENSO_WEBHOOK_SECRET_PRODUCTION ?? \"\",\n      events: [\n        \"document.created\",\n        \"document.sent\",\n        \"document.opened\",\n        \"document.signed\",\n        \"document.completed\",\n        \"document.rejected\",\n        \"document.cancelled\",\n      ],\n    },\n  };\n\n  return configs[env];\n}\n```\n\n## Testing Across Environments\n\n```typescript\n\u002F\u002F tests\u002Fintegration\u002Fenvironment.test.ts\nimport { describe, it, expect, beforeAll } from \"vitest\";\nimport { getDocumensoClient, resetClients } from \"..\u002F..\u002Fsrc\u002Fdocumenso\u002Ffactory\";\n\ndescribe(\"Multi-Environment Configuration\", () => {\n  beforeAll(() => {\n    resetClients();\n  });\n\n  it(\"connects to staging environment\", async () => {\n    const client = getDocumensoClient({ forceEnvironment: \"staging\" });\n    const result = await client.documents.findV0({ perPage: 1 });\n    expect(result).toBeDefined();\n  });\n\n  it(\"uses mock client in development with mockMode\", async () => {\n    const client = getDocumensoClient({\n      forceEnvironment: \"development\",\n      mockMode: true,\n    });\n    const doc = await client.documents.createV0({ title: \"Test\" });\n    expect(doc.documentId).toMatch(\u002F^mock_doc_\u002F);\n  });\n\n  it(\"production requires production API key\", async () => {\n    \u002F\u002F This test verifies production config is properly isolated\n    const config = loadConfigForEnv(\"production\");\n    expect(config.baseUrl).toBe(\"https:\u002F\u002Fapp.documenso.com\u002Fapi\u002Fv2\u002F\");\n    expect(config.debug).toBe(false);\n  });\n});\n```\n\n## Environment Checklist\n\n### Development\n- [ ] Using staging Documenso API\n- [ ] Mock mode available for offline development\n- [ ] Debug logging enabled\n- [ ] Test recipients configured\n- [ ] Caching disabled for fresh data\n\n### Staging\n- [ ] Separate API key from production\n- [ ] All webhooks configured\n- [ ] Integration tests passing\n- [ ] Template parity with production\n- [ ] Monitoring configured\n\n### Production\n- [ ] Production API key secured\n- [ ] Webhooks verified\n- [ ] Caching enabled\n- [ ] Debug logging disabled\n- [ ] Alerting configured\n\n## Output\n- Environment-specific configurations\n- Isolated API keys per environment\n- Promotion workflow documented\n- Mock client for development\n\n## Error Handling\n| Issue | Cause | Solution |\n|-------|-------|----------|\n| Wrong environment | Missing NODE_ENV | Set explicitly |\n| Key mismatch | Using wrong key | Check env var names |\n| Config not loading | File path wrong | Verify config files |\n| Mock not working | mockEnabled: false | Enable in dev config |\n\n## Resources\n- [12-Factor App Config](https:\u002F\u002F12factor.net\u002Fconfig)\n- [Environment Management](https:\u002F\u002Fdocs.documenso.com\u002Fdevelopers)\n- [Secret Management Best Practices](https:\u002F\u002Fcheatsheetseries.owasp.org\u002Fcheatsheets\u002FSecrets_Management_Cheat_Sheet.html)\n\n## Next Steps\nFor monitoring setup, see `documenso-observability`.\n",{"name":5,"author":50,"license":15,"version":51,"description":52,"allowed-tools":53},"Jeremy Longshore \u003Cjeremy@intentsolutions.io>","1.0.0","Configure Documenso across multiple environments (dev, staging, production).\nUse when setting up environment-specific configurations, managing API keys,\nor implementing environment promotion workflows.\nTrigger with phrases like \"documenso environments\", \"documenso staging\",\n\"documenso dev setup\", \"multi-environment documenso\".\n","Read, Write, Edit","main","90b50ed0778745a80fd5bfb13b1843d45b9e82f7","tests\u002Fext_conformance\u002Fartifacts\u002Fplugins-community\u002Fplugins\u002Fsaas-packs\u002Fdocumenso-pack\u002Fskills\u002Fdocumenso-multi-env-setup",233,"https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust","https:\u002F\u002Fgithub.com\u002FDicklesworthstone","https:\u002F\u002Fwww.jeffreyemanuel.com\u002F","Building in NY","2026-11-13 03:33:43",{"source":8,"skillName":5,"commands":64,"prompt":74,"zipAvailable":75},[65,68,71],{"tool":66,"command":67},"npx","npx skills add Dicklesworthstone\u002Fpi_agent_rust --skill documenso-multi-env-setup",{"tool":69,"command":70},"bunx","bunx skills add Dicklesworthstone\u002Fpi_agent_rust --skill documenso-multi-env-setup",{"tool":72,"command":73},"pnpm","pnpm dlx skills add Dicklesworthstone\u002Fpi_agent_rust --skill documenso-multi-env-setup","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Fplugins-community\u002Fplugins\u002Fsaas-packs\u002Fdocumenso-pack\u002Fskills\u002Fdocumenso-multi-env-setup\u002FSKILL.md",true,[],[78,117,141,167,193,215],{"skillNo":79,"name":80,"nameZh":6,"description":81,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":82,"licenseFileUrl":83,"sourceHtmlUrl":84,"categories":85,"keywords":91,"publishedAt":45},"01M6FEGPPK0ZTKCCM0R3E3KS33","web-artifacts-builder","Suite of tools for creating elaborate, multi-component claude.ai HTML artifacts using modern frontend web technologies (React, Tailwind CSS, shadcn\u002Fui). Use for complex artifacts requiring state management, routing, or shadcn\u002Fui components - not for simple single-file HTML\u002FJSX artifacts.","Apache-2.0","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Futilities\u002Fweb-artifacts-builder\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Futilities\u002Fweb-artifacts-builder\u002FSKILL.md",[86],{"categoryNo":87,"categoryName":88,"taxonomyType":89,"taxonomyTypeDesc":90},"01JBSEEDCAT000000000000009","前端开发","SCENARIO","使用场景",[92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,36,110,111,112,113,22,114,115,116],"web artifacts builder","web","artifacts","artifacts builder","builder","frontend","css","components","react","component","routing","state management","state","shadcn","file","ui","Tools","Tailwind","html","single-file","simple","ui components","for","jsx","suite",{"skillNo":118,"name":119,"nameZh":6,"description":120,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":82,"licenseFileUrl":121,"sourceHtmlUrl":122,"categories":123,"keywords":128,"publishedAt":45},"01M6FEE9VTK1W4GY21AKREJXRG","webapp-testing","Toolkit for interacting with and testing local web applications using Playwright. Supports verifying frontend functionality, debugging UI behavior, capturing browser screenshots, and viewing browser logs.","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fsecurity\u002Fwebapp-testing\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fsecurity\u002Fwebapp-testing\u002FSKILL.md",[124,127],{"categoryNo":125,"categoryName":126,"taxonomyType":89,"taxonomyTypeDesc":90},"01JBSEEDCAT000000000000008","测试与质量",{"categoryNo":87,"categoryName":88,"taxonomyType":89,"taxonomyTypeDesc":90},[129,130,131,97,132,133,93,134,135,136,107,137,138,139,140,30,114],"testing","webapp testing","webapp","screenshots","playwright","debugging","toolkit","browser","logs","applications","behavior","local",{"skillNo":142,"name":143,"nameZh":6,"description":144,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":145,"sourceHtmlUrl":146,"categories":147,"keywords":148,"publishedAt":45},"01M6FE20F7ZTQBFWKF4MPJ2SBX","neuropixels-analysis","Neuropixels neural recording analysis. Load SpikeGLX\u002FOpenEphys data, preprocess, motion correction, Kilosort4 spike sorting, quality metrics, Allen\u002FIBL curation, AI-assisted visual analysis, for Neuropixels 1.0\u002F2.0 extracellular electrophysiology. Use when working with neural recordings, spike sorting, extracellular electrophysiology, or when the user mentions Neuropixels, SpikeGLX, Open Ephys, Kilosort, quality metrics, or unit curation.","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fscientific\u002Fneuropixels-analysis\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fscientific\u002Fneuropixels-analysis\u002FSKILL.md",[],[149,150,151,152,153,154,155,156,36,157,158,159,160,161,162,163,30,164,165,166,114,26],"analysis","neuropixels analysis","spike","metrics","data","open","quality","user","recordings","Neural","motion","visual","quality metrics","unit","sorting","curation","recording","load",{"skillNo":168,"name":169,"nameZh":6,"description":170,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":82,"licenseFileUrl":171,"sourceHtmlUrl":172,"categories":173,"keywords":177,"publishedAt":45},"01M6FDP0PDK6F1GGX395C7625Z","skill-creator","Guide for creating effective skills. This skill should be used when users want to create a new skill (or update an existing skill) that extends Claude's capabilities with specialized knowledge, workflows, or tool integrations.","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fproductivity\u002Fskill-creator\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fproductivity\u002Fskill-creator\u002FSKILL.md",[174],{"categoryNo":175,"categoryName":176,"taxonomyType":89,"taxonomyTypeDesc":90},"01JBSEEDCAT000000000000023","Skill与智能体开发",[178,179,44,180,181,182,183,184,185,186,187,188,189,190,30,114,191,26,192],"skill creator","creator","create","update","capabilities","knowledge","tool","integrations","specialized","guide","new skill","existing skill","users","Existing","new",{"skillNo":194,"name":195,"nameZh":6,"description":196,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":197,"sourceHtmlUrl":198,"categories":199,"keywords":201,"publishedAt":45},"01M6FDM8Y7VK7NYG9SM7QPA3PW","notebooklm","Use this skill to query your Google NotebookLM notebooks directly from Claude Code for source-grounded, citation-backed answers from Gemini. Browser automation, library management, persistent auth. Drastically reduced hallucinations through document-only responses.","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fproductivity\u002Fnotebooklm\u002FLICENSE","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fproductivity\u002Fnotebooklm\u002FSKILL.md",[200],{"categoryNo":175,"categoryName":176,"taxonomyType":89,"taxonomyTypeDesc":90},[195,202,203,204,205,206,207,136,208,209,36,210,211,212,213,114,214],"google","browser automation","gemini","automation","auth","code","library","management","only","document","query","Citation","source",{"skillNo":216,"name":217,"nameZh":6,"description":218,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":82,"licenseFileUrl":219,"sourceHtmlUrl":220,"categories":221,"keywords":228,"publishedAt":45},"01M6FDHPXE10J3XGTCH5HBHS1M","slack-gif-creator","Knowledge and utilities for creating animated GIFs optimized for Slack. Provides constraints, validation tools, and animation concepts. Use when users request animated GIFs for Slack like \"make me a GIF of X doing Y for Slack.\"","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fenterprise-communication\u002Fslack-gif-creator\u002FLICENSE.txt","https:\u002F\u002Fgithub.com\u002FDicklesworthstone\u002Fpi_agent_rust\u002Fblob\u002Fmain\u002Ftests\u002Fext_conformance\u002Fartifacts\u002Ftemplates-davila7\u002Fcli-tool\u002Fcomponents\u002Fskills\u002Fenterprise-communication\u002Fslack-gif-creator\u002FSKILL.md",[222,225],{"categoryNo":223,"categoryName":224,"taxonomyType":89,"taxonomyTypeDesc":90},"01JBSEEDCAT000000000000005","邮件与沟通",{"categoryNo":226,"categoryName":227,"taxonomyType":89,"taxonomyTypeDesc":90},"01JBSEEDCAT000000000000016","图像与多媒体",[229,230,231,179,232,108,233,234,183,36,235,190,236,237,238,114,239,26],"slack gif creator","slack","gif","validation","constraints","Animation","me","make","Utilities","concepts","request"]