Home/Resources/Integration
Integration12 min read

Salesforce integration patterns that survive production.

Request-reply, fire-and-forget, events, bulk — and the idempotency, error-queue and API-budget discipline that decides whether they still work in month three.

AJ

Aleš Jeník — Salesforce Enterprise Architect · 12+ years building revenue systems on Salesforce · Clouderia

Key takeaways
  • Pick the pattern by who waits: request-reply when a human needs the answer now, fire-and-forget and events when nobody should.
  • Platform Events signal business intent; Change Data Capture replicates record changes — they are not interchangeable.
  • Assume at-least-once delivery everywhere: idempotency keys and external-ID upserts make retries safe by construction.
  • Failures belong in an owned, replayable error queue — not in log files.
  • Budget API and event allocations per integration and alert before the ceiling, not after.
  • Middleware is justified by fan-out, transformation and throttling needs — never by default.

The happy path is not the pattern

Most Salesforce integrations work on the day they are demoed. The interesting question is what happens in month three: the downstream API times out at 2 a.m., a retry storm doubles every invoice, the daily API allocation runs out an hour before the finance close. The pattern you chose — and the operational plumbing around it — decides whether those are non-events or incidents. This article covers the patterns we rely on in our Salesforce integration practice: when each applies, how it fails, and what keeps it honest under load.

Request-reply: pay for the answer now

Request-reply is a synchronous callout. Salesforce asks, waits, and acts on the answer inside the same interaction. It is the right pattern when the answer changes what happens next while a human is watching — a credit decision before a quote is submitted, address validation at data entry, a real-time price or availability check.

The costs are equally clear. The downstream system's latency becomes your page's latency, and its outage becomes your outage. Salesforce caps the number of callouts per transaction and the cumulative time they may consume (check current documentation for the exact figures), and a callout cannot follow uncommitted DML in the same transaction, which forces you to sequence work deliberately.

In production, request-reply survives on three disciplines: timeouts tuned to what the user will tolerate rather than the vendor's default, a circuit breaker that stops hammering a struggling dependency, and a designed degraded mode — save the record, flag it for later verification, tell the user what happened. If you cannot articulate the degraded mode, the design is not finished.

Fire-and-forget: decouple the user from the delivery

Fire-and-forget fits when Salesforce is the source of intent and the other system only needs to hear about it eventually — provisioning requests, notifications, pushing a won opportunity into fulfilment. The user saves; an asynchronous job delivers.

The naive version is a callout inside a queueable with no record of the attempt, and it is where data disappears silently. The production version is an outbox: write the intent to a custom object in the same transaction as the business change, then let an async worker deliver it, mark it acknowledged, and retry with backoff when it is not. The outbox gives you an audit trail, replay, and somewhere to hang monitoring. Fire-and-forget without an outbox is really fire-and-hope.

Event-driven: Platform Events and Change Data Capture

Event-driven integration inverts the relationship: Salesforce publishes, subscribers decide what to do. Two mechanisms matter. Platform Events carry intentional business signals with a payload you design — order placed, payment failed. Change Data Capture streams record-level changes for replication: warehouse sync, cache invalidation, keeping an ERP mirror current. They are complements, not alternatives.

Both deliver at-least-once, so every subscriber must be idempotent. Both flow through the event bus with replay IDs — a subscriber that goes down can resume from its last position, but only within the retention window, so a subscriber that stays down too long needs a reconciliation path. And neither gives the publisher per-record feedback: if one consumer chokes on an event, the publishing transaction never hears about it. That makes events wrong for flows that need confirmation, and exactly right for fan-out — one publish, many consumers, no coupling. Event allocations for publishing and delivery are a real constraint at scale; budget them the way you budget API calls. We lean on this pattern heavily in order orchestration, where one state change fans out to billing, provisioning and notifications.

1 · REQUEST-REPLY Salesforce External API call · tight timeout response — the user waits 2 · FIRE-AND-FORGET (OUTBOX) Salesforce Outbox queue Downstream same txn async · retry ack 3 · EVENT-DRIVEN (PLATFORM EVENTS / CDC) Salesforce publisher Event bus replay IDs · retention ERP Data warehouse Middleware / iPaaS publish at-least-once fan-out · idempotent subscribers
Three delivery patterns, three failure surfaces: latency, silent loss, and duplicate delivery.

Batch and bulk: the unfashionable workhorse

Not everything deserves to be real-time. A scheduled Bulk API job that moves a large volume of records and reconciles counts afterwards is easier to build, cheaper to run and simpler to operate than most streams. Batch is the honest answer for initial loads and migrations, analytics feeds, and financial or master-data flows that already follow a business calendar — the nightly cost-centre sync that anchors most Salesforce–SAP integrations is a classic example.

Its failure modes are specific: record-lock contention when parallel batches touch children of the same parent account, partial failures buried in a results file nobody reads, and jobs that quietly overrun their window. Design around them — partition and sort input to avoid lock collisions, feed the job's per-record results into the same error queue as everything else, and reconcile with counts and sums rather than trust.

Idempotency and retries: assume every message arrives twice

