Claude Code External API Integration and Webhooks
Claude Code’s ability to integrate with external APIs and webhooks transforms it from a coding assistant into a powerful automation platform. This guide explores how to connect Claude Code with external services, handle webhooks, and build sophisticated integration workflows.
Table of Contents
- Overview
- API Integration Fundamentals
- Webhook Integration
- Claude Hub - GitHub Integration
- Platform Integrations
- Building Custom Integrations
- Security and Authentication
- Advanced Patterns
- Real-World Examples
Overview
Claude Code’s external integration capabilities enable:
- Bidirectional Communication: Send data to and receive data from external services
- Event-Driven Workflows: Respond to webhooks and external triggers
- API Orchestration: Coordinate multiple APIs in complex workflows
- Real-Time Processing: Handle streaming data and real-time events
Integration Architecture
graph TB A[Claude Code] --> B[MCP Servers] B --> C[External APIs] D[Webhooks] --> A A --> E[GitHub/GitLab] A --> F[CI/CD Systems] A --> G[Cloud Services] A --> H[Custom Services]
API Integration Fundamentals
Direct API Calls via MCP
Claude Code can interact with any REST API through MCP servers:
// Example MCP server for API integration
import { Server } from "@modelcontextprotocol/sdk/server";
import axios from "axios";
class APIIntegrationServer {
private server: Server;
private apiClients: Map<string, any> = new Map();
constructor() {
this.server = new Server({
name: "api-integration-server",
version: "1.0.0",
});
this.setupAPIClients();
this.registerTools();
}
private setupAPIClients() {
// Configure multiple API clients
this.apiClients.set("github", {
client: axios.create({
baseURL: "https://api.github.com",
headers: {
Authorization: `token ${process.env.GITHUB_TOKEN}`,
Accept: "application/vnd.github.v3+json",
},
}),
});
this.apiClients.set("jira", {
client: axios.create({
baseURL: process.env.JIRA_BASE_URL,
auth: {
username: process.env.JIRA_EMAIL,
password: process.env.JIRA_API_TOKEN,
},
}),
});
this.apiClients.set("slack", {
client: axios.create({
baseURL: "https://slack.com/api",
headers: {
Authorization: `Bearer ${process.env.SLACK_TOKEN}`,
},
}),
});
}
private registerTools() {
this.server.setRequestHandler("tools/list", async () => ({
tools: [
{
name: "api_request",
description: "Make a request to an external API",
inputSchema: {
type: "object",
properties: {
service: {
type: "string",
enum: ["github", "jira", "slack", "custom"],
description: "The service to make a request to",
},
method: {
type: "string",
enum: ["GET", "POST", "PUT", "DELETE", "PATCH"],
description: "HTTP method",
},
endpoint: {
type: "string",
description: "API endpoint path",
},
data: {
type: "object",
description: "Request body data",
},
params: {
type: "object",
description: "Query parameters",
},
},
required: ["service", "method", "endpoint"],
},
},
],
}));
this.server.setRequestHandler("tools/call", async (request) => {
if (request.params.name === "api_request") {
return this.handleAPIRequest(request.params.arguments);
}
throw new Error("Unknown tool");
});
}
private async handleAPIRequest({
service,
method,
endpoint,
data,
params,
}) {
try {
const apiClient = this.apiClients.get(service);
if (!apiClient) {
throw new Error(`Unknown service: ${service}`);
}
const response = await apiClient.client.request({
method,
url: endpoint,
data,
params,
});
return {
content: [
{
type: "text",
text: JSON.stringify({
status: response.status,
data: response.data,
headers: response.headers,
}, null, 2),
},
],
};
} catch (error) {
return {
content: [
{
type: "text",
text: `API Error: ${error.message}\n${error.response?.data ? JSON.stringify(error.response.data, null, 2) : ""}`,
},
],
};
}
}
}Using Claude Code for API Integration
# Create a GitHub issue
claude -p "Create a GitHub issue in owner/repo with title 'Bug: Login fails' and describe the authentication error we're seeing"
# Query JIRA
claude -p "Find all open tickets assigned to me in JIRA and summarize their status"
# Send Slack notification
claude -p "Send a message to #dev-team channel saying the deployment was successful"Webhook Integration
Claude Hub - GitHub Webhook Service
Claude Hub is a webhook service that connects Claude Code to GitHub repositories:
# claude-hub configuration
version: '1.0'
services:
github:
webhook_url: https://claude-hub.example.com/webhooks/github
events:
- pull_request
- issues
- push
actions:
- analyze_code
- suggest_improvements
- answer_questionsKey features:
- @mention Support: Mention Claude in PRs or issues to trigger analysis
- Automatic Code Review: Claude reviews code changes automatically
- Issue Analysis: Get help understanding and solving reported issues
- Repository Q&A: Ask questions about the codebase directly in GitHub
Setting Up Webhook Receivers
// Webhook receiver server
import express from "express";
import crypto from "crypto";
import { Server as MCPServer } from "@modelcontextprotocol/sdk/server";
class WebhookReceiver {
private app: express.Application;
private mcpServer: MCPServer;
private webhookHandlers: Map<string, WebhookHandler> = new Map();
constructor() {
this.app = express();
this.app.use(express.json());
this.setupWebhookEndpoints();
this.registerHandlers();
}
private setupWebhookEndpoints() {
// GitHub webhook endpoint
this.app.post("/webhooks/github", async (req, res) => {
// Verify GitHub signature
const signature = req.headers["x-hub-signature-256"] as string;
if (!this.verifyGitHubSignature(req.body, signature)) {
return res.status(401).send("Invalid signature");
}
const event = req.headers["x-github-event"] as string;
await this.handleWebhook("github", event, req.body);
res.status(200).send("OK");
});
// Generic webhook endpoint
this.app.post("/webhooks/:service", async (req, res) => {
const { service } = req.params;
const authHeader = req.headers.authorization;
if (!this.verifyWebhookAuth(service, authHeader)) {
return res.status(401).send("Unauthorized");
}
await this.handleWebhook(service, "generic", req.body);
res.status(200).send("OK");
});
}
private verifyGitHubSignature(payload: any, signature: string): boolean {
const secret = process.env.GITHUB_WEBHOOK_SECRET;
const hmac = crypto.createHmac("sha256", secret);
const digest = "sha256=" + hmac.update(JSON.stringify(payload)).digest("hex");
return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(digest));
}
private async handleWebhook(
service: string,
event: string,
payload: any
) {
const handler = this.webhookHandlers.get(`${service}:${event}`);
if (!handler) {
console.warn(`No handler for ${service}:${event}`);
return;
}
try {
await handler(payload);
} catch (error) {
console.error(`Error handling webhook ${service}:${event}:`, error);
// Could send to error tracking service
}
}
private registerHandlers() {
// GitHub PR handler
this.webhookHandlers.set("github:pull_request", async (payload) => {
if (payload.action === "opened" || payload.action === "synchronize") {
await this.analyzePullRequest(payload);
}
});
// GitHub issue handler
this.webhookHandlers.set("github:issues", async (payload) => {
if (payload.action === "opened") {
await this.analyzeIssue(payload);
}
});
// Custom webhook handlers
this.webhookHandlers.set("custom:deploy", async (payload) => {
await this.handleDeploymentWebhook(payload);
});
}
private async analyzePullRequest(payload: any) {
const pr = payload.pull_request;
// Use Claude to analyze the PR
const analysis = await this.mcpServer.callTool("analyze_code", {
repo: payload.repository.full_name,
pr_number: pr.number,
diff_url: pr.diff_url,
});
// Post analysis as comment
await this.postGitHubComment(
payload.repository.full_name,
pr.number,
analysis.content
);
}
private async analyzeIssue(payload: any) {
const issue = payload.issue;
// Check if Claude was mentioned
if (!issue.body.includes("@claude")) {
return;
}
// Extract question/request
const request = this.extractClaudeRequest(issue.body);
// Use Claude to respond
const response = await this.mcpServer.callTool("answer_question", {
context: "github_issue",
repo: payload.repository.full_name,
question: request,
});
// Post response as comment
await this.postGitHubComment(
payload.repository.full_name,
issue.number,
response.content
);
}
listen(port: number) {
this.app.listen(port, () => {
console.log(`Webhook receiver listening on port ${port}`);
});
}
}Claude Hub - GitHub Integration
Claude Hub provides seamless GitHub integration:
Setup
# 1. Install Claude Hub
npm install -g claude-hub
# 2. Configure GitHub webhook
claude-hub init --github-token YOUR_TOKEN --webhook-secret YOUR_SECRET
# 3. Add webhook to repository
# Go to Settings > Webhooks > Add webhook
# URL: https://your-domain.com/webhooks/github
# Content type: application/json
# Secret: YOUR_SECRET
# Events: Pull requests, Issues, PushesUsage Examples
Pull Request Review
<!-- In a pull request comment -->
@claude Can you review this PR for security vulnerabilities and suggest improvements?Claude will:
- Analyze the code changes
- Check for security issues
- Suggest improvements
- Post a detailed review comment
Issue Analysis
<!-- In a GitHub issue -->
@claude I'm getting a "TypeError: Cannot read property 'id' of undefined" in the authentication module. Can you help me understand what's causing this?Claude will:
- Analyze the codebase
- Locate the authentication module
- Identify potential causes
- Provide debugging steps
Advanced GitHub Integration
// Advanced GitHub integration features
class GitHubIntegration {
private octokit: Octokit;
private claude: ClaudeClient;
async handlePRMention(
owner: string,
repo: string,
prNumber: number,
comment: string
) {
// Parse the request from comment
const request = this.parseClaudeRequest(comment);
// Get PR details
const { data: pr } = await this.octokit.pulls.get({
owner,
repo,
pull_number: prNumber,
});
// Get changed files
const { data: files } = await this.octokit.pulls.listFiles({
owner,
repo,
pull_number: prNumber,
});
// Analyze based on request type
let response: string;
switch (request.type) {
case "security_review":
response = await this.performSecurityReview(files);
break;
case "performance_analysis":
response = await this.analyzePerformance(files);
break;
case "test_coverage":
response = await this.checkTestCoverage(files, pr);
break;
case "documentation":
response = await this.generateDocumentation(files);
break;
default:
response = await this.generalCodeReview(files, request.query);
}
// Post response
await this.octokit.issues.createComment({
owner,
repo,
issue_number: prNumber,
body: response,
});
}
private async performSecurityReview(files: any[]): Promise<string> {
const vulnerabilities = [];
for (const file of files) {
const content = await this.getFileContent(file);
// Use Claude to analyze security
const analysis = await this.claude.analyze({
prompt: `Analyze this code for security vulnerabilities:
File: ${file.filename}
\`\`\`${this.getLanguage(file.filename)}
${content}
\`\`\`
Look for:
- SQL injection
- XSS vulnerabilities
- Authentication issues
- Data exposure
- Dependency vulnerabilities`,
context: {
file: file.filename,
changes: file.patch,
},
});
if (analysis.vulnerabilities.length > 0) {
vulnerabilities.push({
file: file.filename,
issues: analysis.vulnerabilities,
});
}
}
return this.formatSecurityReport(vulnerabilities);
}
}Platform Integrations
Zapier Integration
Connect Claude Code with 5000+ apps via Zapier:
// Zapier webhook handler
class ZapierIntegration {
async handleZapierWebhook(payload: any) {
const { trigger, data, return_url } = payload;
// Process with Claude
const result = await this.processWithClaude(trigger, data);
// Send result back to Zapier
await axios.post(return_url, {
status: "success",
data: result,
});
}
async processWithClaude(trigger: string, data: any) {
switch (trigger) {
case "analyze_document":
return await this.claude.callTool("analyze_text", {
text: data.document_content,
analysis_type: data.analysis_type,
});
case "generate_code":
return await this.claude.callTool("generate_code", {
description: data.description,
language: data.language,
framework: data.framework,
});
case "answer_question":
return await this.claude.callTool("answer", {
question: data.question,
context: data.context,
});
}
}
}n8n Integration
n8n provides visual workflow automation with Claude:
// n8n Claude node implementation
export class ClaudeNode implements INodeType {
description: INodeTypeDescription = {
displayName: "Claude Code",
name: "claudeCode",
icon: "file:claude.svg",
group: ["transform"],
version: 1,
description: "Use Claude Code for AI-powered automation",
defaults: {
name: "Claude Code",
},
inputs: ["main"],
outputs: ["main"],
properties: [
{
displayName: "Operation",
name: "operation",
type: "options",
options: [
{
name: "Analyze Code",
value: "analyzeCode",
},
{
name: "Generate Code",
value: "generateCode",
},
{
name: "Process Data",
value: "processData",
},
{
name: "Custom Prompt",
value: "customPrompt",
},
],
default: "customPrompt",
},
{
displayName: "Prompt",
name: "prompt",
type: "string",
typeOptions: {
alwaysOpenEditWindow: true,
},
default: "",
description: "The prompt to send to Claude",
},
],
};
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
const items = this.getInputData();
const returnData: INodeExecutionData[] = [];
for (let i = 0; i < items.length; i++) {
const operation = this.getNodeParameter("operation", i) as string;
const prompt = this.getNodeParameter("prompt", i) as string;
// Get input data
const inputData = items[i].json;
// Process with Claude
const result = await this.processWithClaude(
operation,
prompt,
inputData
);
returnData.push({
json: {
...inputData,
claude_response: result,
},
});
}
return [returnData];
}
private async processWithClaude(
operation: string,
prompt: string,
data: any
) {
const claudeClient = this.getClaudeClient();
switch (operation) {
case "analyzeCode":
return await claudeClient.analyze({
code: data.code,
language: data.language,
analysis_type: prompt,
});
case "generateCode":
return await claudeClient.generate({
description: prompt,
context: data,
});
case "processData":
return await claudeClient.process({
data: data,
instructions: prompt,
});
case "customPrompt":
return await claudeClient.prompt({
prompt: prompt,
context: data,
});
}
}
}Building Custom Integrations
REST API Integration Pattern
// Generic REST API integration
class RESTAPIIntegration {
private baseURL: string;
private headers: Record<string, string>;
private rateLimiter: RateLimiter;
constructor(config: APIConfig) {
this.baseURL = config.baseURL;
this.headers = {
"Content-Type": "application/json",
...config.headers,
};
this.rateLimiter = new RateLimiter(config.rateLimit);
}
async request(
method: string,
endpoint: string,
options?: RequestOptions
): Promise<any> {
await this.rateLimiter.acquire();
const url = new URL(endpoint, this.baseURL);
// Add query parameters
if (options?.params) {
Object.entries(options.params).forEach(([key, value]) => {
url.searchParams.append(key, String(value));
});
}
const response = await fetch(url.toString(), {
method,
headers: {
...this.headers,
...options?.headers,
},
body: options?.body ? JSON.stringify(options.body) : undefined,
});
if (!response.ok) {
throw new APIError(
`API request failed: ${response.status} ${response.statusText}`,
response.status,
await response.text()
);
}
return await response.json();
}
// Convenience methods
async get(endpoint: string, options?: RequestOptions) {
return this.request("GET", endpoint, options);
}
async post(endpoint: string, body: any, options?: RequestOptions) {
return this.request("POST", endpoint, { ...options, body });
}
async put(endpoint: string, body: any, options?: RequestOptions) {
return this.request("PUT", endpoint, { ...options, body });
}
async delete(endpoint: string, options?: RequestOptions) {
return this.request("DELETE", endpoint, options);
}
}
// Usage example
const api = new RESTAPIIntegration({
baseURL: "https://api.example.com",
headers: {
"X-API-Key": process.env.API_KEY,
},
rateLimit: {
requests: 100,
per: 60000, // per minute
},
});
// Use in Claude Code MCP server
server.setRequestHandler("tools/call", async (request) => {
if (request.params.name === "api_call") {
const { endpoint, method, body } = request.params.arguments;
try {
const result = await api.request(method, endpoint, { body });
return {
content: [
{
type: "text",
text: JSON.stringify(result, null, 2),
},
],
};
} catch (error) {
return {
content: [
{
type: "text",
text: `API Error: ${error.message}`,
},
],
};
}
}
});GraphQL Integration
// GraphQL API integration
import { GraphQLClient } from "graphql-request";
class GraphQLIntegration {
private client: GraphQLClient;
constructor(endpoint: string, options?: any) {
this.client = new GraphQLClient(endpoint, options);
}
async query(query: string, variables?: any): Promise<any> {
try {
return await this.client.request(query, variables);
} catch (error) {
throw new GraphQLError(error.message, error.response);
}
}
async mutation(mutation: string, variables?: any): Promise<any> {
return this.query(mutation, variables);
}
// Helper for building dynamic queries
buildQuery(
operation: string,
fields: string[],
filters?: Record<string, any>
): string {
const filterStr = filters
? `(${Object.entries(filters)
.map(([key, value]) => `${key}: "${value}"`)
.join(", ")})`
: "";
return `
query {
${operation}${filterStr} {
${fields.join("\n ")}
}
}
`;
}
}
// Usage in MCP server
const graphql = new GraphQLIntegration("https://api.example.com/graphql", {
headers: {
Authorization: `Bearer ${process.env.API_TOKEN}`,
},
});
server.setRequestHandler("tools/list", async () => ({
tools: [
{
name: "graphql_query",
description: "Execute a GraphQL query",
inputSchema: {
type: "object",
properties: {
query: {
type: "string",
description: "GraphQL query",
},
variables: {
type: "object",
description: "Query variables",
},
},
required: ["query"],
},
},
],
}));Security and Authentication
OAuth 2.0 Integration
// OAuth 2.0 flow implementation
class OAuth2Integration {
private clientId: string;
private clientSecret: string;
private redirectUri: string;
private tokenStore: TokenStore;
constructor(config: OAuth2Config) {
this.clientId = config.clientId;
this.clientSecret = config.clientSecret;
this.redirectUri = config.redirectUri;
this.tokenStore = new TokenStore();
}
getAuthorizationUrl(state: string, scopes: string[]): string {
const params = new URLSearchParams({
client_id: this.clientId,
redirect_uri: this.redirectUri,
response_type: "code",
scope: scopes.join(" "),
state: state,
});
return `${this.config.authorizationEndpoint}?${params.toString()}`;
}
async exchangeCodeForToken(code: string): Promise<TokenResponse> {
const response = await fetch(this.config.tokenEndpoint, {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
Authorization: `Basic ${Buffer.from(
`${this.clientId}:${this.clientSecret}`
).toString("base64")}`,
},
body: new URLSearchParams({
grant_type: "authorization_code",
code: code,
redirect_uri: this.redirectUri,
}),
});
if (!response.ok) {
throw new Error("Failed to exchange code for token");
}
const tokens = await response.json();
await this.tokenStore.save(tokens);
return tokens;
}
async refreshToken(refreshToken: string): Promise<TokenResponse> {
const response = await fetch(this.config.tokenEndpoint, {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
Authorization: `Basic ${Buffer.from(
`${this.clientId}:${this.clientSecret}`
).toString("base64")}`,
},
body: new URLSearchParams({
grant_type: "refresh_token",
refresh_token: refreshToken,
}),
});
if (!response.ok) {
throw new Error("Failed to refresh token");
}
const tokens = await response.json();
await this.tokenStore.save(tokens);
return tokens;
}
async getValidToken(): Promise<string> {
const tokens = await this.tokenStore.get();
if (!tokens) {
throw new Error("No tokens available");
}
// Check if token is expired
if (this.isTokenExpired(tokens)) {
const refreshed = await this.refreshToken(tokens.refresh_token);
return refreshed.access_token;
}
return tokens.access_token;
}
private isTokenExpired(tokens: TokenResponse): boolean {
if (!tokens.expires_at) {
return false;
}
return Date.now() >= tokens.expires_at;
}
}API Key Management
// Secure API key management
class APIKeyManager {
private keys: Map<string, APIKey> = new Map();
private encryptionKey: string;
constructor() {
this.encryptionKey = process.env.ENCRYPTION_KEY || this.generateKey();
this.loadKeys();
}
async addKey(
service: string,
key: string,
options?: APIKeyOptions
): Promise<void> {
const encryptedKey = await this.encrypt(key);
this.keys.set(service, {
encrypted: encryptedKey,
createdAt: Date.now(),
expiresAt: options?.expiresAt,
permissions: options?.permissions || [],
rateLimit: options?.rateLimit,
});
await this.saveKeys();
}
async getKey(service: string): Promise<string | null> {
const keyData = this.keys.get(service);
if (!keyData) {
return null;
}
// Check expiration
if (keyData.expiresAt && Date.now() > keyData.expiresAt) {
this.keys.delete(service);
await this.saveKeys();
return null;
}
return await this.decrypt(keyData.encrypted);
}
async rotateKey(service: string, newKey: string): Promise<void> {
const existing = this.keys.get(service);
if (existing) {
// Keep old key for grace period
existing.rotatedAt = Date.now();
existing.gracePeriodEnds = Date.now() + 24 * 60 * 60 * 1000; // 24 hours
this.keys.set(`${service}_old`, existing);
}
await this.addKey(service, newKey);
}
private async encrypt(text: string): Promise<string> {
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv(
"aes-256-gcm",
Buffer.from(this.encryptionKey, "hex"),
iv
);
let encrypted = cipher.update(text, "utf8", "hex");
encrypted += cipher.final("hex");
const authTag = cipher.getAuthTag();
return iv.toString("hex") + ":" + authTag.toString("hex") + ":" + encrypted;
}
private async decrypt(text: string): Promise<string> {
const parts = text.split(":");
const iv = Buffer.from(parts[0], "hex");
const authTag = Buffer.from(parts[1], "hex");
const encrypted = parts[2];
const decipher = crypto.createDecipheriv(
"aes-256-gcm",
Buffer.from(this.encryptionKey, "hex"),
iv
);
decipher.setAuthTag(authTag);
let decrypted = decipher.update(encrypted, "hex", "utf8");
decrypted += decipher.final("utf8");
return decrypted;
}
}Advanced Patterns
Event-Driven Architecture
// Event-driven API integration
import { EventEmitter } from "events";
class EventDrivenAPIIntegration extends EventEmitter {
private eventSources: Map<string, EventSource> = new Map();
private webhookServer: WebhookServer;
constructor() {
super();
this.webhookServer = new WebhookServer();
this.setupEventHandlers();
}
// Server-Sent Events (SSE) integration
connectToSSE(name: string, url: string, options?: any) {
const eventSource = new EventSource(url, options);
eventSource.onmessage = (event) => {
this.emit("sse:message", {
source: name,
data: JSON.parse(event.data),
});
};
eventSource.onerror = (error) => {
this.emit("sse:error", {
source: name,
error: error,
});
// Reconnect logic
setTimeout(() => {
this.reconnectSSE(name, url, options);
}, 5000);
};
this.eventSources.set(name, eventSource);
}
// WebSocket integration
connectToWebSocket(name: string, url: string) {
const ws = new WebSocket(url);
ws.on("open", () => {
this.emit("ws:connected", { source: name });
});
ws.on("message", (data) => {
this.emit("ws:message", {
source: name,
data: JSON.parse(data.toString()),
});
});
ws.on("error", (error) => {
this.emit("ws:error", {
source: name,
error: error,
});
});
ws.on("close", () => {
this.emit("ws:disconnected", { source: name });
// Reconnect logic
setTimeout(() => {
this.connectToWebSocket(name, url);
}, 5000);
});
this.webSockets.set(name, ws);
}
// Process events with Claude
private setupEventHandlers() {
this.on("sse:message", async (event) => {
await this.processEventWithClaude("sse", event);
});
this.on("ws:message", async (event) => {
await this.processEventWithClaude("websocket", event);
});
this.on("webhook:received", async (event) => {
await this.processEventWithClaude("webhook", event);
});
}
private async processEventWithClaude(type: string, event: any) {
const response = await this.claude.process({
type: "event",
source: type,
data: event,
prompt: `Process this ${type} event and determine appropriate action`,
});
// Execute recommended action
if (response.action) {
await this.executeAction(response.action);
}
}
}Batch Processing and Queuing
// Batch API processing with queue
import Queue from "bull";
class BatchAPIProcessor {
private queue: Queue.Queue;
private batchSize: number;
private batchTimeout: number;
private currentBatch: any[] = [];
private batchTimer: NodeJS.Timeout | null = null;
constructor(config: BatchConfig) {
this.queue = new Queue("api-batch-processing", {
redis: config.redis,
});
this.batchSize = config.batchSize || 100;
this.batchTimeout = config.batchTimeout || 5000;
this.setupWorkers();
}
async addToBatch(item: any): Promise<void> {
this.currentBatch.push(item);
if (this.currentBatch.length >= this.batchSize) {
await this.processBatch();
} else if (!this.batchTimer) {
this.batchTimer = setTimeout(() => {
this.processBatch();
}, this.batchTimeout);
}
}
private async processBatch(): Promise<void> {
if (this.currentBatch.length === 0) {
return;
}
const batch = [...this.currentBatch];
this.currentBatch = [];
if (this.batchTimer) {
clearTimeout(this.batchTimer);
this.batchTimer = null;
}
await this.queue.add("process-batch", { items: batch });
}
private setupWorkers() {
this.queue.process("process-batch", async (job) => {
const { items } = job.data;
try {
// Process with Claude for intelligent batching
const optimizedBatches = await this.optimizeBatches(items);
// Execute API calls in parallel batches
const results = await Promise.all(
optimizedBatches.map((batch) => this.executeBatch(batch))
);
return {
processed: items.length,
results: results.flat(),
};
} catch (error) {
throw new Error(`Batch processing failed: ${error.message}`);
}
});
}
private async optimizeBatches(items: any[]): Promise<any[][]> {
// Use Claude to intelligently group items
const grouping = await this.claude.analyze({
prompt: "Group these items for optimal API batch processing based on their properties",
items: items,
constraints: {
maxBatchSize: this.batchSize,
optimizeFor: ["rate_limits", "data_locality", "dependency_order"],
},
});
return grouping.batches;
}
private async executeBatch(batch: any[]): Promise<any[]> {
// Execute batch API call
const response = await this.api.post("/batch", {
operations: batch.map((item) => ({
method: item.method,
path: item.path,
body: item.body,
})),
});
return response.results;
}
}Retry and Circuit Breaker
// Advanced retry and circuit breaker pattern
class ResilientAPIClient {
private circuitBreaker: CircuitBreaker;
private retryPolicy: RetryPolicy;
constructor(config: ResilientConfig) {
this.circuitBreaker = new CircuitBreaker({
threshold: config.failureThreshold || 5,
timeout: config.circuitTimeout || 60000,
resetTimeout: config.resetTimeout || 30000,
});
this.retryPolicy = new RetryPolicy({
maxAttempts: config.maxRetries || 3,
backoff: config.backoffStrategy || "exponential",
baseDelay: config.baseDelay || 1000,
});
}
async request(
method: string,
url: string,
options?: any
): Promise<any> {
return this.circuitBreaker.execute(async () => {
return this.retryPolicy.execute(async () => {
const response = await this.makeRequest(method, url, options);
// Check if response indicates we should back off
if (response.status === 429 || response.status >= 500) {
throw new RetryableError(
`Server error: ${response.status}`,
response
);
}
return response;
});
});
}
private async makeRequest(
method: string,
url: string,
options?: any
): Promise<any> {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 30000);
try {
const response = await fetch(url, {
method,
...options,
signal: controller.signal,
});
if (!response.ok) {
throw new APIError(
`Request failed: ${response.status}`,
response.status,
await response.text()
);
}
return await response.json();
} finally {
clearTimeout(timeout);
}
}
}
class CircuitBreaker {
private state: "closed" | "open" | "half-open" = "closed";
private failures: number = 0;
private lastFailureTime: number = 0;
private successCount: number = 0;
async execute<T>(fn: () => Promise<T>): Promise<T> {
if (this.state === "open") {
if (Date.now() - this.lastFailureTime > this.config.resetTimeout) {
this.state = "half-open";
} else {
throw new Error("Circuit breaker is open");
}
}
try {
const result = await fn();
this.onSuccess();
return result;
} catch (error) {
this.onFailure();
throw error;
}
}
private onSuccess() {
this.failures = 0;
if (this.state === "half-open") {
this.successCount++;
if (this.successCount >= this.config.successThreshold) {
this.state = "closed";
this.successCount = 0;
}
}
}
private onFailure() {
this.failures++;
this.lastFailureTime = Date.now();
if (this.failures >= this.config.threshold) {
this.state = "open";
}
if (this.state === "half-open") {
this.state = "open";
this.successCount = 0;
}
}
}Real-World Examples
1. Multi-Service Orchestration
// Orchestrate multiple services for complex workflows
class MultiServiceOrchestrator {
private services: Map<string, ServiceClient> = new Map();
private claude: ClaudeClient;
async executeWorkflow(
workflowName: string,
context: any
): Promise<WorkflowResult> {
// Use Claude to plan the workflow
const plan = await this.claude.planWorkflow({
workflow: workflowName,
context: context,
availableServices: Array.from(this.services.keys()),
});
const results: any[] = [];
const state: any = { ...context };
for (const step of plan.steps) {
try {
const result = await this.executeStep(step, state);
results.push(result);
// Update state for next step
Object.assign(state, result.outputs);
// Check if we should continue
if (result.shouldStop) {
break;
}
} catch (error) {
// Use Claude to determine recovery strategy
const recovery = await this.claude.determineRecovery({
step: step,
error: error,
state: state,
previousResults: results,
});
if (recovery.action === "retry") {
// Retry with modifications
const retryResult = await this.executeStep(
{ ...step, ...recovery.modifications },
state
);
results.push(retryResult);
} else if (recovery.action === "skip") {
// Skip this step
continue;
} else {
// Abort workflow
throw new WorkflowError(
`Workflow failed at step ${step.name}`,
error,
results
);
}
}
}
return {
success: true,
results: results,
finalState: state,
};
}
private async executeStep(step: WorkflowStep, state: any): Promise<any> {
const service = this.services.get(step.service);
if (!service) {
throw new Error(`Unknown service: ${step.service}`);
}
// Transform inputs using Claude if needed
const inputs = step.requiresTransformation
? await this.claude.transformData({
source: state,
targetSchema: step.inputSchema,
mappingHints: step.mappingHints,
})
: this.extractInputs(step.inputs, state);
// Execute service call
const result = await service.execute(step.operation, inputs);
// Validate result
if (step.validation) {
const isValid = await this.validateResult(result, step.validation);
if (!isValid) {
throw new ValidationError(
`Step ${step.name} produced invalid output`
);
}
}
return {
step: step.name,
outputs: result,
shouldStop: step.stopCondition
? this.evaluateCondition(step.stopCondition, result)
: false,
};
}
}
// Example usage
const orchestrator = new MultiServiceOrchestrator();
// Add services
orchestrator.addService("github", githubClient);
orchestrator.addService("jira", jiraClient);
orchestrator.addService("slack", slackClient);
orchestrator.addService("jenkins", jenkinsClient);
// Execute complex workflow
const result = await orchestrator.executeWorkflow("release-deployment", {
version: "2.1.0",
environment: "production",
approvers: ["alice", "bob"],
});2. Intelligent API Gateway
// AI-powered API gateway
class IntelligentAPIGateway {
private routes: Map<string, APIRoute> = new Map();
private claude: ClaudeClient;
private cache: CacheManager;
private metrics: MetricsCollector;
async handleRequest(request: Request): Promise<Response> {
const startTime = Date.now();
try {
// Use Claude to understand the request intent
const intent = await this.analyzeIntent(request);
// Check cache if applicable
if (intent.cacheable) {
const cached = await this.cache.get(intent.cacheKey);
if (cached) {
this.metrics.recordHit("cache");
return new Response(cached, { status: 200 });
}
}
// Route to appropriate service(s)
const result = await this.routeRequest(intent, request);
// Post-process if needed
const processed = intent.requiresProcessing
? await this.processResponse(result, intent)
: result;
// Cache if applicable
if (intent.cacheable) {
await this.cache.set(
intent.cacheKey,
processed,
intent.cacheTTL
);
}
// Record metrics
this.metrics.record({
route: intent.route,
duration: Date.now() - startTime,
status: "success",
});
return new Response(processed, {
status: 200,
headers: {
"Content-Type": "application/json",
"X-Processing-Time": `${Date.now() - startTime}ms`,
},
});
} catch (error) {
// Use Claude for intelligent error handling
const errorResponse = await this.handleError(error, request);
this.metrics.record({
route: request.url,
duration: Date.now() - startTime,
status: "error",
error: error.message,
});
return errorResponse;
}
}
private async analyzeIntent(request: Request): Promise<RequestIntent> {
const body = await request.text();
const headers = Object.fromEntries(request.headers);
const analysis = await this.claude.analyze({
prompt: `Analyze this API request and determine:
1. The intended operation
2. Which service(s) to route to
3. If the response can be cached
4. Any required transformations
5. Security considerations`,
context: {
method: request.method,
url: request.url,
headers: headers,
body: body,
},
});
return {
operation: analysis.operation,
route: analysis.route,
services: analysis.services,
cacheable: analysis.cacheable,
cacheKey: analysis.cacheKey,
cacheTTL: analysis.cacheTTL || 300,
requiresProcessing: analysis.requiresProcessing,
security: analysis.security,
};
}
private async routeRequest(
intent: RequestIntent,
request: Request
): Promise<any> {
if (intent.services.length === 1) {
// Single service call
const route = this.routes.get(intent.services[0]);
return await route.handler(request);
} else {
// Multi-service orchestration
const results = await Promise.all(
intent.services.map(async (service) => {
const route = this.routes.get(service);
return {
service: service,
result: await route.handler(request),
};
})
);
// Use Claude to merge results
return await this.claude.mergeResults({
results: results,
operation: intent.operation,
mergeStrategy: intent.mergeStrategy,
});
}
}
private async processResponse(
response: any,
intent: RequestIntent
): Promise<any> {
return await this.claude.process({
prompt: `Process this API response according to the intent`,
data: response,
intent: intent,
transformations: intent.transformations,
});
}
private async handleError(
error: Error,
request: Request
): Promise<Response> {
const errorAnalysis = await this.claude.analyzeError({
error: error.message,
stack: error.stack,
request: {
method: request.method,
url: request.url,
},
});
// Generate user-friendly error response
const errorResponse = {
error: {
code: errorAnalysis.code,
message: errorAnalysis.userMessage,
details: errorAnalysis.details,
},
suggestions: errorAnalysis.suggestions,
documentation: errorAnalysis.documentationLinks,
};
return new Response(JSON.stringify(errorResponse), {
status: errorAnalysis.statusCode || 500,
headers: {
"Content-Type": "application/json",
},
});
}
}Conclusion
Claude Code’s external API integration and webhook capabilities transform it into a powerful hub for automation and orchestration. By leveraging these features, you can:
- Automate Complex Workflows: Orchestrate multiple services with intelligent decision-making
- Respond to Events: Handle webhooks and real-time events with AI-powered processing
- Build Intelligent Integrations: Create smart connectors that understand context and intent
- Scale Automation: Process large volumes of API calls efficiently with batching and queuing
The key to successful integration is:
- Start Simple: Begin with basic integrations and gradually add complexity
- Focus on Security: Always implement proper authentication and validation
- Monitor Performance: Track metrics and optimize based on real usage
- Leverage Claude’s Intelligence: Use AI to handle edge cases and complex decisions
- Build Resilient Systems: Implement retry logic, circuit breakers, and error handling
As the ecosystem continues to evolve, Claude Code’s integration capabilities will expand, enabling even more sophisticated automation scenarios. Stay connected with the community and keep exploring new ways to connect Claude Code with your tools and services.