Agent Skills: Instantly Deploy Integration

|

UncategorizedID: jeremylongshore/claude-code-plugins-plus-skills/instantly-deploy-integration

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/instantly-pack/skills/instantly-deploy-integration

Skill Files

Browse the full folder contents for instantly-deploy-integration.

Download Skill

Loading file tree…

plugins/saas-packs/instantly-pack/skills/instantly-deploy-integration/SKILL.md

Skill Metadata

Name
instantly-deploy-integration
Description
|

Instantly Deploy Integration

Overview

Deploy Instantly API v2 integrations — primarily webhook receivers and automation services — to cloud platforms. Instantly webhooks require a public HTTPS endpoint that responds within 30 seconds (3 retries on failure). This skill covers Vercel serverless functions, Google Cloud Run containers, and Fly.io deployments.

Prerequisites

  • Completed instantly-install-auth setup
  • Working Instantly integration tested locally (see instantly-local-dev-loop)
  • Cloud platform account (Vercel, GCP, or Fly.io)
  • Domain or HTTPS URL for webhook endpoint

Instructions

Option A: Vercel Serverless Functions

// api/webhooks/instantly.ts — Vercel serverless function
import type { VercelRequest, VercelResponse } from "@vercel/node";

export default async function handler(req: VercelRequest, res: VercelResponse) {
  if (req.method !== "POST") {
    return res.status(405).json({ error: "Method not allowed" });
  }

  // Validate webhook secret
  const secret = req.headers["x-webhook-secret"];
  if (secret !== process.env.INSTANTLY_WEBHOOK_SECRET) {
    return res.status(401).json({ error: "Unauthorized" });
  }

  const { event_type, data } = req.body;

  // Respond immediately — Instantly expects 2xx within 30s
  res.status(200).json({ received: true });

  // Process event asynchronously
  try {
    switch (event_type) {
      case "reply_received":
        await syncReplyToCRM(data);
        break;
      case "email_bounced":
        await handleBounce(data);
        break;
      case "lead_interested":
        await notifySalesTeam(data);
        break;
      case "lead_unsubscribed":
        await addToBlockList(data);
        break;
    }
  } catch (err) {
    console.error(`Webhook processing error: ${event_type}`, err);
  }
}

async function addToBlockList(data: { lead_email: string }) {
  await fetch("https://api.instantly.ai/api/v2/block-lists-entries", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.INSTANTLY_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ bl_value: data.lead_email }),
  });
}
# Deploy to Vercel
vercel env add INSTANTLY_API_KEY
vercel env add INSTANTLY_WEBHOOK_SECRET
vercel deploy --prod

Option B: Google Cloud Run

# Dockerfile
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --production=false
COPY . .
RUN npm run build

FROM node:20-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY package*.json ./
EXPOSE 8080
ENV PORT=8080
CMD ["node", "dist/server.js"]
// src/server.ts — Express server for Cloud Run
import express from "express";
import { instantly } from "./instantly";

const app = express();
app.use(express.json());

app.get("/health", (_, res) => res.json({ status: "ok" }));

app.post("/webhooks/instantly", async (req, res) => {
  const secret = req.headers["x-webhook-secret"];
  if (secret !== process.env.INSTANTLY_WEBHOOK_SECRET) {
    return res.status(401).json({ error: "Unauthorized" });
  }

  res.status(200).json({ received: true });

  const { event_type, data } = req.body;
  console.log(`Webhook: ${event_type}`, JSON.stringify(data).slice(0, 200));

  // Process based on event type
  if (event_type === "reply_received") {
    // Update lead interest status
    await instantly("/leads/update-interest-status", {
      method: "POST",
      body: JSON.stringify({
        lead_email: data.lead_email,
        campaign_id: data.campaign_id,
        interest_value: 1, // Interested
      }),
    });
  }
});

const PORT = process.env.PORT || 8080;
app.listen(PORT, () => console.log(`Listening on port ${PORT}`));
set -euo pipefail
# Deploy to Cloud Run
gcloud run deploy instantly-webhooks \
  --source . \
  --region us-central1 \
  --allow-unauthenticated \
  --set-env-vars "INSTANTLY_API_KEY=${INSTANTLY_API_KEY},INSTANTLY_WEBHOOK_SECRET=${INSTANTLY_WEBHOOK_SECRET}" \
  --min-instances 1 \
  --max-instances 10 \
  --memory 256Mi \
  --cpu 1

Option C: Fly.io

# fly.toml
app = "instantly-webhooks"
primary_region = "iad"

[build]
  dockerfile = "Dockerfile"

[http_service]
  internal_port = 8080
  force_https = true
  auto_stop_machines = true
  auto_start_machines = true
  min_machines_running = 1

[env]
  NODE_ENV = "production"
set -euo pipefail
fly launch --name instantly-webhooks
fly secrets set INSTANTLY_API_KEY="your-key" INSTANTLY_WEBHOOK_SECRET="your-secret"
fly deploy

Step 2: Register Webhook After Deployment

async function registerProductionWebhook(deployedUrl: string) {
  const webhook = await instantly<{ id: string; name: string }>("/webhooks", {
    method: "POST",
    body: JSON.stringify({
      name: "Production CRM Sync",
      target_hook_url: `${deployedUrl}/webhooks/instantly`,
      event_type: "all_events",
      headers: {
        "X-Webhook-Secret": process.env.INSTANTLY_WEBHOOK_SECRET,
      },
    }),
  });

  console.log(`Webhook registered: ${webhook.id}`);

  // Test the webhook
  await instantly(`/webhooks/${webhook.id}/test`, { method: "POST" });
  console.log("Test webhook sent — check your endpoint logs");
}

Step 3: Post-Deploy Verification

set -euo pipefail
DEPLOY_URL="https://instantly-webhooks-abc123.run.app"

# Health check
curl -s ${DEPLOY_URL}/health | jq .

# Test webhook endpoint
curl -X POST ${DEPLOY_URL}/webhooks/instantly \
  -H "Content-Type: application/json" \
  -H "X-Webhook-Secret: ${INSTANTLY_WEBHOOK_SECRET}" \
  -d '{"event_type":"reply_received","data":{"lead_email":"test@example.com"}}'

Error Handling

| Error | Cause | Solution | |-------|-------|----------| | Webhook delivery fails | Endpoint returns non-2xx | Ensure 200 response before async processing | | Cold start timeout | Serverless function too slow | Set min-instances=1 or use always-on | | Secret not available | Env var not set | Verify with fly secrets list or cloud console | | Webhook retries flooding | Processing takes >30s | Return 200 immediately, process async |

Resources

Next Steps

For webhook event handling patterns, see instantly-webhooks-events.