Every delivery guarantee you can actually afford is at-least-once. Networks retry, workers crash after the call succeeds but before the acknowledgement lands, humans press replay. The systems that survive this are the ones where a duplicate is a no-op by construction.

Inbound to Salesforce, that means a stable external identifier on every payload and upsert against an external ID field instead of insert. Outbound, it means sending an idempotency key the receiver can deduplicate on — agreed with the partner up front, not bolted on after the first double payment. Retries themselves need exponential backoff with jitter, a capped attempt count, and a hard distinction between retryable failures (timeouts, 5xx, row locks) and non-retryable ones (validation, authorization) — retrying a 400 forever is a popular way to burn an API budget. In money-moving flows none of this is optional; our guide to core banking API integration treats it as the foundation.

Error queues: failures as first-class records

When the retries are exhausted, the message must land somewhere a human will find it. The production answer is a dead-letter queue built as ordinary records: payload, error detail, attempt count, correlation ID, timestamps and an owner. That structure buys you three things logs cannot provide — replay tooling (fix the data, requeue the record, never re-key by hand), business-readable alerting ("twelve orders have not reached billing, the oldest is 40 minutes old"), and accountability, because an unowned error queue becomes a landfill within a quarter. Measure the age of the oldest unresolved item, not just the count; a stable count of slowly rotting failures is worse than a spike that gets cleared.

API limit budgeting: capacity planning, not incident response

Salesforce grants an org-wide API request allocation over a rolling 24-hour window, shared by every integration, tool and script that touches the org. One runaway consumer starves the rest — and the failure usually surfaces in whichever integration matters most that day, not the one that misbehaved.

Treat the allocation as a budget. Give each integration its own integration user so consumption is attributable and revocable. Estimate calls per business event times peak volume, allocate headroom, and alert at consumption thresholds long before the ceiling. Then reduce spend structurally: bulkify with composite and Bulk APIs, cache reference data instead of re-fetching it, and replace polling with event subscriptions — polling is the single most common budget leak we find in architecture reviews. Exact allocations vary by edition and licensing; check current Salesforce documentation, and treat purchased add-on capacity as the last resort after the polling is gone.

When middleware earns its keep

A single integration between Salesforce and one well-behaved API rarely justifies an iPaaS or ESB. Middleware adds a hop, a bill and an operational surface someone has to staff; point-to-point with two excellent endpoints is often the better engineering decision, and saying so builds more trust than selling a platform.

Middleware earns its keep under specific pressures: several systems consuming the same data, where point-to-point decays into an N×M mesh; transformation and orchestration logic that would otherwise accumulate inside Salesforce as integration-flavoured Apex — the same ownership question we examine in when to write Apex; protocol mediation to legacy systems that speak SOAP, files or message queues; and queueing or throttling to protect a fragile downstream that cannot absorb Salesforce-scale bursts.

The honest test: without middleware, would you end up rebuilding queueing, retries, transformation and monitoring inside Salesforce? If yes, adopt the platform and let it do that job. If no, keep the architecture simple and spend the money on making both endpoints excellent. We build and run both shapes, and the pattern decision belongs in the architecture phase — not in the procurement one.

Frequently asked questions

Should I use Platform Events or Change Data Capture?

Use Platform Events for intentional business signals — an order was placed, a payment failed — where you control the payload and the moment of publication. Use Change Data Capture when downstream systems need to mirror Salesforce record changes and field-level detail matters more than business meaning. Many architectures use both: CDC for replication into a warehouse, Platform Events for process triggers.

Do we need MuleSoft or an iPaaS to integrate Salesforce?

Not automatically. One or two point-to-point integrations with a well-designed API on the other side are often simpler and cheaper without middleware. Middleware earns its keep when several systems consume the same data, when transformation and orchestration logic would otherwise live inside Salesforce, or when you need queueing and throttling to protect fragile downstream systems. Buy it for those reasons, not by default.

How do we prevent duplicate records when integrations retry?

Design for at-least-once delivery. Give every inbound write a stable external identifier and use upsert against an external ID field instead of insert. For outbound calls, send an idempotency key the receiving system can deduplicate on. Retries then become safe by construction, and you can retry transient failures aggressively without creating duplicate orders, payments or accounts.

What happens when we hit Salesforce API limits?

Salesforce rejects further API requests until the rolling 24-hour window frees capacity, which can take unrelated integrations down with the one that misbehaved. Prevent it by giving each integration its own integration user and API budget, bulkifying calls, caching reference data, and alerting well before consumption approaches the ceiling. Exact allocations depend on edition and licenses — check current Salesforce documentation.

Is real-time integration always better than batch?

No. Real-time costs more to build, monitor and keep reliable, and many business processes do not need it. Financial postings, inventory snapshots and analytics feeds are often better served by scheduled bulk jobs with reconciliation checks. Reserve event-driven and request-reply patterns for flows where seconds genuinely change a decision, and let everything else move in honest, verifiable batches.

Related reading

Salesforce integration services — how we design and build it Integrating Salesforce with core banking APIs safely Salesforce–SAP integration: patterns and pitfalls Order orchestration for telco on Salesforce Bank feed reconciliation in Salesforce
Need this in your org?

Bring the hard part to a senior Salesforce engineering team.

Talk to the builders about this pattern