When agents use itUse this whenever the user asks how to connect Salesforce to another system, call an external API from Apex or Flow, expose Salesforce data to an app, sync records between systems, use Platform Events or CDC, authenticate an integration, or debug callout/API errors and limits. Also apply it when reviewing an integration design.
Install
Copilot (VS Code, Visual Studio, Copilot CLI and github.com) reads skills from the repository — commit them so the whole team gets them.
npx skills add AGCO-Global/org-skills --skill salesforce-integration -a github-copilot
# or with the org installer (adds .github/skills/salesforce-integration):
npx -y github:AGCO-Global/org-skills add skill salesforce-integration
Uses the open skills CLI. Works with Claude Code, Codex, Cursor, Copilot, Gemini CLI, OpenCode, Windsurf and 60+ others — it asks which agent to install into.
Installs the salesforce-integration-skills plugin, which bundles all Salesforce / Integration skills and keeps them updated.
/plugin marketplace add AGCO-Global/org-skills
/plugin install salesforce-integration-skills@org-skills
# or just this skill, in this repository:
npx skills add AGCO-Global/org-skills --skill salesforce-integration -a claude-code
Use Download .zip above, then upload it under Customize → Skills → Upload skill. Team and Enterprise admins can sync this repo as a plugin marketplace instead.
Installs into .agents/skills/, which Codex reads.
npx skills add AGCO-Global/org-skills --skill salesforce-integration -a codex
Installs into Cursor's skills folder.
npx skills add AGCO-Global/org-skills --skill salesforce-integration -a cursor
Installs into Gemini CLI's skills folder.
npx skills add AGCO-Global/org-skills --skill salesforce-integration -a gemini-cli
Any tool with rules, instructions or custom prompts: use Copy SKILL.md above and paste it in. It is plain Markdown.
Commands use your normal git sign-in to GitHub, so they work while the repository is private. Node.js 20+ required.
Skill contents
Salesforce Integration
Integrations fail at the edges: authentication that expires, limits nobody counted, retries that duplicate records, and events nobody idempotently handled. Design the failure path first; the happy path is the easy part.
1. Pick the pattern (Salesforce's canonical set)
Pattern
When
Salesforce mechanism
Request and Reply
UI or process needs an answer now
Apex callout (sync from LWC via imperative Apex; never from a trigger) / External Services in Flow
Fire and Forget
Notify another system, no response needed
Platform Event publish → external subscriber via Pub/Sub API; or Outbound Message (legacy)
Batch Data Synchronisation
Nightly/periodic bulk moves
External ETL calling Bulk API 2.0; or Batch Apex callouts for small sets
Remote Call-In
External system reads/writes Salesforce
REST / Composite / GraphQL API, Bulk API 2.0, or Apex REST for custom contracts
UI Update Based on Data Changes
Screen must reflect external/other-user changes
CDC or Platform Events consumed by lightning/empApi in LWC
Also decide: point-to-point vs middleware. More than ~3 systems, transformation logic, orchestration, or non-Salesforce consumers → put MuleSoft / an iPaaS / an event bus in the middle. Salesforce should not be the integration hub.
2. Authentication
Use case
Method
Server-to-server, no user
OAuth 2.0 JWT Bearer with a dedicated integration user + permission set; certificate rotated on schedule
Rules: one integration user per system (traceability, revocation), minimum permissions via permission sets, API-only user where possible, IP restrictions/relaxation deliberately configured, token refresh handled (Named Credentials do it for you), and secrets never in the browser bundle of an off-platform app — use a backend-for-frontend.
3. Inbound (external → Salesforce)
REST API for CRUD; Composite API (/composite, /composite/tree, /composite/batch) to do multiple operations in one call and one transaction; GraphQL API for shaped reads; Bulk API 2.0 for > ~2k records or long-running jobs.
Upsert on External Id — the single most important inbound decision. Idempotent, retry-safe, no duplicate hunting.
Custom Apex REST (@RestResource) only when the standard API can't express the contract (multi-object business operation). Version the URL (/v1/orders), validate the payload, return proper status codes and a stable error body, bulkify (accept arrays), enforce sharing/USER_MODE, and write tests with RestContext.
API request limits: 24-hour rolling limit by edition + licence; Bulk API has separate batch/record limits; check /limits and alert at 70 %. One misbehaving client can starve every integration — budget per client.
Concurrency: avoid many parallel writes to children of one parent (row locks, UNABLE_TO_LOCK_ROW); order loads parent → child; use Bulk API serial mode for skewed data.
Trigger/flow impact: inbound loads fire all automation. Provide a bypass (Custom Permission/Custom Setting checked by the trigger framework) for controlled migrations only.
4. Outbound (Salesforce → external)
Callouts from Apex via Named Credential: callout:My_Service/orders; set timeouts (max 120 s), handle CalloutException, check status codes, parse with typed wrapper classes (JSON.deserialize), log request/response Ids.
Never call out synchronously from a trigger — enqueue a Queueable (Database.AllowsCallouts) or publish a Platform Event and let a subscriber call out. Callouts must happen before DML in the same transaction ("uncommitted work pending" otherwise).
External Services: register an OpenAPI spec → invocable actions for Flow; great for simple request-reply from screen flows.
Retry with backoff for 5xx/timeouts; no retry for 4xx except 429 (honour Retry-After). Cap attempts; dead-letter to a custom object with the payload for replay. Queueable Transaction Finalizer re-enqueues on failure.
Idempotency keys on POSTs (Idempotency-Key header = Salesforce record Id + change stamp) so retries don't duplicate downstream.
Circuit breaker: a Custom Setting/Platform Cache flag that stops callouts to a failing system for N minutes; surface status to admins.
Limits: 100 callouts per transaction, 120 s cumulative timeout, 12 MB response in async; large payloads → paginate or let the external system pull via Bulk API.
5. Event-driven
Platform Events: define a contract (fields, version field, source system, correlation Id). Publish after commit (EventBus.publish behaviour; use Publish After Commit in Flow). High-volume events for scale; standard-volume rarely justified now.
Change Data Capture: publish record changes without code; subscribers get before/after-ish deltas; enable per object; watch entity allocation.
Subscribers: external via Pub/Sub API (gRPC, replay Id checkpointing — persist the last processed replay Id; the retention window is 72 h); internal via Apex event triggers or event-triggered flows.
At-least-once delivery means idempotent consumers: dedupe on EventUuid/correlation Id; make handlers safe to run twice.
Ordering is guaranteed only within a partition/subscription; don't build logic that assumes global order.
Error handling in Apex event triggers: EventBus.RetryableException for transient failures (max retries, then the trigger is suspended — monitor Event Manager); non-retryable → log and move on.
6. Data virtualisation and reference data
Salesforce Connect for read-mostly external data (ERP orders, inventory): OData 4.0 endpoint or custom Apex adapter; external objects support lookups/indirect lookups, reports (limited), and writable if the adapter supports it. Watch the per-hour callout limits and latency in list views.
For reference data that rarely changes, replicate on a schedule (cheaper, faster) rather than virtualise.
7. Middleware boundary (MuleSoft / iPaaS)
Salesforce exposes clean, stable contracts (standard API + a few Apex REST endpoints + Platform Events); middleware owns transformation, orchestration, protocol bridging, and fan-out.
Canonical model lives in middleware; Salesforce fields map to it via external Ids and clear ownership per field (system of record per attribute — document it).
MuleSoft Salesforce connector handles Bulk API, Pub/Sub, and Composite natively; still design idempotency and replay.
8. Observability
Every integration writes to a log object (or Nebula Logger / Event Monitoring): direction, system, correlation Id, record Ids, status, latency, error, payload excerpt (no secrets/PII).
Dashboards: failures per system per day, API usage vs limit, event backlog, dead-letter count. Alerts to the owning team.
Correlation Id end to end: generated at the origin, passed in headers/event fields, written to logs on both sides.
Setup → Event Manager, API Usage, Debug Logs (CALLOUT_*), Connected App usage for diagnosis.
9. Testing
Apex: HttpCalloutMock per external system with success/4xx/5xx/timeout responses; Test.setMock; tests for retry, dead-letter, and idempotency paths. Platform Events: Test.getEventBus().deliver(). Apex REST: set RestContext.request/response.
Contract tests against the external system's sandbox in a nightly job, not in unit tests.
Load test Bulk API loads in a Full sandbox with production-like volumes; measure automation cost per record.
Chaos: disable the Named Credential in a sandbox and confirm the circuit breaker and dead-letter behave.
10. Integration design document (deliver this)
# Integration: <Salesforce ↔ System>
## Purpose & pattern — which canonical pattern(s) and why
## Systems of record — per object/attribute
## Auth — flow, integration user, permission set, rotation
## Contracts — endpoints/events, payload schemas, versioning, external Ids
## Volume & limits — records/day, peak, API budget, bulk vs real-time
## Failure handling — retries, idempotency, dead-letter, circuit breaker, alerts
## Security & privacy — PII in payloads, encryption, IP restrictions
## Observability — correlation Id, logs, dashboards
## Test plan — mocks, contract tests, load tests
## Rollout — sandbox sequence, bypass flags, backfill, rollback
Anti-patterns to reject
Synchronous callout from a trigger; callout after DML in the same transaction.
Inserts keyed on Name / no external Id → duplicates on retry.
Secrets in Apex, Custom Settings, static resources, or an LWC bundle; one shared "integration" user for every system.
Polling Salesforce every minute instead of subscribing to CDC/Platform Events.
Retrying 4xx forever; no dead-letter; failures only in debug logs.
Salesforce as the integration hub transforming payloads for five other systems.
Bulk loads with automation left on and no skew handling.
Custom Apex REST duplicating what the standard REST/Composite API already does.
---
name: salesforce-integration
description: Design and implement integrations to and from Salesforce — choosing patterns (request-reply, fire-and-forget, batch sync, event-driven, data virtualisation, UI mashup), inbound APIs (REST, SOAP, Composite, GraphQL, Bulk API 2.0, custom Apex REST), outbound callouts with Named Credentials and External Services, Platform Events and Change Data Capture, Pub/Sub API, Salesforce Connect, MuleSoft/middleware boundaries, authentication (OAuth flows, JWT, External Client Apps, Connected Apps), API limits, idempotency, error handling and retries, and integration testing. Use this whenever the user asks how to connect Salesforce to another system, call an external API from Apex or Flow, expose Salesforce data to an app, sync records between systems, use Platform Events or CDC, authenticate an integration, or debug callout/API errors and limits. Also apply it when reviewing an integration design.
metadata:
technology: Salesforce
type: architecture
---
# Salesforce Integration
Integrations fail at the edges: authentication that expires, limits nobody counted, retries that duplicate records, and events nobody idempotently handled. Design the failure path first; the happy path is the easy part.
## 1. Pick the pattern (Salesforce's canonical set)
| Pattern | When | Salesforce mechanism |
|---|---|---|
| **Request and Reply** | UI or process needs an answer now | Apex callout (sync from LWC via imperative Apex; **never** from a trigger) / External Services in Flow |
| **Fire and Forget** | Notify another system, no response needed | **Platform Event** publish → external subscriber via **Pub/Sub API**; or Outbound Message (legacy) |
| **Batch Data Synchronisation** | Nightly/periodic bulk moves | External ETL calling **Bulk API 2.0**; or Batch Apex callouts for small sets |
| **Remote Call-In** | External system reads/writes Salesforce | **REST / Composite / GraphQL API**, Bulk API 2.0, or **Apex REST** for custom contracts |
| **UI Update Based on Data Changes** | Screen must reflect external/other-user changes | **CDC** or Platform Events consumed by `lightning/empApi` in LWC |
| **Data Virtualisation** | Show external data without copying it | **Salesforce Connect** (OData / custom Apex adapter) → external objects |
| **UI Mashup** | Embed external UI | Canvas / iframe with CSP Trusted Sites (limited) |
Also decide: **point-to-point vs middleware**. More than ~3 systems, transformation logic, orchestration, or non-Salesforce consumers → put MuleSoft / an iPaaS / an event bus in the middle. Salesforce should not be the integration hub.
## 2. Authentication
| Use case | Method |
|---|---|
| Server-to-server, no user | **OAuth 2.0 JWT Bearer** with a dedicated integration user + permission set; certificate rotated on schedule |
| Web/mobile app acting as a user | **OAuth 2.0 Authorization Code + PKCE** (public clients), refresh tokens with policies |
| Salesforce calling out | **Named Credential** (+ **External Credential** with OAuth/JWT/AWS Sig4/API key principals) — never store secrets in code, custom settings, or metadata |
| Org-to-org | Named Credential with OAuth to the other org; or Salesforce-to-Salesforce alternatives (events + APIs) |
| New apps | **External Client Apps** (successor to Connected Apps, packageable, better secret handling) |
Rules: one **integration user per system** (traceability, revocation), minimum permissions via permission sets, API-only user where possible, IP restrictions/relaxation deliberately configured, **token refresh handled** (Named Credentials do it for you), and secrets never in the browser bundle of an off-platform app — use a backend-for-frontend.
## 3. Inbound (external → Salesforce)
- **REST API** for CRUD; **Composite API** (`/composite`, `/composite/tree`, `/composite/batch`) to do multiple operations in one call and one transaction; **GraphQL API** for shaped reads; **Bulk API 2.0** for > ~2k records or long-running jobs.
- **Upsert on External Id** — the single most important inbound decision. Idempotent, retry-safe, no duplicate hunting.
- **Custom Apex REST** (`@RestResource`) only when the standard API can't express the contract (multi-object business operation). Version the URL (`/v1/orders`), validate the payload, return proper status codes and a stable error body, bulkify (accept arrays), enforce sharing/USER_MODE, and write tests with `RestContext`.
- **API request limits**: 24-hour rolling limit by edition + licence; Bulk API has separate batch/record limits; check `/limits` and alert at 70 %. One misbehaving client can starve every integration — budget per client.
- **Concurrency**: avoid many parallel writes to children of one parent (row locks, `UNABLE_TO_LOCK_ROW`); order loads parent → child; use Bulk API serial mode for skewed data.
- **Trigger/flow impact**: inbound loads fire all automation. Provide a **bypass** (Custom Permission/Custom Setting checked by the trigger framework) for controlled migrations only.
## 4. Outbound (Salesforce → external)
- **Callouts from Apex** via Named Credential: `callout:My_Service/orders`; set timeouts (max 120 s), handle `CalloutException`, check status codes, parse with typed wrapper classes (`JSON.deserialize`), log request/response Ids.
- **Never call out synchronously from a trigger** — enqueue a **Queueable** (`Database.AllowsCallouts`) or publish a Platform Event and let a subscriber call out. Callouts must happen before DML in the same transaction ("uncommitted work pending" otherwise).
- **External Services**: register an OpenAPI spec → invocable actions for Flow; great for simple request-reply from screen flows.
- **Retry with backoff** for 5xx/timeouts; **no retry** for 4xx except 429 (honour `Retry-After`). Cap attempts; dead-letter to a custom object with the payload for replay. Queueable **Transaction Finalizer** re-enqueues on failure.
- **Idempotency keys** on POSTs (`Idempotency-Key` header = Salesforce record Id + change stamp) so retries don't duplicate downstream.
- **Circuit breaker**: a Custom Setting/Platform Cache flag that stops callouts to a failing system for N minutes; surface status to admins.
- Limits: 100 callouts per transaction, 120 s cumulative timeout, 12 MB response in async; large payloads → paginate or let the external system pull via Bulk API.
## 5. Event-driven
- **Platform Events**: define a contract (fields, version field, source system, correlation Id). Publish **after commit** (`EventBus.publish` behaviour; use `Publish After Commit` in Flow). High-volume events for scale; standard-volume rarely justified now.
- **Change Data Capture**: publish record changes without code; subscribers get before/after-ish deltas; enable per object; watch entity allocation.
- **Subscribers**: external via **Pub/Sub API** (gRPC, replay Id checkpointing — persist the last processed replay Id; the retention window is 72 h); internal via Apex event triggers or event-triggered flows.
- **At-least-once delivery** means **idempotent consumers**: dedupe on `EventUuid`/correlation Id; make handlers safe to run twice.
- **Ordering** is guaranteed only within a partition/subscription; don't build logic that assumes global order.
- **Error handling in Apex event triggers**: `EventBus.RetryableException` for transient failures (max retries, then the trigger is suspended — monitor Event Manager); non-retryable → log and move on.
## 6. Data virtualisation and reference data
- **Salesforce Connect** for read-mostly external data (ERP orders, inventory): OData 4.0 endpoint or custom Apex adapter; external objects support lookups/indirect lookups, reports (limited), and writable if the adapter supports it. Watch the per-hour callout limits and latency in list views.
- For **reference data** that rarely changes, replicate on a schedule (cheaper, faster) rather than virtualise.
## 7. Middleware boundary (MuleSoft / iPaaS)
- Salesforce exposes **clean, stable contracts** (standard API + a few Apex REST endpoints + Platform Events); middleware owns **transformation, orchestration, protocol bridging, and fan-out**.
- Canonical model lives in middleware; Salesforce fields map to it via external Ids and clear ownership per field (system of record per attribute — document it).
- MuleSoft Salesforce connector handles Bulk API, Pub/Sub, and Composite natively; still design idempotency and replay.
## 8. Observability
- Every integration writes to a **log object** (or Nebula Logger / Event Monitoring): direction, system, correlation Id, record Ids, status, latency, error, payload excerpt (no secrets/PII).
- Dashboards: failures per system per day, API usage vs limit, event backlog, dead-letter count. Alerts to the owning team.
- **Correlation Id end to end**: generated at the origin, passed in headers/event fields, written to logs on both sides.
- Setup → **Event Manager**, **API Usage**, **Debug Logs** (`CALLOUT_*`), **Connected App usage** for diagnosis.
## 9. Testing
- Apex: `HttpCalloutMock` per external system with success/4xx/5xx/timeout responses; `Test.setMock`; tests for retry, dead-letter, and idempotency paths. Platform Events: `Test.getEventBus().deliver()`. Apex REST: set `RestContext.request`/`response`.
- Contract tests against the external system's sandbox in a nightly job, not in unit tests.
- Load test Bulk API loads in a Full sandbox with production-like volumes; measure automation cost per record.
- Chaos: disable the Named Credential in a sandbox and confirm the circuit breaker and dead-letter behave.
## 10. Integration design document (deliver this)
```
# Integration: <Salesforce ↔ System>
## Purpose & pattern — which canonical pattern(s) and why
## Systems of record — per object/attribute
## Auth — flow, integration user, permission set, rotation
## Contracts — endpoints/events, payload schemas, versioning, external Ids
## Volume & limits — records/day, peak, API budget, bulk vs real-time
## Failure handling — retries, idempotency, dead-letter, circuit breaker, alerts
## Security & privacy — PII in payloads, encryption, IP restrictions
## Observability — correlation Id, logs, dashboards
## Test plan — mocks, contract tests, load tests
## Rollout — sandbox sequence, bypass flags, backfill, rollback
```
## Anti-patterns to reject
- Synchronous callout from a trigger; callout after DML in the same transaction.
- Inserts keyed on Name / no external Id → duplicates on retry.
- Secrets in Apex, Custom Settings, static resources, or an LWC bundle; one shared "integration" user for every system.
- Polling Salesforce every minute instead of subscribing to CDC/Platform Events.
- Non-idempotent event subscribers; ignoring replay Ids.
- Retrying 4xx forever; no dead-letter; failures only in debug logs.
- Salesforce as the integration hub transforming payloads for five other systems.
- Bulk loads with automation left on and no skew handling.
- Custom Apex REST duplicating what the standard REST/Composite API already does.