Salesforce + SAP S/4HANA Integration: Customer/Order Sync, OData vs RFC, Middleware
How do you integrate Salesforce and SAP S/4HANA - customer/order sync, OData vs RFC, middleware?
TL;DR
Bottom line: Use OData V2/V4 for greenfield Salesforce-to-SAP S/4HANA integrations; RFC/BAPI only for on-premise custom logic not exposed via OData; IDocs only for high-volume batch or legacy EDI. A middleware layer (MuleSoft or SAP CPI) is required for production.
Key limit: Salesforce API calls are capped at 100,000 + 1,000/user license per 24h (Enterprise); SAP OData returns max 1,000 records/page by default.
Watch out for: SAP CSRF token handling — every write operation requires a fresh token fetched via GET + session cookies cached. Forgetting this is the #1 cause of 403 errors.
Best for: Bidirectional customer master + sales order sync between Salesforce CRM and SAP S/4HANA ERP.
Authentication: Salesforce uses OAuth 2.0 (JWT bearer for server-to-server); SAP S/4HANA Cloud uses OAuth 2.0 + CSRF; on-premise uses Basic Auth + CSRF or X.509.
System Profile
This card covers the integration between Salesforce (CRM, source of truth for leads, opportunities, and customer-facing data) and SAP S/4HANA (ERP, source of truth for financial master data, pricing, inventory, and order fulfillment). It applies to SAP S/4HANA Cloud (Public and Private Edition) and On-Premise 2021+ releases.
System
Role
API Surface
Direction
Salesforce (API v62.0)
CRM — customers, leads, opportunities
REST API, Bulk API 2.0, Platform Events
Outbound + Inbound
SAP S/4HANA (2408 / Cloud 2502)
ERP — financial master, inventory, fulfillment
OData V2/V4, SOAP, RFC/BAPI, IDoc
Inbound + Outbound
Middleware (MuleSoft / SAP CPI)
Integration orchestrator
Connectors for both systems
Bidirectional
API Surfaces & Capabilities
Salesforce Side
API Surface
Protocol
Best For
Max Records/Request
Rate Limit
Real-time?
Bulk?
REST API
HTTPS/JSON
Individual record CRUD, <2K records
2,000 per SOQL query
100K+ calls/24h
Yes
No
Bulk API 2.0
HTTPS/CSV
ETL, data migration, >2K records
150M per file
15,000 batches/24h
No
Yes
SOAP API
HTTPS/XML
Metadata operations, legacy
2,000 records
Shared with REST
Yes
No
Composite API
HTTPS/JSON
Multi-object operations
25 subrequests
1 API call
Yes
No
Platform Events
CometD/gRPC
Real-time event notifications
N/A
200K-1M events/24h
Yes
N/A
Change Data Capture
CometD
Field-level change tracking
N/A
24h replay window
Yes
N/A
SAP S/4HANA Side
API Surface
Protocol
Best For
Max Records/Request
Rate Limit
Real-time?
Bulk?
OData V2
HTTPS/JSON or XML
Standard CRUD (most services)
1,000/page
~100 req/s (Cloud)
Yes
Via $batch
OData V4
HTTPS/JSON
Newer APIs (greenfield)
1,000/page
~100 req/s (Cloud)
Yes
Via $batch
SOAP (WSDL)
HTTPS/XML
Legacy, WS-Security scenarios
Varies
Fair-use
Yes
No
RFC/BAPI
SAP protocol (JCo)
Custom ABAP logic
Single call
Pool-dependent
Yes
No
IDoc
ALE/EDI
Bulk document transfer
Unlimited (async)
Throughput-dep.
No
Yes
SAP Event Mesh
AMQP/MQTT/REST
Real-time pub/sub
N/A
Tier-dependent
Yes
N/A
Rate Limits & Quotas
Salesforce Per-Request Limits
Limit Type
Value
Applies To
Notes
Max records per SOQL query
2,000
REST/SOAP API
Use queryMore for pagination
Max request body size
50 MB
REST API
Max composite subrequests
25
Composite API
All-or-nothing by default
Max batch file size
150 MB
Bulk API 2.0
Split larger files
Max records per batch
10,000
Bulk API 2.0
API request timeout
600,000 ms
All REST/SOAP
10 minutes
Salesforce Rolling / Daily Limits
Limit Type
Value
Window
Edition Differences
Total API calls
100,000 base
24h rolling
Enterprise: +1,000/license; Unlimited: +5,000/license; Developer: 15,000 total
Concurrent API requests
25
Per org
Developer/Trial: 5
Bulk API batches
15,000
24h rolling
Shared across editions
Streaming API events
200K-1M
24h
Enterprise: 200K; Unlimited: 1M
Salesforce Governor Limits (Per Transaction)
Limit Type
Per-Transaction Value
Notes
SOQL queries
100
Includes queries from triggers
DML statements
150
Each insert/update/delete counts as 1
Callouts (HTTP)
100
External HTTP requests within a transaction
CPU time
10,000 ms (sync) / 60,000 ms (async)
Exceeded = abort
Heap size
6 MB (sync) / 12 MB (async)
SAP S/4HANA Rate Limits
Limit Type
Value
Applies To
Notes
Default throughput
~100 req/s
OData (Cloud)
Managed via SAP API Management
Burst capacity
~200 req/s
OData (Cloud)
Short-burst before throttling
Max page size
1,000 records/page
OData queries
Configurable via $top
$batch max changesets
100 per request
OData $batch (Cloud)
On-premise varies
HTTP 429 response
Retry-After header
All Cloud APIs
Exponential backoff recommended
Authentication
Salesforce Authentication
Flow
Use When
Token Lifetime
Refresh?
Notes
OAuth 2.0 JWT Bearer
Server-to-server (recommended)
2h default
New JWT per request
Requires connected app + certificate
OAuth 2.0 Client Credentials
Server-to-server (simpler)
2h
New token per request
Spring '23+; no user context
Username-Password (legacy)
Testing only
Session timeout
No
Do NOT use in production
SAP S/4HANA Authentication
Flow
Use When
Token Lifetime
Refresh?
Notes
OAuth 2.0 Client Credentials
S/4HANA Cloud
12h default
Yes
Requires Communication Arrangement
Basic Auth + CSRF
On-premise
Session-based
Fetch new CSRF per session
CSRF required for all writes
X.509 Client Certificates
High-security on-prem
Cert validity
N/A
Recommended for production
Authentication Gotchas
SAP CSRF token is session-bound: fetch via GET with X-CSRF-Token: Fetch, cache token AND all Set-Cookie values. [src6]
Salesforce JWT flow requires connected app with digital certificate — self-signed for dev, CA-signed for prod. [src2]
SAP Cloud Communication Arrangements are mandatory — direct API calls without one return 401. [src1]
Constraints
Salesforce Enterprise: 100,000 + 1,000/user license API calls per 24h. High-frequency polling exhausts this quickly — use event-driven patterns.
SAP OData is not true REST — SAP-specific query conventions ($filter format, guid/datetime syntax).
SAP RFC requires proprietary SAP JCo or .NET Connector (licensed) — cannot run in serverless/containers without native libraries.
IDocs are asynchronous-only — no request-response pattern; unsuitable for real-time validation.
Salesforce governor limits cascade through triggers — integration writes can breach limits via trigger chains.
S/4HANA Cloud Public Edition restricts custom ABAP — only released OData/SOAP APIs or RAP extensions.
Both vendors recommend middleware for production — point-to-point is fragile at scale.
SAP $batch max 100 changesets per request (Cloud); Salesforce Composite max 25 subrequests.
Integration Pattern Decision Tree
START — Salesforce <-> SAP S/4HANA Integration
+-- SAP protocol selection?
| +-- S/4HANA Cloud Public? -> OData V4 (preferred) or V2
| +-- Standard API exists on api.sap.com? -> OData
| +-- Custom logic not exposed via OData? -> RFC/BAPI (on-prem only)
| +-- High-volume batch (>10K records)? -> IDoc or OData $batch
+-- Salesforce protocol selection?
| +-- Real-time <2K records? -> REST API (Composite for multi-object)
| +-- Batch/bulk >2K records? -> Bulk API 2.0
| +-- Change notifications FROM Salesforce? -> Platform Events or CDC
| +-- Show SAP data without copying? -> Salesforce Connect (read-only)
+-- Middleware selection?
| +-- Salesforce strategic? -> MuleSoft
| +-- SAP strategic? -> SAP Integration Suite / CPI
| +-- Neither dominant? -> Boomi, Workato, or Jitterbit
+-- Data direction?
| +-- SF -> SAP: Middleware maps SF objects to SAP OData entities
| +-- SAP -> SF: Middleware queries SAP OData, upserts to SF REST
| +-- Bidirectional: Define source-of-truth per field first
+-- Error handling?
+-- Zero-loss: idempotency keys + dead letter queue
+-- Best-effort: retry 3x with backoff, then log and alert
Quick Reference
Key SAP OData Services
SAP OData Service
API Hub ID
Use Case
OData Ver.
Key Entity Sets
Business Partner
API_BUSINESS_PARTNER
Customer master sync
V2
A_BusinessPartner, A_BusinessPartnerAddress
Sales Order
API_SALES_ORDER_SRV
Order creation
V2
A_SalesOrder, A_SalesOrderItem
Sales Order (V4)
API_SALESORDER_0001
Order creation (newer)
V4
SalesOrder, SalesOrderItem
Product Master
API_PRODUCT_SRV
Product catalog sync
V2
A_Product, A_ProductDescription
Material Availability
API_MATERIAL_AVAILABILITY_INFO
ATP stock check
V2
SupplyDemandItem
Pricing Conditions
API_SLSPRICINGCONDITIONRECORD_SRV
Pricing sync
V2
A_SlsPrcgCndnRecdValidity
Outbound Delivery
API_OUTBOUND_DELIVERY_SRV_0002
Fulfillment status
V2
A_OutbDeliveryHeader, A_OutbDeliveryItem
Salesforce Objects for SAP Integration
Salesforce Object
SAP Equivalent
Sync Direction
Typical Frequency
Account
Business Partner (BP)
Bidirectional
Real-time or 15-min batch
Contact
BP Contact
Bidirectional
Real-time
Opportunity (Closed Won)
Sales Order (VA01)
SF -> SAP
Event-driven
Product2
Material Master (MM01)
SAP -> SF
Daily batch
PricebookEntry
Condition Records (VK11)
SAP -> SF
Daily or on-change
Step-by-Step Integration Guide
1. Set up authentication on both sides
Salesforce: Create a Connected App with OAuth 2.0 JWT Bearer flow. SAP S/4HANA Cloud: Create a Communication Arrangement. [src1, src2]
# Test Salesforce auth
curl -X POST https://login.salesforce.com/services/oauth2/token \
-d "grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer" \
-d "assertion=${JWT_TOKEN}"
Verify: Response includes access_token and instance_url
2. Fetch SAP CSRF token and test OData connectivity
Every SAP write requires a CSRF token. Fetch with GET, cache token + cookies. [src6]
# Fetch CSRF token from SAP
curl -X GET "https://{sap-host}/sap/opu/odata/sap/API_BUSINESS_PARTNER/A_BusinessPartner?$top=1" \
-H "Authorization: Basic ${SAP_CREDENTIALS}" \
-H "X-CSRF-Token: Fetch" \
-c cookies.txt -D headers.txt
Verify: Response returns HTTP 200 with x-csrf-token header (not "Required")
3. Sync customer data (Salesforce Account to SAP Business Partner)
Map Account fields to Business Partner and POST via OData. [src1, src5]
Date formats: SF uses ISO 8601; SAP OData V2 uses datetime'...' or epoch ms; V4 uses ISO 8601. [src4]
Currency: SAP stores with currency-dependent decimals (JPY=0, USD=2). Always transmit currency code alongside amount. [src1]
SAP Material IDs: 18-char with leading zeros. Salesforce stores trimmed. Middleware must pad/trim consistently. [src5]
SAP OData returns empty strings for null fields, not JSON null. Treat "" as null to avoid overwriting good data. [src4]
Error Handling & Failure Points
Common Error Codes
Code
System
Meaning
Resolution
403
SAP
CSRF token invalid/expired
Re-fetch CSRF token with session cookies
429
Both
Rate limit exceeded
Exponential backoff (2^n seconds, max 5 retries)
401
SAP
Missing Communication Arrangement
Verify Communication User + Arrangement
REQUEST_LIMIT_EXCEEDED
Salesforce
Daily API limit hit
Check /limits/ endpoint; optimize or purchase add-on
UNABLE_TO_LOCK_ROW
Salesforce
Concurrent record update
Retry with jitter; serialize same-record updates
/IWBEP/CX_MGW_BUSI_EXCEPTION
SAP
Business logic rejection
Read structured error message for details
Failure Points in Production
CSRF token caching without cookies: Middleware caches token but not session cookies — all SAP writes fail with 403. Fix: Cache CSRF token AND Set-Cookie headers together. [src6]
Governor limit breach from trigger cascading: 200 records trigger Apex with SOQL in loop = 200 queries (limit 100). Fix: Bulkify triggers; use Composite/Bulk API to control batch size. [src2]
OData V2 date format silently rejected: ISO 8601 dates fail in V2 with cryptic parse error. Fix: Use datetime'YYYY-MM-DDT00:00:00' for V2; ISO 8601 for V4 only. [src4]
Material number padding mismatch: SF sends "MAT001", SAP returns "not found". Fix: Always pad to 18 chars with leading zeros. [src5]
OData $batch partial failure unhandled: $batch returns 200 overall but individual changesets fail silently. Fix: Parse each changeset response; log failures to dead letter queue. [src1]
OAuth refresh token expiry after 90 days: SF refresh tokens expire after 90 days of non-use. Fix: Set "valid until revoked" policy, or use JWT Bearer flow. [src2]
Anti-Patterns
Wrong: Polling SAP every 60 seconds for all records
// BAD — wastes API calls; misses changes between polls
setInterval(async () => {
const all = await fetchAllCustomersFromSAP();
for (const c of all) await upsertToSalesforce(c);
}, 60000);
Correct: Use delta query with LastChangeDateTime filter
// GOOD — fetch only changed records; minimize API calls
async function syncDelta(lastSync) {
const filter = `LastChangeDateTime gt datetime'${lastSync}'`;
const changes = await fetchFromSAP(`/A_BusinessPartner?$filter=${filter}`);
const batches = chunk(changes, 25);
for (const batch of batches) await compositeUpsert(batch);
}
Wrong: Direct SAP callouts inside Salesforce Apex triggers
// BAD — callout in trigger context; cannot do DML after
trigger OrderTrigger on Opportunity (after update) {
Http h = new Http();
h.send(new HttpRequest()); // Callout in trigger = BAD
}
Correct: Fire Platform Events for async processing
// GOOD — Platform Event; middleware picks it up
trigger OrderTrigger on Opportunity (after update) {
List<Order_Created__e> events = new List<Order_Created__e>();
// ... build events for closed-won opportunities
EventBus.publish(events); // Async, no callout limit
}
Wrong: Hardcoding SAP Material IDs without padding
# BAD — SAP returns 404 for unpadded IDs
material_id = "MAT001" # SAP expects '000000000000MAT001'
Correct: Always pad to 18 characters
# GOOD — pad with leading zeros
material_id = "MAT001".zfill(18) # '000000000000MAT001'
Common Pitfalls
Sandbox vs production API limits: SF sandboxes have 5M calls/24h vs production 100K+. Load tests pass in sandbox but fail in prod. Fix: Calculate expected daily API consumption before go-live. [src2]
SAP API version not pinned: S/4HANA Cloud upgrades quarterly; behavior may change. Fix: Pin to specific Communication Scenario version; test after each upgrade. [src1]
SAP field-level authorizations silently omit fields: OData returns 200 but restricted fields are missing (no error). Fix: Test every mapped field; check authorization objects. [src1]
Composite API partial success unhandled: Composite returns 200 overall but subrequests may fail. Fix: Check httpStatusCode of each subrequest result. [src2]
SAP number ranges not configured for external assignment: Creating BP with external number when range is internal = rejection. Fix: Prefer internal numbering; store SAP-assigned number back in Salesforce. [src1]
Timezone mismatch in delta queries: SF datetimes are UTC; SAP on-prem may use app server timezone. Fix: Always convert to UTC; store last-sync timestamp in UTC. [src4]
Diagnostic Commands
# Check Salesforce API usage
curl -s -H "Authorization: Bearer ${SF_TOKEN}" \
"${SF_INSTANCE}/services/data/v62.0/limits/" | \
python3 -c "import sys,json; d=json.load(sys.stdin); \
print(f\"API remaining: {d['DailyApiRequests']['Remaining']}/{d['DailyApiRequests']['Max']}\")"
# Test SAP OData metadata
curl -s -u "${SAP_USER}:${SAP_PASS}" \
"https://${SAP_HOST}/sap/opu/odata/sap/API_BUSINESS_PARTNER/\$metadata" | head -50
# Verify SAP Communication Arrangement
curl -s -u "${SAP_USER}:${SAP_PASS}" \
"https://${SAP_HOST}/sap/opu/odata/sap/API_BUSINESS_PARTNER/A_BusinessPartner?\$top=1"
# Monitor SAP request performance
curl -s -o /dev/null -w "HTTP %{http_code} | Time: %{time_total}s\n" \
-u "${SAP_USER}:${SAP_PASS}" \
"https://${SAP_HOST}/sap/opu/odata/sap/API_SALES_ORDER_SRV/A_SalesOrder?\$top=10"
Version History & Compatibility
Salesforce API Versions
API Version
Release
Status
Key Changes
v62.0
Spring '26
Current
Latest composite API improvements
v61.0
Winter '26
Supported
Enhanced CDC replay
v60.0
Spring '24
Supported
Deprecated legacy SOAP partner endpoints
v58.0
Spring '23
Supported
Introduced Client Credentials flow
SAP S/4HANA Releases
Release
Date
Status
Key Changes
Cloud 2502
Feb 2026
Current
New OData V4 Sales Order API
2408 (On-Prem)
Aug 2024
Current LTS
Expanded OData V4 catalog
Cloud 2308
Aug 2023
Supported
Enhanced BP OData V4
2021 FPS02
2022
Supported
Min recommended for OData V4
When to Use / When Not to Use
Use When
Don't Use When
Use Instead
Bidirectional customer/order sync between SF CRM and SAP ERP
Only reading SAP data (no writes)
Salesforce Connect (OData external objects)
Real-time order creation on opportunity close
Batch migration >100K records
SAP Data Migration Cockpit or LT Replication Server
Event-driven delivery/fulfillment status sync
Simple one-time data load
SAP Data Services or Informatica
Multi-object orchestration (account + order + items)
SAP ECC (pre-S/4HANA) without OData
SAP ECC integration (RFC/IDoc approach)
Cross-System Comparison
Capability
Salesforce (v62.0)
SAP S/4HANA (2408/Cloud)
Notes
API Style
REST (native) + SOAP
OData V2/V4 + SOAP + RFC
SAP OData has SAP-specific conventions
Rate Limits
100K+/24h (hard cap)
~100 req/s (configurable)
SF per-day; SAP per-second
Bulk Import
Bulk API 2.0 (150MB/file)
OData $batch (100 changesets) or IDoc
SF bulk more mature
Event-Driven
Platform Events, CDC
Event Mesh, Business Events
Different protocols
Authentication
OAuth 2.0
OAuth 2.0 + CSRF + Basic + X.509
SAP requires CSRF overlay
Sandbox
Full + Partial copies
Quality/Dev systems
SF sandboxes easier to create
API Versioning
Numbered (v62.0), 3yr support
Release-based (2408)
SF more predictable
Concurrent Requests
25 (hard cap)
Configurable
SF has hard cap; SAP tunable
Important Caveats
Salesforce edition determines API access: Enterprise gets 100K + 1K/license; Professional has no API access by default.
S/4HANA Cloud vs On-Premise differ fundamentally: Cloud restricts custom ABAP; On-Premise gives full access. Architecture may differ based on deployment.
Middleware costs are significant: MuleSoft ~$50K-150K/year; SAP CPI may be included in S/4HANA Cloud license.
SAP rate limits are not publicly documented — the ~100 req/s figure is approximate. Contact SAP support for tenant-specific limits.
All rate limits and endpoints are subject to change: Salesforce releases 3x/year; SAP Cloud quarterly. Verify against current release notes.