---
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.
