Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Configure Apollo.io multi-environment setup. Use when setting up development, staging, and production environments, or managing multiple Apollo configurations. Trigger with phrases like "apollo environments", "apollo staging", "apollo dev prod", "apollo multi-tenant", "apollo env config".
.claude/skills/jeremylongshore-apollo-multi-env-setup/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 28% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 70% | 0% |
| case-10 | ✗→✓ | ▲ Improved | 59% | 0% |
| case-11 | ✗→✓ | ▲ Improved | -11% | 0% |
| case-12 | ✗→✓ | ▲ Improved | 226% | 0% |
Configure Apollo.io for development, staging, and production with isolated API keys, environment-specific rate limits, feature gating, and Kubernetes-native secret management. Apollo provides sandbox tokens for testing that return dummy data without consuming credits.
typescript// src/config/apollo-config.ts import { z } from 'zod'; const EnvironmentSchema = z.enum(['development', 'staging', 'production']); const ApolloEnvConfig = z.object({ environment: EnvironmentSchema, apiKey: z.string().min(10), baseUrl: z.string().url().default('https://api.apollo.io/api/v1'), isSandbox: z.boolean().default(false), rateLimit: z.object({ maxPerMinute: z.number().min(1), concurrency: z.number().min(1).max(20), }), features: z.object({ enrichment: z.boolean(), sequences: z.boolean(), deals: z.boolean(), bulkEnrichment: z.boolean(), }), credits: z.object({ dailyBudget: z.number(), alertThreshold: z.number(), // percentage }), logging: z.object({ level: z.enum(['debug', 'info', 'warn', 'error']), redactPII: z.boolean(), }), }); type ApolloEnvConfig = z.infer<typeof ApolloEnvConfig>;
typescriptconst configs: Record<string, ApolloEnvConfig> = { development: { environment: 'development', apiKey: process.env.APOLLO_SANDBOX_KEY ?? process.env.APOLLO_API_KEY_DEV!, baseUrl: 'https://api.apollo.io/api/v1', isSandbox: true, rateLimit: { maxPerMinute: 20, concurrency: 2 }, features: { enrichment: true, sequences: false, deals: false, bulkEnrichment: false }, credits: { dailyBudget: 10, alertThreshold: 80 }, logging: { level: 'debug', redactPII: false }, }, staging: { environment: 'staging', apiKey: process.env.APOLLO_API_KEY_STAGING!, baseUrl: 'https://api.apollo.io/api/v1', isSandbox: false, rateLimit: { maxPerMinute: 50, concurrency: 5 }, features: { enrichment: true, sequences: true, deals: true, bulkEnrichment: true }, credits: { dailyBudget: 50, alertThreshold: 70 }, logging: { level: 'info', redactPII: true }, }, production: { environment: 'production', apiKey: process.env.APOLLO_API_KEY_PROD!, baseUrl: 'https://api.apollo.io/api/v1', isSandbox: false, rateLimit: { maxPerMinute: 100, concurrency: 10 }, features: { enrichment: true, sequences: true, deals: true, bulkEnrichment: true }, credits: { dailyBudget: 500, alertThreshold: 90 }, logging: { level: 'warn', redactPII: true }, }, };
typescript// src/config/client-factory.ts import axios, { AxiosInstance } from 'axios'; export function createEnvClient(config: ApolloEnvConfig): AxiosInstance { const validated = ApolloEnvConfig.parse(config); const client = axios.create({ baseURL: validated.baseUrl, headers: { 'Content-Type': 'application/json', 'x-api-key': validated.apiKey }, timeout: validated.environment === 'development' ? 30_000 : 15_000, }); // Feature gate: block disabled endpoints client.interceptors.request.use((req) => { if (req.url?.includes('/people/match') && !validated.features.enrichment) throw new Error(`Enrichment disabled in ${validated.environment}`); if (req.url?.includes('/emailer_campaigns') && !validated.features.sequences) throw new Error(`Sequences disabled in ${validated.environment}`); if (req.url?.includes('/opportunities') && !validated.features.deals) throw new Error(`Deals disabled in ${validated.environment}`); if (req.url?.includes('/people/bulk_match') && !validated.features.bulkEnrichment) throw new Error(`Bulk enrichment disabled in ${validated.environment}`); return req; }); // Debug logging in dev if (validated.logging.level === 'debug') { client.interceptors.request.use((req) => { console.log(`[${validated.environment}] ${req.method?.toUpperCase()} ${req.url}`); return req; }); } return client; } export function getClient(): AxiosInstance { const env = process.env.NODE_ENV ?? 'development'; return createEnvClient(configs[env] ?? configs.development); }
yaml# k8s/dev/apollo-secret.yaml apiVersion: v1 kind: Secret metadata: name: apollo-credentials namespace: sales-dev type: Opaque stringData: APOLLO_SANDBOX_KEY: "sandbox-token-here" APOLLO_API_KEY_DEV: "dev-key-here" --- # k8s/prod/apollo-secret.yaml apiVersion: v1 kind: Secret metadata: name: apollo-credentials namespace: sales-prod type: Opaque stringData: APOLLO_API_KEY_PROD: "master-key-here"
typescript// src/scripts/verify-envs.ts async function verifyAllEnvironments() { for (const [env, config] of Object.entries(configs)) { try { const client = createEnvClient(config); const { data } = await client.get('/auth/health'); const isMaster = await testMasterAccess(client); console.log(`${env}: ${data.is_logged_in ? 'OK' : 'FAIL'} (${isMaster ? 'master' : 'standard'} key, sandbox: ${config.isSandbox})`); } catch (err: any) { console.error(`${env}: FAIL — ${err.message}`); } } } async function testMasterAccess(client: AxiosInstance): Promise<boolean> { try { await client.post('/contacts/search', { per_page: 1 }); return true; } catch { return false; } }
| Issue | Resolution | |-------|------------| | Feature disabled | Client throws descriptive error identifying which env blocked it | | Wrong environment | Check NODE_ENV, client falls back to development | | Missing API key | Zod throws with clear validation error | | Sandbox returning dummy data | Expected in development — use staging for real data testing |
Proceed to apollo-observability for monitoring setup.
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 22,018 | 18,594 | -16% | 1 | 1 | 0% | 5,183 | 6,623 | +28% | 0 | 0 | — |
case-02 | fail→pass | 12,747 | 12,203 | -4% | 1 | 1 | 0% | 2,874 | 4,881 | +70% | 0 | 0 | — |
case-03 | fail→fail | 19,693 | 17,328 | -12% | 1 | 1 | 0% | 4,475 | 6,216 | +39% | 0 | 0 | — |
case-04 | fail→fail | 6,453 | 6,399 | -1% | 1 | 1 | 0% | 1,372 | 3,274 | +139% | 0 | 0 | — |
case-05 | pass→pass | 10,730 | 9,281 | -14% | 1 | 1 | 0% | 2,449 | 3,996 | +63% | 0 | 0 | — |
case-06 | pass→pass | 13,923 | 13,727 | -1% | 1 | 1 | 0% | 3,199 | 5,059 | +58% | 0 | 0 | — |
case-07 | pass→pass | 12,268 | 8,888 | -28% | 1 | 1 | 0% | 2,640 | 3,819 | +45% | 0 | 0 | — |
case-08 | fail→fail | 11,904 | 6,658 | -44% | 1 | 1 | 0% | 2,302 | 3,237 | +41% | 0 | 0 | — |
case-09 | fail→fail | 10,845 | 6,672 | -38% | 1 | 1 | 0% | 2,085 | 3,184 | +53% | 0 | 0 | — |
case-10 | fail→pass | 8,910 | 3,162 | -65% | 1 | 1 | 0% | 1,611 | 2,569 | +59% | 0 | 0 | — |
case-11 | fail→pass | 14,431 | 3,701 | -74% | 1 | 1 | 0% | 2,949 | 2,629 | -11% | 0 | 0 | — |
case-12 | fail→pass | 3,882 | 2,028 | -48% | 1 | 1 | 0% | 707 | 2,307 | +226% | 0 | 0 | — |
case-13 | pass→pass | 4,748 | 2,165 | -54% | 1 | 1 | 0% | 613 | 2,294 | +274% | 0 | 0 | — |
case-14 | fail→pass | 10,523 | 5,246 | -50% | 1 | 1 | 0% | 2,097 | 2,956 | +41% | 0 | 0 | — |
case-15 | fail→pass | 9,622 | 3,023 | -69% | 1 | 1 | 0% | 1,847 | 2,408 | +30% | 0 | 0 | — |
case-16 | fail→fail | 10,336 | 8,369 | -19% | 1 | 1 | 0% | 1,890 | 3,580 | +89% | 0 | 0 | — |
case-17 | fail→fail | 12,197 | 7,250 | -41% | 1 | 1 | 0% | 2,195 | 3,300 | +50% | 0 | 0 | — |
case-18 | pass→pass | 11,818 | 11,387 | -4% | 1 | 1 | 0% | 2,055 | 3,992 | +94% | 0 | 0 | — |
case-19 | pass→pass | 6,253 | 4,183 | -33% | 1 | 1 | 0% | 1,008 | 2,718 | +170% | 0 | 0 | — |
case-20 | pass→pass | 10,132 | 1,752 | -83% | 1 | 1 | 0% | 1,675 | 2,203 | +32% | 0 | 0 | — |
case-21 | pass→pass | 8,349 | 2,246 | -73% | 1 | 1 | 0% | 1,481 | 2,340 | +58% | 0 | 0 | — |
case-22 | fail→pass | 3,413 | 2,026 | -41% | 1 | 1 | 0% | 456 | 2,258 | +395% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +36 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.