Agent Skills: Intercom Multi-Environment Setup

|

UncategorizedID: jeremylongshore/claude-code-plugins-plus-skills/intercom-multi-env-setup

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/intercom-pack/skills/intercom-multi-env-setup

Skill Files

Browse the full folder contents for intercom-multi-env-setup.

Download Skill

Loading file tree…

plugins/saas-packs/intercom-pack/skills/intercom-multi-env-setup/SKILL.md

Skill Metadata

Name
intercom-multi-env-setup
Description
|

Intercom Multi-Environment Setup

Overview

Configure separate Intercom workspaces for development, staging, and production with environment-specific access tokens, webhook URLs, and safety guards.

Prerequisites

  • Separate Intercom workspaces (or at minimum, separate apps in Developer Hub)
  • Secret management solution (Vault, AWS Secrets Manager, GCP Secret Manager)
  • CI/CD pipeline with environment variable support

Environment Strategy

| Environment | Workspace | Token Type | Data | Webhooks | |-------------|-----------|-----------|------|----------| | Development | Dev/sandbox workspace | Dev access token | Test data | localhost via ngrok | | Staging | Staging workspace | Staging token | Seed data | staging.example.com | | Production | Production workspace | Production token | Real data | api.example.com |

Instructions

Step 1: Environment Configuration

// src/config/intercom.ts
interface IntercomEnvironmentConfig {
  accessToken: string;
  webhookSecret: string;
  identitySecret: string;
  environment: "development" | "staging" | "production";
  baseUrl: string;
  debug: boolean;
  cache: { enabled: boolean; ttlSeconds: number };
  rateLimit: { maxConcurrency: number };
}

function loadConfig(): IntercomEnvironmentConfig {
  const env = (process.env.NODE_ENV || "development") as IntercomEnvironmentConfig["environment"];

  const shared = {
    accessToken: process.env.INTERCOM_ACCESS_TOKEN!,
    webhookSecret: process.env.INTERCOM_WEBHOOK_SECRET!,
    identitySecret: process.env.INTERCOM_IDENTITY_SECRET || "",
    environment: env,
    baseUrl: "https://api.intercom.io",
  };

  const envDefaults: Record<string, Partial<IntercomEnvironmentConfig>> = {
    development: {
      debug: true,
      cache: { enabled: false, ttlSeconds: 0 },
      rateLimit: { maxConcurrency: 2 },
    },
    staging: {
      debug: false,
      cache: { enabled: true, ttlSeconds: 60 },
      rateLimit: { maxConcurrency: 5 },
    },
    production: {
      debug: false,
      cache: { enabled: true, ttlSeconds: 300 },
      rateLimit: { maxConcurrency: 10 },
    },
  };

  return { ...shared, ...envDefaults[env] } as IntercomEnvironmentConfig;
}

export const intercomConfig = loadConfig();

Step 2: Environment-Aware Client Factory

// src/intercom/client.ts
import { IntercomClient } from "intercom-client";
import { intercomConfig } from "../config/intercom";

let client: IntercomClient | null = null;

export function getClient(): IntercomClient {
  if (!client) {
    if (!intercomConfig.accessToken) {
      throw new Error(
        `INTERCOM_ACCESS_TOKEN not set for ${intercomConfig.environment}. ` +
        `Create a .env.${intercomConfig.environment} file.`
      );
    }

    client = new IntercomClient({
      token: intercomConfig.accessToken,
    });

    if (intercomConfig.debug) {
      console.log(`[Intercom] Connected to ${intercomConfig.environment} workspace`);
    }
  }
  return client;
}

Step 3: Secret Management by Platform

# Local development (.env.development - git-ignored)
INTERCOM_ACCESS_TOKEN=dG9rOmRldl90b2tlbg==
INTERCOM_WEBHOOK_SECRET=dev-webhook-secret
NODE_ENV=development

# GitHub Actions (for CI)
gh secret set INTERCOM_DEV_TOKEN --body "dev-token"
gh secret set INTERCOM_STAGING_TOKEN --body "staging-token"
gh secret set INTERCOM_PROD_TOKEN --body "prod-token"

