agentsclimarketplace

Instantly multi env setup

Skill jeremylongshore/claude-code-plugins-plus-skills/plugins/saas-packs/instantly-pack/skills/instantly-multi-env-setup

425 plugins, 2,810 skills, 200 agents for Claude Code. Open-source marketplace at tonsofskills.com with the ccpi CLI package manager.

Install
npx -y skills add jeremylongshore/claude-code-plugins-plus-skills --skill instantly-multi-env-setup

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

What its author says it does

Copied from the file, not written here

'Configure Instantly.ai across development, staging, and production environments.

The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

8.5 KB, as published. Nobody here has run it

Instantly Multi-Environment Setup

Overview

Configure Instantly API v2 integrations across development, staging, and production environments. Instantly uses workspace-level isolation — each workspace has its own accounts, campaigns, leads, and API keys. This skill covers workspace separation, environment-specific configuration, mock server for dev, and safe promotion workflows.

Environment Strategy

EnvironmentInstantly BackendAPI KeysWebhooksPurpose
DevelopmentMock servermock-keylocalhost:3000Code iteration
StagingSeparate workspaceStaging keystaging.yourapp.comIntegration testing
ProductionProduction workspaceProd keyprod.yourapp.comLive outreach

Instructions

Step 1: Environment Configuration

// src/config.ts
import "dotenv/config";

type Env = "development" | "staging" | "production";

interface InstantlyConfig {
  env: Env;
  apiKey: string;
  baseUrl: string;
  webhookSecret: string;
  useMock: boolean;
  dailyLimitCap: number;
  enableRealSending: boolean;
}

export function getConfig(): InstantlyConfig {
  const env = (process.env.NODE_ENV || "development") as Env;

  const configs: Record<Env, InstantlyConfig> = {
    development: {
      env: "development",
      apiKey: process.env.INSTANTLY_API_KEY_DEV || "mock-key",
      baseUrl: "https://developer.instantly.ai/_mock/api/v2",
      webhookSecret: "dev-secret",
      useMock: true,
      dailyLimitCap: 5,
      enableRealSending: false,
    },
    staging: {
      env: "staging",
      apiKey: process.env.INSTANTLY_API_KEY_STAGING || "",
      baseUrl: "https://api.instantly.ai/api/v2",
      webhookSecret: process.env.INSTANTLY_WEBHOOK_SECRET_STAGING || "",
      useMock: false,
      dailyLimitCap: 10,
      enableRealSending: true,
    },
    production: {
      env: "production",
      apiKey: process.env.INSTANTLY_API_KEY_PROD || "",
      baseUrl: "https://api.instantly.ai/api/v2",
      webhookSecret: process.env.INSTANTLY_WEBHOOK_SECRET_PROD || "",
      useMock: false,
      dailyLimitCap: 100,
      enableRealSending: true,
    },
  };

  const config = configs[env];
  if (!config.useMock && !config.apiKey) {
    throw new Error(`INSTANTLY_API_KEY_${env.toUpperCase()} is required for ${env}`);
  }

  return config;
}

Step 2: Environment-Specific .env Files

# .env.development
NODE_ENV=development
INSTANTLY_API_KEY_DEV=mock-key
INSTANTLY_BASE_URL=https://developer.instantly.ai/_mock/api/v2
INSTANTLY_WEBHOOK_SECRET=dev-secret-123

# .env.staging
NODE_ENV=staging
INSTANTLY_API_KEY_STAGING=your-staging-workspace-key
INSTANTLY_BASE_URL=https://api.instantly.ai/api/v2
INSTANTLY_WEBHOOK_SECRET_STAGING=staging-secret-456

# .env.production
NODE_ENV=production
INSTANTLY_API_KEY_PROD=your-production-workspace-key
INSTANTLY_BASE_URL=https://api.instantly.ai/api/v2
INSTANTLY_WEBHOOK_SECRET_PROD=prod-secret-789

Step 3: Safe Campaign Creation with Environment Guards

import { getConfig } from "./config";
import { InstantlyClient } from "./instantly/client";

const config = getConfig();
const client = new InstantlyClient(config.apiKey, config.baseUrl);

