Salesforce + SAP S/4HANA Integration: Customer/Order Sync, OData vs RFC, Middleware
Type: ERP Integration
Systems: Salesforce (API v62.0) + SAP S/4HANA (2408 / Cloud 2502)
Confidence: 0.88
Sources: 7
Verified: 2026-03-02
Freshness: evolving
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]
- CSRF token expires on document update — ETag resets, re-fetch required. [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]
curl -X POST "https://{sap-host}/sap/opu/odata/sap/API_BUSINESS_PARTNER/A_BusinessPartner" \
-H "X-CSRF-Token: ${CSRF_TOKEN}" -H "Content-Type: application/json" \
-b cookies.txt \
-d '{"BusinessPartnerCategory":"2","OrganizationBPName1":"Acme Corp"}'
Verify: HTTP 201 with BusinessPartner number in response
4. Create Sales Order from Salesforce Opportunity
On Opportunity closed-won, create Sales Order via API_SALES_ORDER_SRV. [src1]
curl -X POST "https://{sap-host}/sap/opu/odata/sap/API_SALES_ORDER_SRV/A_SalesOrder" \
-H "X-CSRF-Token: ${CSRF_TOKEN}" -H "Content-Type: application/json" \
-b cookies.txt \
-d '{"SalesOrderType":"OR","SalesOrganization":"1000","SoldToParty":"BP_NUM","to_Item":[{"Material":"MAT001","RequestedQuantity":"5"}]}'
Verify: HTTP 201 with SalesOrder number
5. Implement delivery status sync (SAP to Salesforce)
Query SAP for changed deliveries, upsert to Salesforce custom object. [src1, src2]
# Query SAP for recent deliveries
curl "https://{sap-host}/sap/opu/odata/sap/API_OUTBOUND_DELIVERY_SRV_0002/A_OutbDeliveryHeader?\
$filter=LastChangeDateTime gt datetime'2026-03-01T00:00:00'"
# Upsert to Salesforce
curl -X PATCH "${SF_INSTANCE}/services/data/v62.0/sobjects/Delivery_Status__c/SAP_Delivery__c/${DOC}" \
-H "Authorization: Bearer ${SF_TOKEN}" -H "Content-Type: application/json" \
-d '{"Status__c":"Shipped"}'
Verify: Salesforce record updated with delivery status
Code Examples
Python: Bidirectional Customer Sync
# Input: Salesforce Account changes + SAP BP API access
# Output: Synced customer records with conflict resolution
import requests
def get_sap_csrf_token(session, sap_host, auth):
"""Fetch CSRF token + cookies. Must call before any write."""
resp = session.get(
f"{sap_host}/sap/opu/odata/sap/API_BUSINESS_PARTNER/A_BusinessPartner?$top=1",
headers={"X-CSRF-Token": "Fetch", "Accept": "application/json"},
auth=auth
)
resp.raise_for_status()
return resp.headers["x-csrf-token"]
def sync_account_to_bp(account, session, sap_host, auth, csrf):
"""Create SAP Business Partner from Salesforce Account."""
payload = {
"BusinessPartnerCategory": "2",
"OrganizationBPName1": account["Name"][:40],
"SearchTerm1": account["Name"][:20].upper(),
}
resp = session.post(
f"{sap_host}/sap/opu/odata/sap/API_BUSINESS_PARTNER/A_BusinessPartner",
json=payload,
headers={"X-CSRF-Token": csrf},
auth=auth
)
if resp.status_code == 429:
raise Exception(f"Rate limited. Retry after {resp.headers.get('Retry-After', 5)}s")
resp.raise_for_status()
return resp.json()["d"]["BusinessPartner"]
JavaScript/Node.js: Sales Order Creation
// Input: Salesforce Opportunity (Closed Won event)
// Output: SAP Sales Order number
const axios = require('axios'); // v1.7+
async function getSapCsrfToken(sapHost, auth) {
const resp = await axios.get(
`${sapHost}/sap/opu/odata/sap/API_SALES_ORDER_SRV/A_SalesOrder?$top=1`,
{ headers: { 'X-CSRF-Token': 'Fetch' }, auth, withCredentials: true }
);
return { token: resp.headers['x-csrf-token'], cookies: resp.headers['set-cookie'] };
}
async function createSalesOrder(sapHost, auth, orderData) {
const { token, cookies } = await getSapCsrfToken(sapHost, auth);
const resp = await axios.post(
`${sapHost}/sap/opu/odata/sap/API_SALES_ORDER_SRV/A_SalesOrder`,
{ SalesOrderType: 'OR', SalesOrganization: orderData.salesOrg,
SoldToParty: orderData.sapCustomerNumber,
to_Item: orderData.items.map((it, i) => ({
SalesOrderItem: String((i+1)*10).padStart(6,'0'),
Material: it.materialNumber, RequestedQuantity: String(it.qty)
}))
},
{ headers: { 'X-CSRF-Token': token, 'Cookie': cookies.join('; ') }, auth }
);
return resp.data.d.SalesOrder;
}
cURL: Quick End-to-End Connectivity Test
# Test Salesforce API
curl -s -o /dev/null -w "%{http_code}" \
-H "Authorization: Bearer ${SF_TOKEN}" "${SF_INSTANCE}/services/data/v62.0/"
# Expected: 200
# Test SAP OData + CSRF fetch
curl -s -o /dev/null -w "%{http_code}" -H "X-CSRF-Token: Fetch" \
-u "${SAP_USER}:${SAP_PASS}" \
"https://${SAP_HOST}/sap/opu/odata/sap/API_BUSINESS_PARTNER/A_BusinessPartner?\$top=1"
# Expected: 200
Data Mapping
Field Mapping Reference
| Salesforce Field | SAP S/4HANA Field | Type | Transform | Gotcha |
| Account.Name | BP.OrganizationBPName1 | String | Truncate to 40 chars | SAP max 40; SF max 255 |
| Account.BillingStreet | BPAddress.StreetName | String | Direct | SAP splits street + house number |
| Account.BillingCountry | BPAddress.Country | String | ISO 3166-1 alpha-2 | Must be 2-letter code |
| Opportunity.CloseDate | SO.RequestedDeliveryDate | Date | V2: datetime'...'; V4: ISO 8601 | OData V2 uses epoch ms format |
| Product2.ProductCode | Material.Material | String | Pad to 18 chars (leading zeros) | SAP IDs are 18-char padded |
| OpportunityLineItem.Quantity | SOItem.RequestedQuantity | Decimal | Direct | SAP returns as string in OData |
| OpportunityLineItem.UnitPrice | SOItem (via pricing) | Currency | Cannot set directly | SAP pricing from condition records |
Data Type Gotchas
- 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.
Related Units