# AWS Secrets Manager
aws secretsmanager create-secret \
  --name intercom/production/access-token \
  --secret-string "prod-token"

# GCP Secret Manager
echo -n "prod-token" | gcloud secrets create intercom-prod-token --data-file=-

# HashiCorp Vault
vault kv put secret/intercom/production \
  access_token="prod-token" \
  webhook_secret="prod-webhook-secret"

Step 4: Production Safety Guards

// Prevent destructive operations in wrong environment
class EnvironmentGuard {
  constructor(private env: string) {}

  requireProduction(operation: string): void {
    if (this.env !== "production") {
      throw new Error(
        `${operation} is only allowed in production (current: ${this.env})`
      );
    }
  }

  preventProduction(operation: string): void {
    if (this.env === "production") {
      throw new Error(
        `${operation} is blocked in production for safety`
      );
    }
  }
}

const guard = new EnvironmentGuard(intercomConfig.environment);

// Usage
async function deleteAllTestContacts() {
  guard.preventProduction("deleteAllTestContacts"); // Blocks in prod

  const contacts = await client.contacts.search({
    query: { field: "custom_attributes.is_test", operator: "=", value: true },
  });

  for (const contact of contacts.data) {
    await client.contacts.delete({ contactId: contact.id });
  }
}

async function sendBulkMessages(contactIds: string[], message: string) {
  guard.requireProduction("sendBulkMessages"); // Only works in prod
  // ... send messages
}

Step 5: Webhook URL per Environment

// CI/CD: Set webhook URL based on environment
const webhookUrls: Record<string, string> = {
  development: "https://dev-abc123.ngrok.io/webhooks/intercom",
  staging: "https://staging.example.com/webhooks/intercom",
  production: "https://api.example.com/webhooks/intercom",
};

// In your webhook handler, log the environment for debugging
app.post("/webhooks/intercom", (req, res) => {
  console.log(`[${intercomConfig.environment}] Webhook received: ${req.body.topic}`);
  // ... process webhook
});

Step 6: Environment Validation on Startup

async function validateIntercomSetup(): Promise<void> {
  const client = getClient();
  const config = intercomConfig;

  console.log(`[Intercom] Validating ${config.environment} setup...`);

  try {
    const admins = await client.admins.list();
    const appName = admins.admins[0]?.name || "Unknown";
    console.log(`[Intercom] Connected to workspace (admin: ${appName})`);

    // Verify we're in the right workspace
    if (config.environment === "production") {
      // In production, verify the token has expected permissions
      try {
        await client.contacts.list({ perPage: 1 });
        console.log("[Intercom] Contact access: OK");
      } catch {
        console.error("[Intercom] WARNING: Cannot access contacts");
      }
    }
  } catch (err) {
    console.error(`[Intercom] Setup validation FAILED for ${config.environment}:`, err);
    if (config.environment === "production") {
      throw err; // Fail fast in production
    }
  }
}

// Call during application startup
validateIntercomSetup().catch(console.error);

GitHub Actions Environment Matrix

jobs:
  test:
    strategy:
      matrix:
        environment: [development, staging]
    runs-on: ubuntu-latest
    env:
      NODE_ENV: ${{ matrix.environment }}
      INTERCOM_ACCESS_TOKEN: ${{ secrets[format('INTERCOM_{0}_TOKEN', matrix.environment)] }}
    steps:
      - uses: actions/checkout@v4
      - run: npm ci
      - run: npm test

Error Handling

| Issue | Cause | Solution | |-------|-------|----------| | Wrong workspace | Dev token used in staging | Validate workspace on startup | | Token not found | Missing env file | Copy .env.example to .env.{env} | | Guard blocked operation | Environment mismatch | Verify NODE_ENV is correct | | Webhook URL mismatch | Forgot to update URL | Use env-based URL config |

Resources

Next Steps

For observability setup, see intercom-observability.