Claude-code-plugins-plus-skills instantly-security-basics
install
source · Clone the upstream repo
git clone https://github.com/jeremylongshore/claude-code-plugins-plus-skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/jeremylongshore/claude-code-plugins-plus-skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/plugins/saas-packs/instantly-pack/skills/instantly-security-basics" ~/.claude/skills/jeremylongshore-claude-code-plugins-plus-skills-instantly-security-basics && rm -rf "$T"
manifest:
plugins/saas-packs/instantly-pack/skills/instantly-security-basics/SKILL.mdsource content
Instantly Security Basics
Overview
Secure your Instantly.ai integration with scoped API keys, least-privilege access, secret management, webhook validation, and audit logging. Instantly API v2 uses Bearer token auth with granular scope-based permissions.
Prerequisites
- Instantly account with API access
- Understanding of environment variable management
- Access to Instantly dashboard Settings > Integrations
Instructions
Step 1: Least-Privilege API Key Scopes
Create separate API keys for different use cases with minimal required scopes.
// Key scopes follow the pattern: resource:action // resource = campaigns, accounts, leads, etc. // action = read, update, all // Read-only analytics dashboard // Scopes needed: campaigns:read, accounts:read const ANALYTICS_KEY_SCOPES = ["campaigns:read", "accounts:read"]; // Campaign automation bot // Scopes needed: campaigns:all, leads:all const AUTOMATION_KEY_SCOPES = ["campaigns:all", "leads:all"]; // Webhook-only integration // Scopes needed: leads:read (to look up lead data on events) const WEBHOOK_KEY_SCOPES = ["leads:read"]; // AVOID: all:all gives unrestricted access — dev/test only
| Use Case | Recommended Scopes | Risk Level |
|---|---|---|
| Analytics dashboard | , | Low |
| Lead import tool | | Medium |
| Campaign launcher | , , | High |
| Full automation | | Critical — dev only |
| Webhook handler | | Low |
Step 2: Secret Management
// NEVER hardcode API keys // BAD: const client = new InstantlyClient({ apiKey: "sk_live_abc123" }); // GOOD: environment variables const client = new InstantlyClient({ apiKey: process.env.INSTANTLY_API_KEY!, }); // BETTER: secret manager integration import { SecretManagerServiceClient } from "@google-cloud/secret-manager"; async function getApiKey(): Promise<string> { const client = new SecretManagerServiceClient(); const [version] = await client.accessSecretVersion({ name: "projects/my-project/secrets/instantly-api-key/versions/latest", }); return version.payload?.data?.toString() || ""; }
# .gitignore — always exclude secrets .env .env.* *.key
Step 3: API Key Rotation
// Instantly supports multiple API keys — rotate without downtime async function rotateApiKey() { const oldKey = process.env.INSTANTLY_API_KEY; // 1. Create new API key via dashboard (Settings > Integrations > API) // 2. Update secret manager / env vars with new key // 3. Deploy with new key // 4. Verify new key works const client = new InstantlyClient({ apiKey: process.env.INSTANTLY_API_KEY_NEW! }); await client.getCampaigns({ limit: 1 }); // test call // 5. Delete old key via API // GET /api/v2/api-keys to find the old key ID const keys = await client.request<Array<{ id: string; name: string }>>( "/api-keys" ); const oldKeyEntry = keys.find((k) => k.name === "old-key-name"); if (oldKeyEntry) { await client.request(`/api-keys/${oldKeyEntry.id}`, { method: "DELETE" }); console.log("Old API key deleted"); } } // API Key management endpoints: // POST /api/v2/api-keys — Create new key (name, scopes) // GET /api/v2/api-keys — List all keys // DELETE /api/v2/api-keys/{id} — Revoke a key
Step 4: Webhook Security
import express from "express"; const app = express(); app.use(express.json()); // Validate webhook requests app.post("/webhooks/instantly", (req, res) => { // Option 1: Verify via custom header set during webhook creation const expectedSecret = process.env.INSTANTLY_WEBHOOK_SECRET; const receivedSecret = req.headers["x-webhook-secret"]; if (expectedSecret && receivedSecret !== expectedSecret) { console.warn("Webhook auth failed: invalid secret"); return res.status(401).json({ error: "Unauthorized" }); } // Option 2: IP allowlisting (check Instantly's outbound IPs) // Option 3: Verify payload structure matches Instantly schema const { event_type, data } = req.body; if (!event_type || !data) { return res.status(400).json({ error: "Invalid payload" }); } // Process immediately, return 200 fast (Instantly retries 3x in 30s) res.status(200).json({ received: true }); // Async processing handleWebhookEvent(event_type, data).catch(console.error); }); // When creating webhooks, add custom auth headers async function createSecureWebhook(targetUrl: string) { return instantly("/webhooks", { method: "POST", body: JSON.stringify({ name: "Secure CRM Sync", target_hook_url: targetUrl, event_type: "reply_received", headers: { "X-Webhook-Secret": process.env.INSTANTLY_WEBHOOK_SECRET, Authorization: `Basic ${Buffer.from("user:pass").toString("base64")}`, }, }), }); }
Step 5: Audit Logging
// Instantly provides audit logs for workspace activity async function checkAuditLogs() { const logs = await instantly<Array<{ id: string; action: string; resource: string; timestamp_created: string; user: string; }>>("/audit-logs?limit=50"); console.log("Recent Audit Events:"); for (const log of logs) { console.log(` ${log.timestamp_created} | ${log.action} | ${log.resource} | ${log.user}`); } }
Step 6: Workspace Member Permissions
// Manage workspace members with role-based access async function listWorkspaceMembers() { const members = await instantly<Array<{ id: string; email: string; role: string; }>>("/workspace-members"); for (const m of members) { console.log(`${m.email}: ${m.role}`); } } // Remove a member async function removeMember(memberId: string) { await instantly(`/workspace-members/${memberId}`, { method: "DELETE" }); }
Security Checklist
- API keys stored in environment variables or secret manager
-
files listed in.env.gitignore - Each integration has its own scoped API key
- No
keys in productionall:all - Webhook endpoints validate authentication
- API key rotation schedule (quarterly recommended)
- Audit logs reviewed periodically
- Workspace members have minimum necessary roles
- Block list entries for competitor/internal domains
Error Handling
| Error | Cause | Solution |
|---|---|---|
after rotation | Old key still in use | Verify deployment picked up new key |
on scope-limited key | Missing required scope | Create new key with correct scopes |
Webhook | Secret mismatch | Check field in webhook config |
| Audit log empty | Plan doesn't include audit logs | Upgrade plan or check workspace settings |
Resources
Next Steps
For production readiness, see
instantly-prod-checklist.