async function createCampaignSafe(name: string, sequences: any[]) {
  // Guard: add environment prefix to campaign names
  const envPrefix = config.env === "production" ? "" : `[${config.env.toUpperCase()}] `;
  const safeName = `${envPrefix}${name}`;

  // Guard: cap daily limit per environment
  const campaign = await client.campaigns.create({
    name: safeName,
    daily_limit: Math.min(50, config.dailyLimitCap),
    sequences,
    campaign_schedule: {
      start_date: new Date().toISOString().split("T")[0],
      schedules: [{
        name: "Business Hours",
        timing: { from: "09:00", to: "17:00" },
        days: { "1": true, "2": true, "3": true, "4": true, "5": true, "0": false, "6": false },
        timezone: "America/New_York",
      }],
    },
    stop_on_reply: true,
  });

  console.log(`[${config.env}] Campaign created: ${campaign.name} (${campaign.id})`);

  // Guard: never auto-activate in production
  if (config.env !== "production") {
    await client.campaigns.activate(campaign.id);
    console.log(`[${config.env}] Campaign auto-activated (non-prod)`);
  } else {
    console.log(`[production] Campaign created in DRAFT — manual activation required`);
  }

  return campaign;
}

Step 4: Workspace Isolation Verification

async function verifyWorkspaceIsolation() {
  const config = getConfig();

  // Get current workspace info
  const workspace = await client.request<{
    id: string; name: string;
  }>("/workspaces/current");

  console.log(`Environment: ${config.env}`);
  console.log(`Workspace: ${workspace.name} (${workspace.id})`);

  // Safety check: verify workspace matches expected environment
  const expectedWorkspaceNames: Record<string, string[]> = {
    development: ["dev", "test", "mock"],
    staging: ["staging", "stage", "qa"],
    production: ["prod", "production", "live"],
  };

  const expected = expectedWorkspaceNames[config.env] || [];
  const nameMatch = expected.some((n) =>
    workspace.name.toLowerCase().includes(n)
  );

  if (!nameMatch && config.env !== "development") {
    console.warn(`WARNING: Workspace name "${workspace.name}" doesn't match expected ${config.env} pattern`);
    console.warn("Verify you're using the correct API key for this environment");
  }

  // List accounts to verify correct workspace
  const accounts = await client.accounts.list(5);
  console.log(`Accounts in workspace: ${accounts.length}`);
}

Step 5: Webhook Registration Per Environment

async function setupWebhooksForEnv() {
  const config = getConfig();

  const webhookBaseUrls: Record<string, string> = {
    development: "http://localhost:3000",
    staging: "https://staging-webhooks.yourapp.com",
    production: "https://webhooks.yourapp.com",
  };

  const baseUrl = webhookBaseUrls[config.env];

  // Clean up existing webhooks
  const existing = await client.webhooks.list();
  for (const w of existing) {
    if (w.name.startsWith(`[${config.env}]`)) {
      await client.webhooks.delete(w.id);
    }
  }

  // Register environment-specific webhooks
  const events = ["reply_received", "email_bounced", "lead_interested", "lead_meeting_booked"];

  for (const event of events) {
    await client.webhooks.create({
      name: `[${config.env}] ${event}`,
      target_hook_url: `${baseUrl}/webhooks/instantly`,
      event_type: event,
      headers: { "X-Webhook-Secret": config.webhookSecret },
    });
  }

  console.log(`[${config.env}] Registered ${events.length} webhooks -> ${baseUrl}`);
}

Promotion Workflow

Development (mock)  →  Staging (real API, test data)  →  Production (live)
    |                        |                               |
    |  Code changes          |  Integration test             |  Manual activation
    |  Unit tests            |  Small lead list (10)         |  Full lead list
    |  Mock server           |  Staging workspace            |  Production workspace
    |                        |  Webhook verification         |  Monitoring + alerts

Error Handling

ErrorCauseSolution
Wrong workspaceAPI key mismatchRun verifyWorkspaceIsolation()
Prod campaign auto-launchedMissing environment guardAdd if (env !== "production") check
Webhook pointing to wrong envStale webhook registrationRe-run setupWebhooksForEnv()
Staging data in productionCross-env contaminationUse separate workspaces with separate API keys

Resources

Next Steps

For observability and monitoring, see instantly-observability.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.