Back to insights

Integration Architecture

Your Salesforce object model is not an integration contract

JSBC Labs8 min read

CRUD is a capability—not a contract

A new partner needs to create customers, so the project grants API access and documents Account, Contact and several custom fields. The integration works quickly. Then a field becomes required, a record type changes, automation adds a validation rule and the partner starts receiving errors that only a Salesforce administrator can interpret. The API did not suddenly become unreliable; the team exposed an internal implementation as an external promise.

Salesforce REST API intentionally provides flexible programmatic access to objects, records, queries and metadata. That is an excellent platform capability. An enterprise integration contract answers a different set of questions: what business operation is requested, which facts are accepted, what completion means, how identity is resolved, which errors are stable and how consumers survive change.

Decide whether record access is actually enough

Direct sObject REST access is appropriate when a controlled consumer understands the Salesforce schema, shares its release cadence and genuinely needs generic record operations. Administrative tools, governed data pipelines and tightly owned internal applications can benefit from this simplicity. Adding a custom service layer to every integration would create cost without automatically creating a better design.

The boundary changes when several consumers perform the same business process, when external teams cannot track Salesforce metadata, or when an operation spans records and rules. ‘Register a distributor,’ ‘submit a claim’ and ‘activate a subscription’ are business commands. Representing them as a sequence of Account, Contact and custom-object updates pushes orchestration, validation and recovery into every caller.

Expose intent, not table-shaped payloads

Design the request around the decision the caller is allowed to make. A customer-registration contract can accept a legal name, trading name, verified identifiers and contact channels without revealing record types, lookup IDs or helper fields. Salesforce can translate that stable language into its current object model and return a business identifier, outcome and next action.

This is not an argument for a universal canonical model. Large canonical schemas often become another source of coupling. Keep contracts capability-sized and map only what the use case needs. Reuse well-governed concepts such as customer identifiers and addresses, but allow separate operations to express different validation, authority and timing requirements.

Make identity independent of Salesforce record IDs

A Salesforce ID identifies a record inside an org; it is rarely the right enterprise identity. Sandbox refreshes, migrations, mergers and multi-org designs make that dependency visible. Consumers should send a durable business key or source-system key that Salesforce stores in a governed external ID field.

Salesforce provides sObject Rows by External ID for create-or-update operations based on an external ID value. That mechanism is useful, but the key still needs ownership rules: who issues it, whether it can change, how duplicates are handled and which system may correct it. A technical upsert does not resolve an ambiguous customer identity or decide whether two legal entities should merge.

Keep Salesforce automation behind the boundary

A PATCH request can trigger validation rules, record-triggered Flow, Apex, assignment and sharing behaviour. Those mechanisms are part of Salesforce's transaction, but their internal shape should not leak into the consumer contract. Returning raw validation messages makes downstream applications dependent on administrator wording and exposes details that may change without an API review.

Translate platform failures into a small, documented error model: invalid input, duplicate request, unauthorised operation, conflict, temporary failure and internal failure. Include a correlation ID and field-level details only where they are safe and actionable. Preserve the original diagnostic evidence inside the operational boundary so support teams can investigate without forcing callers to parse Salesforce-specific exceptions.

Define transaction and partial-success semantics

Salesforce Composite resources can reduce round trips, and Composite Graph can execute related record operations with graph-level all-or-none behaviour. That is valuable when the required operation is still record-oriented. It does not decide the correct business transaction or make remote systems part of the same commit.

State explicitly whether the API is atomic, whether individual items can succeed, and how the caller learns which outcomes require correction. Keep transactional groups small enough to diagnose and retry. When work continues asynchronously, return an accepted response with a durable request identifier and status resource; do not label an enqueued operation as completed.

Idempotency belongs in the public design

Networks fail ambiguously. A caller can time out after Salesforce commits and legitimately send the same request again. External IDs can help prevent duplicate records, but they do not necessarily prevent duplicate side effects such as notifications, approvals or downstream orders.

Require a client-generated idempotency key for commands with consequential effects. Store the key with a request fingerprint and final result, reject conflicting reuse, and return the previous outcome for a genuine replay. Define the retention period and scope. Idempotency is a business-operation guarantee, not merely an upsert technique.

Security is more than a successful OAuth flow

Salesforce documents OAuth 2.0 through external client apps or connected apps as the authorisation path for REST resources, and recommends external client apps for new configurations. After authentication, the integration still needs a dedicated identity, least-privilege permissions, controlled token lifecycle and a clear separation between what the client may read and what it may command.

Do not solve integration failures by granting broad object access. A task-oriented façade can validate an allowed operation and expose fewer fields, but its Apex or middleware implementation must still enforce sharing, object and field security deliberately. Log the calling application and business actor where relevant so an audit shows who requested the change, not only which technical user performed it.

Version the consumer promise

Pinning a Salesforce REST API version controls platform behaviour; it does not version your enterprise meaning. A field can keep the same API name while its allowed values, ownership or interpretation changes. Salesforce also maintains an API end-of-life policy, so consumers must be inventoried and upgraded before old platform versions retire.

Define compatibility for your own contract. Add optional fields with safe defaults, avoid repurposing existing values and publish a deprecation window for breaking changes. Maintain consumer ownership, traffic and version telemetry. Contract tests should replay representative requests against a production-like org and verify business outcomes, permissions and stable errors—not only HTTP status codes.

Operate the boundary as a product

Measure end-to-end success, latency, rejected requests, retries and saturation alongside Salesforce API consumption. The REST Limits resource exposes maximum and remaining org limits, but a healthy remaining count does not prove a healthy customer process. Set service objectives around the business operation and alert before backlogs or repeated invalid requests exhaust shared capacity.

Give the contract an owner, catalogue entry, schema, example payloads, error catalogue and change history. Record which consumers use each operation and who approves new data exposure. This turns integration from a collection of credentials and field mappings into a governed service that can evolve with Salesforce without making every downstream team part of each org change.

The JSBC Labs view

Use standard Salesforce REST resources directly when record-level access is the real requirement and ownership is tight. Introduce a capability API when consumers need stable business meaning, orchestration, security or lifecycle independence. The decision is not standard API versus custom code; it is implementation exposure versus an intentional boundary.

Begin with one operation, name its business outcome and document identity, atomicity, idempotency, errors, authorisation and versioning. Map that contract to Salesforce behind the boundary and test it against real automation. Your object model will continue to evolve. A good integration contract lets it evolve without turning each field change into an enterprise incident.

Official references

Continue reading