When building side-by-side applications on SAP BTP (or external portals) communicating with SAP S/4HANA, developers frequently make sequential HTTP calls:
POST /SalesOrder → Header created with ID 10001.POST /SalesOrderItem (Item 10) → Committed successfully.POST /SalesOrderItem (Item 20) → HTTP 500 Network Timeout or Validation Failure!In this catastrophic failure mode, the sales order header and item 10 remain committed in SAP S/4HANA while item 20 is lost. The database is left in a corrupted, half-posted business state, generating audit anomalies and manual reconciliation overhead.
The OData $batch endpoint allows bundling multiple read queries and mutating operations into a single HTTP POST request. Crucially, mutating operations grouped inside a ChangeSet execute within a single SAP HANA Logical Unit of Work (LUW):
| Architectural Vector | Sequential HTTP Calls | Atomic OData ChangeSet ($batch) |
|---|---|---|
| Transaction Atomicity | None (Partial commits occur) | 100% All-or-Nothing Rollback |
| Roundtrip Latency | N roundtrips (High WAN/Cloud Connector overhead) | Single TCP / TLS connection roundtrip |
| Key Resolution | Requires synchronous read before next POST | Dynamic via Content-ID referencing ($1/_Item) |
| SAP Work Process Consumption | N work process context switches | Single transactional work process execution |
When creating a parent header and dependent children in the same request, the header's primary key (e.g. Sales Order Number) is generated by a number range sequence and unknown to the client beforehand.
The OData standard resolves this via Content-ID referencing:
--changeset_boundary
Content-Type: application/http
Content-ID: 1
POST SalesOrder HTTP/1.1
Content-Type: application/json
{ "SalesOrderType": "OR", "SalesOrganization": "1010" }
--changeset_boundary
Content-Type: application/http
Content-ID: 2
POST $1/_Item HTTP/1.1
Content-Type: application/json
{ "Material": "MAT-001", "RequestedQuantity": 50 }
The SAP Gateway runtime captures the generated entity key from the response of Content-ID: 1 and transparently rewrites $1/_Item to SalesOrder('10001')/_Item before executing the second operation in the same database transaction.
multipart/mixed; boundary=...). Requires strict adherence to CRLF line endings.{"requests": [...]}) with atomicityGroup properties, eliminating MIME parsing overhead.HEAD request with X-CSRF-Token: Fetch and retain cookies across the session to prevent 403 Forbidden - CSRF token validation failed errors.Generate validated multipart/mixed payloads, JSON batch descriptors, and client execution scripts with our interactive orchestrator.
Launch Batch Orchestrator âš¡ Get Architecture Suite (80% Off)