Claude-code-plugins-plus-skills salesforce-webhooks-events
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/salesforce-pack/skills/salesforce-webhooks-events" ~/.claude/skills/jeremylongshore-claude-code-plugins-plus-skills-salesforce-webhooks-events && rm -rf "$T"
manifest:
plugins/saas-packs/salesforce-pack/skills/salesforce-webhooks-events/SKILL.mdsource content
Salesforce Webhooks & Events
Overview
Salesforce doesn't use traditional webhooks. Instead, it offers Platform Events, Change Data Capture (CDC), and Outbound Messages for real-time data flow. All use the CometD (Bayeux) streaming protocol via jsforce.
Prerequisites
- jsforce installed with connection configured
- Platform Events or CDC enabled in your org
- Understanding of publish/subscribe patterns
- Express.js for Outbound Message endpoints
Event Mechanism Comparison
| Mechanism | Direction | Use Case | Retention |
|---|---|---|---|
| Platform Events | Bi-directional | Custom event bus | 72 hours |
| Change Data Capture (CDC) | Salesforce → External | Record change notifications | 3 days |
| Outbound Messages | Salesforce → External | Workflow-triggered HTTP POST | Until confirmed |
| Streaming API (PushTopics) | Salesforce → External | SOQL-based subscriptions | No replay |
Instructions
Step 1: Subscribe to Change Data Capture (CDC)
import jsforce from 'jsforce'; const conn = new jsforce.Connection({ loginUrl: process.env.SF_LOGIN_URL, }); await conn.login(process.env.SF_USERNAME!, process.env.SF_PASSWORD! + process.env.SF_SECURITY_TOKEN!); // Subscribe to Account changes // CDC channel format: /data/AccountChangeEvent const subscription = conn.streaming.topic('/data/AccountChangeEvent').subscribe((message) => { const header = message.payload.ChangeEventHeader; console.log('Change Type:', header.changeType); // CREATE, UPDATE, DELETE, UNDELETE console.log('Record IDs:', header.recordIds); console.log('Changed Fields:', header.changedFields); console.log('User ID:', header.commitUser); // Access changed field values if (header.changeType === 'UPDATE') { console.log('New values:', message.payload); // Only changed fields are populated in the payload } }); // Enable CDC for objects in Setup: // Setup > Integrations > Change Data Capture > Select Objects
Step 2: Publish and Subscribe to Platform Events
// Define a Platform Event in Salesforce: // Setup > Platform Events > New Platform Event // Example: Order_Status__e with fields: // - Order_Id__c (Text) // - Status__c (Text) // - Amount__c (Number) // Publish a Platform Event via API await conn.sobject('Order_Status__e').create({ Order_Id__c: 'ORD-12345', Status__c: 'Shipped', Amount__c: 499.99, }); // Subscribe to Platform Events const eventSub = conn.streaming.topic('/event/Order_Status__e').subscribe((message) => { console.log('Event received:', { orderId: message.payload.Order_Id__c, status: message.payload.Status__c, amount: message.payload.Amount__c, replayId: message.event.replayId, }); }); // Use replayId to resume from a specific point (-1 = new only, -2 = all available) conn.streaming.topic('/event/Order_Status__e', { replayId: -2 }).subscribe((message) => { // Receives all stored events (up to 72 hours) });
Step 3: Handle Outbound Messages (SOAP-based)
// Outbound Messages are sent by Salesforce Workflow Rules or Flows // They are SOAP XML posts to your endpoint import express from 'express'; import { parseString } from 'xml2js'; const app = express(); app.use(express.text({ type: 'text/xml' })); app.post('/salesforce/outbound-message', (req, res) => { parseString(req.body, (err, result) => { if (err) { console.error('XML parse error:', err); return res.status(400).send('Invalid XML'); } // Extract notification data const notification = result['soapenv:Envelope']['soapenv:Body'][0] ['notifications'][0]['Notification'][0]; const sobject = notification['sObject'][0]; console.log('Record ID:', sobject['sf:Id'][0]); console.log('Object Type:', sobject.$['xsi:type']); // Respond with acknowledgment (required!) res.type('text/xml').send(`<?xml version="1.0" encoding="UTF-8"?> <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:out="http://soap.sforce.com/2005/09/outbound"> <soapenv:Body> <out:notificationsResponse> <out:Ack>true</out:Ack> </out:notificationsResponse> </soapenv:Body> </soapenv:Envelope>`); }); });
Step 4: Robust Event Processing
// Idempotent event handler with replay ID tracking import { Redis } from 'ioredis'; const redis = new Redis(process.env.REDIS_URL); async function processEvent(message: any): Promise<void> { const replayId = message.event.replayId; const eventKey = `sf:event:${replayId}`; // Check if already processed (idempotency) if (await redis.exists(eventKey)) { console.log(`Event ${replayId} already processed, skipping`); return; } try { // Process the event const changeType = message.payload.ChangeEventHeader?.changeType; const recordIds = message.payload.ChangeEventHeader?.recordIds || []; switch (changeType) { case 'CREATE': await handleRecordCreated(recordIds, message.payload); break; case 'UPDATE': await handleRecordUpdated(recordIds, message.payload); break; case 'DELETE': await handleRecordDeleted(recordIds); break; } // Mark as processed with 7-day TTL await redis.set(eventKey, '1', 'EX', 86400 * 7); // Save replay ID for resume on restart await redis.set('sf:last-replay-id', replayId.toString()); } catch (error) { console.error(`Failed to process event ${replayId}:`, error); throw error; // Let retry logic handle it } }
Output
- CDC subscription for real-time record change notifications
- Platform Event publishing and subscribing
- Outbound Message endpoint with SOAP acknowledgment
- Idempotent event processing with replay ID tracking
Error Handling
| Issue | Cause | Solution |
|---|---|---|
| Object not selected for CDC | Setup > Change Data Capture > select objects |
| Platform Event doesn't exist | Create in Setup > Platform Events |
| Missed events | Client disconnected | Use to resume from last position |
| Duplicate processing | No idempotency check | Track processed values in Redis |
| Outbound Message retry | Ack not sent | Return XML response |
Resources
- Change Data Capture Developer Guide
- Platform Events Developer Guide
- Streaming API
- Outbound Messaging
Next Steps
For performance optimization, see
salesforce-performance-tuning.