Scope Item: 5HP — Advanced Intercompany Stock Transfer
Document Type: NBIC (Cross-Company, Delivery-Linked)
Verified on: SAP S/4HANA Cloud Public Edition
Document chain (example):
STO (NBIC)
└─→ OBD
├─→ IV2 (auto-generated after PGI via Output Determination)
└─→ IBD (auto-generated after PGI)
└─→ GR Complete1. Why 5HP Is Architecturally Different from Standard Stock Transfers
5HP uses the NBIC purchase order type. Three differences that break any assumption carried over from standard NB flows:
No direct GR against the PO. The full delivery chain is mandatory — OBD → PGI → IBD → GR. Attempting to post GR directly against the intercompany PO returns error
M7/036.IV2 is auto-generated, not API-driven. After PGI, Output Determination automatically creates the intercompany invoice (IV2).
API_BILLING_DOCUMENT_SRVis read-only — there is no API to create IV2, and none is needed.Session-bound ETag on all FunctionImports. Every write operation in
API_OUTBOUND_DELIVERY_SRVrequires the CSRF token fetch, entity GET, and FunctionImport POST to share the same HTTP session (cookie jar). Stateless calls return 412 or 428. (See SAP Note 3539067.)
2. Authentication and Session Architecture
All write operations use a Communication User via Basic Auth, provisioned through the relevant Communication Arrangement (e.g., SAP_COM_0106 for Delivery Processing Integration, SAP_COM_0209 for Inbound Delivery).
$user = “<comm-user>”
$pass=”<password>”
$base = “https://<your-tenant>.s4hana.cloud.sap”
$cookieJar = “$env:TEMP\obd_cookies_$(Get-Date -Format ‘HHmmss').txt”The session rule: Every POST / PATCH / DELETE must use curl -c/-b <cookie-jar> to maintain session continuity across calls. PowerShell Invoke-WebRequest and stateless REST clients do not preserve the server-side session and will fail with CSRF or ETag errors.
BTP Integration Suite: Configure a persistent HTTP Receiver Adapter with a Cookie Session Handler policy. The iFlow must model the CSRF token fetch as an explicit preceding step — do not assume the token survives across integration flow restarts or parallel executions.
3. Step 1 — Create STO (✅ API Verified)
Service: api_stocktransportorder (OData V4)
Available from: Release 2602
Reference: SAP Community — Stock Transport Orders via API in SAP S/4HANA Cloud Public Edition
POST https://<your-tenant>.s4hana.cloud.sap/sap/opu/odata4/sap/api_stocktransportorder/srvd_a2x/sap/stocktransportorder/0001/StockTransportOrder
Content-Type: application/json
x-csrf-token: <fetched-token>Request body:
{
“StockTransportOrderType”: “NBIC”,
“CompanyCode”: “<receiving-company-code>”,
“PurchasingOrganization”: “<purchasing-org>”,
“PurchasingGroup”: “<purchasing-group>”,
“SupplyingPlant”: “<supplying-plant>”,
“DocumentCurrency”: “<currency>”,
“Language”: “<language-key>”,
“_StockTransportOrderItem”: [
{
“StockTransportOrderItem”: “00010”,
“Product”: “<product-number>”,
“Plant”: “<receiving-plant>”,
“StorageLocation”: “<storage-location>”,
“OrderQuantity”: 1,
“OrderQuantityUnit”: “ST”,
“OrderQuantityUnitISOCode”: “PCE”,
“NetPriceAmount”: 60.00,
“DocumentCurrency”: “<currency>”,
“DeliveryDocumentType”: “NCC3”
}
]
}Response (HTTP 201) — key fields:
Field Notes
StockTransportOrder | Generated STO number |
StockTransportOrderType | Must be NBIC for 5HP |
PurchasingInfoRecord | Auto-resolved from master data |
EffectiveAmount | Net price + applicable tax |
Five things that will break this call:
OrderQuantityUnitISOCodeis required — omitting it returns “ISO unit must be provided”StockTransportOrderItemmust be a string (“00010”), not an integer (10)- Fetch the CSRF token in a separate preceding GET with header
x-csrf-token: fetch - Write the JSON body to a file and use
–data-binary @file— avoids PowerShell escape issues with nested quotes and special characters API_PURCHASEORDER_PROCESS_SRV(OData V2) does not support NBIC STO document types — use the dedicated V4 service
Note: Account assignment at the item level is not supported for NBIC. The process is designed exclusively for stock-to-stock transfers between company codes. (→ KBA 3744416)
4. Step 2 — Create OBD (⚠️ Constraints for NBIC)
Service: API_OUTBOUND_DELIVERY_SRV (OData V2)
The standard API supports OBD creation via POST /A_OutbDeliveryHeader with ReferenceSDDocument pointing to the STO. However, for NBIC specifically, two prerequisites must be in place before this path is viable:
Value Chain Monitoring must be enabled for PO type NBIC. If value chains are not activated in the configuration activity Enable Value Chain Monitoring for Purchase Orders, OBD creation will fail at the backend. → KBA 3413153
Only a small set of fields is accepted on create. The API rejects any field not in the explicit allowlist:
ReferenceSDDocument,ReferenceSDDocumentItem,ActualDeliveryQuantity,DeliveryQuantityUnit,ShippingPoint,DeliveryDocument,SerialNumber. All other fields must be set via a subsequentPATCH. → SAP Note 2899036
Alternative (tested in this flow): Application Job API — schedule a backend job to create OBDs in bulk against the STO. This is the SAP-recommended approach for high-volume scenarios and avoids the prerequisites and field constraints above.
Design rationale: For 5HP, OBD creation is a scheduled, bulk-oriented operation. Real-time per-STO OBD creation via REST is a secondary path and adds operational risk at scale.
5. Step 3 — OBD Picking and Goods Issue (✅ API Verified, curl Required)
The Session-Bound ETag Mechanism
All FunctionImport operations in API_OUTBOUND_DELIVERY_SRV require the CSRF token and ETag to be acquired within the same HTTP session as the subsequent POST. A stateless client that fetches a CSRF token and then calls PostGoodsIssue without session persistence will receive:
- 428 Precondition Required — no ETag provided
- 412 Precondition Failed — ETag is outdated or not seeded in the session
SAP Note 3539067 — Error when posting goods issue via API: 412 = ETag is outdated (re-GET the delivery and retry); 428 = ETag header is missing entirely.
Client Type Result Reason
| Stateless (Postman, MCP, iFlow without session handler) | 428 / 412 | No shared session between CSRF fetch and POST |
| curl + cookie jar (same session) | 200 ✅ | CSRF fetch → entity GET → POST share one session |
Correct call sequence:
$svcRoot = “https://<your-tenant>.s4hana.cloud.sap/sap/opu/odata/sap/API_OUTBOUND_DELIVERY_SRV”
$obd = “<outbound-delivery-number>”
# Step 0: Fetch CSRF token
# Important: target the entity set URL, NOT the service root
$fetchOut = & curl.exe -s -D – -o NUL `
-u “${user}:${pass}” `
-H “x-csrf-token: fetch” `
-H “Accept: application/json” `
-c $cookieJar `
“$svcRoot/A_OutbDeliveryHeader”
$csrf = ($fetchOut | Select-String “(?i)x-csrf-token:\s*(.+)”).Matches.Groups[1].Value.Trim()
# Step 1: GET entity in same session
# This seeds the ETag in the server-side session cache
& curl.exe -s -D – -o NUL `
-u “${user}:${pass}” `
-H “Accept: application/json” `
-b $cookieJar -c $cookieJar `
“$svcRoot/A_OutbDeliveryHeader(‘${obd}')?\`$format=json”
3a. PickAllItems — If-Match: not required
& curl.exe -s -X POST `
-u “${user}:${pass}” `
-H “x-csrf-token: $csrf” `
-H “Accept: application/json” `
-b $cookieJar -c $cookieJar `
-w “`nHTTP:%{http_code}” `
“$svcRoot/PickAllItems?DeliveryDocument=”${obd}”&\`$format=json”
# Expected: HTTP 2003b. ConfirmPickingAllItems — If-Match: do NOT include
& curl.exe -s -X POST `
-u “${user}:${pass}” `
-H “x-csrf-token: $csrf” `
-H “Accept: application/json” `
-b $cookieJar -c $cookieJar `
-w “`nHTTP:%{http_code}” `
“$svcRoot/ConfirmPickingAllItems?DeliveryDocument=”${obd}”&\`$format=json”
# Expected: HTTP 200Warning: Including
If-Matchin this call returns 412 unconditionally. This is not a configuration problem — the operation explicitly rejects the header.
3c. PostGoodsIssue — If-Match: * is mandatory
& curl.exe -s -X POST `
-u “${user}:${pass}” `
-H “x-csrf-token: $csrf” `
-H “If-Match: *” `
-H “Accept: application/json” `
-b $cookieJar -c $cookieJar `
-w “`nHTTP:%{http_code}” `
“$svcRoot/PostGoodsIssue?DeliveryDocument=”${obd}”&\`$format=json”
# Expected: HTTP 200, OverallGoodsMvtStatus = CMaterial documents created after PGI (NBIC-specific):
Movement Type Plant Stock Change
| MT681 | Supplying plant | Unrestricted → in-transit |
| MT107 (WL-linked) | Receiving plant | GR blocked stock +1 |
6. Step 4 — OBD Actual Posting Date (⚠️ Manual Step Required)
KBA 3794858 confirms that the Internal Actual Transfer of Control Date (Header → Shipping tab → Internal Transfer of Control Dates → Actual Date) is not exposed as an updatable field in API_OUTBOUND_DELIVERY_SRV. After PGI, all header date fields are locked by the system.
Manual steps:
- Open the OBD in Manage Outbound Deliveries (Fiori) or transaction
VL02N - Header → Shipping tab → Internal Transfer of Control Dates
- Set Actual Date to the current system date
- Save
Why this is a blocker: The IBD
PostGoodsReceiptFunctionImport validates this date as a precondition. Without it, the GR call will fail.Enhancement path: If end-to-end automation of this date is required, raise an Enhancement Request referencing KBA 3794858 as the documented product limitation.
7. Step 5 — IBD Putaway Confirmation (✅ API Verified)
The IBD is auto-generated by the system immediately after PGI. Use API_INBOUND_DELIVERY_SRV for putaway.
$ibdRoot = “https://<your-tenant>.s4hana.cloud.sap/sap/opu/odata/sap/API_INBOUND_DELIVERY_SRV”
$ibd = “<inbound-delivery-number>”Reuse the
$csrftoken and$cookieJarfrom Step 3 if still within the same session. If the session has expired, re-execute the CSRF fetch and entity GET sequence for the IBD service root.
5a. PutawayAllItems — If-Match: not required
& curl.exe -s -X POST `
-u “${user}:${pass}” `
-H “x-csrf-token: $csrf” `
-H “Accept: application/json” `
-b $cookieJar -c $cookieJar `
“$ibdRoot/PutawayAllItems?DeliveryDocument=”${ibd}”&\`$format=json”5b. ConfirmPutawayAllItems — If-Match: not required
& curl.exe -s -X POST `
-u “${user}:${pass}” `
-H “x-csrf-token: $csrf” `
-H “Accept: application/json” `
-b $cookieJar -c $cookieJar `
“$ibdRoot/ConfirmPutawayAllItems?DeliveryDocument=”${ibd}”&\`$format=json”8. Step 6 — Post Goods Receipt (✅ API Verified)
& curl.exe -s -X POST `
-u “${user}:${pass}” `
-H “x-csrf-token: $csrf” `
-H “If-Match: *” `
-H “Accept: application/json” `
-b $cookieJar -c $cookieJar `
“$ibdRoot/PostGoodsReceipt?DeliveryDocument=”${ibd}”&\`$format=json”
# Expected: HTTP 200, OverallGoodsMovementStatus = CPrerequisite: Step 4 (OBD Actual Posting Date) must be completed first.
Two-step material document movement unique to NBIC:
Movement Type Plant Stock Change Notes
| MT107 (WL-linked) | Receiving plant | GR blocked +1 | Linked to OBD — cannot be directly cancelled via MatDoc Cancel API (M7/492) |
| MT109 | Receiving plant | GR blocked -1 → unrestricted +1 | Moves stock from restricted to unrestricted |
MT107 being WL-linked to the OBD is expected NBIC behaviour, not an error. It does not indicate a problem with the preceding GI.
9. Step 7 — Intercompany Invoice IV2 (Auto-Generated, No Action Required)
After PGI, Output Determination automatically creates the IV2 intercompany invoice. No API call, job trigger, or manual action is needed.
Verify via query:
GET https://<your-tenant>.s4hana.cloud.sap/sap/opu/odata/sap/API_BILLING_DOCUMENT_SRV/A_BillingDocumentItem
?$filter=ReferenceSDDocument eq ‘<outbound-delivery-number>'
&$select=BillingDocument,BillingDocumentItem,ReferenceSDDocument,
Material,BillingQuantity,NetAmount
&$format=jsonExpected response — key fields:
Field Expected Value Notes
BillingDocumentType | IV2 | Intercompany invoice |
CompanyCode | Supplying company code | Issuing entity |
PartnerCompany | Receiving company code | Recipient entity |
AccountingPostingStatus | C | FI document fully posted |
OverallSDProcessStatus | C | SD flow complete |
CreationDate | PGI date | Same-day auto-generation |
API limitation: Both
A_BillingDocumentandA_BillingDocumentItemcarrysap:creatable=”false”inAPI_BILLING_DOCUMENT_SRV. The IV2 is entirely system-driven — neither API creation nor manual creation is needed or supported.
10. If-Match Quick Reference
FunctionImport Service If-Match Rule What Happens If Wrong
PickAllItems | OBD | Not required | — |
ConfirmPickingAllItems | OBD | Must NOT be included | 412 unconditionally if added |
PostGoodsIssue | OBD | Must be * | 412 (outdated) / 428 (missing) |
ReverseGoodsIssue | OBD | Must be * | Same as above |
PutawayAllItems | IBD | Not required | — |
ConfirmPutawayAllItems | IBD | Not required | — |
PostGoodsReceipt | IBD | Must be * | — |
ReverseGoodsReceipt | IBD | Must NOT be included | 501 if added |
Cancel | Material Document | Not required | WL-type (MT107): M7/492 — cancel via Fiori instead |
Cancel | Billing Document | N/A | 428 — SD-BIL-BD rejects at business layer; cancel via Fiori |
Source: SAP Help — ETag handling for Outbound Delivery (A2X); SAP Note 3539067
11. API Capability Summary
Step Automatable via API Notes
| Create STO | ✅ | api_stocktransportorder V4 (release 2602+) |
| Create OBD | ⚠️ | Standard API requires value chain config (KBA 3413153); Application Job API recommended for bulk |
| OBD Picking + GI | ✅ | API_OUTBOUND_DELIVERY_SRV — requires curl + cookie jar session |
| OBD Actual Posting Date | ❌ | KBA 3794858 — manual Fiori step required |
| IBD Putaway + Confirmation | ✅ | API_INBOUND_DELIVERY_SRV |
| IBD Post Goods Receipt | ✅ | API_INBOUND_DELIVERY_SRV |
| IV2 Intercompany Invoice | Auto | Output Determination after PGI — no action needed |
| Direct GR against NBIC PO | ❌ | M7/036 — delivery chain is non-negotiable |
12. Complete Document Chain
STO [NBIC — Supplying Plant → Receiving Plant]
│
└─→ OBD [Shipping Point: Supplying Plant]
│
│ PGI material documents:
│ ├── MT681: Supplying plant unrestricted → in-transit
│ └── MT107 (WL-linked): Receiving plant GR blocked +1
│
├─→ IV2 [Auto-generated by Output Determination after PGI]
│ Supplying Co. Code bills Receiving Co. Code
│ FI document: AccountingPostingStatus = C
│
└─→ IBD [Auto-generated after PGI]
│
│ GR material documents:
│ ├── MT107 (WL-linked to OBD): GR blocked +1
│ └── MT109: GR blocked -1 → unrestricted +1
│
GR Complete — OverallGoodsMovementStatus = C13. Implementation Recommendations
For SI teams and integration developers:
Session management is the critical path. All OBD and IBD FunctionImport operations must execute within a single HTTP session. The sequence is always: CSRF token fetch → entity GET → FunctionImport POST, without breaking the session between calls. In BTP Integration Suite, use a persistent HTTP Receiver Adapter with Cookie Session Handler enabled. Do not invoke these operations from stateless middleware.
The OBD creation gap is the biggest automation blocker in 5HP. The standard API can create OBDs with STO reference, but the NBIC-specific prerequisite — value chain configuration for PO type NBIC — must be active first (KBA 3413153). For production-scale automation, use the Application Job API to batch-create OBDs rather than issuing per-document REST calls. This avoids the configuration dependency and scales better.
Regression-test after every FPS update. The ETag and session-binding behaviour of OBD FunctionImports has changed between patch levels. The If-Match rules documented above reflect the tested state as of 2026-09-23. Validate the full API chain after each FPS delivery — specifically the ConfirmPickingAllItems (rejects If-Match) and PostGoodsIssue (requires If-Match: *) asymmetry.
IV2 requires zero intervention. The intercompany invoice is fully owned by Output Determination. Do not add any trigger logic around billing — it creates race conditions and duplicate documents.
KBA 3794858 is an enhancement candidate. If your process requires fully automated OBD-to-GR flow, the OBD Actual Posting Date is the only remaining manual gate. Raise an Enhancement Request with SAP referencing this KBA if the business case warrants it.
Reference SAP Notes
Note / KBA Title Where Relevant
| KBA 3794858 | Internal Actual Control Transfer Date not updatable via API_OUTBOUND_DELIVERY_SRV | Step 4 — manual workaround required |
| SAP Note 3539067 | Error when posting goods issue via API — 412 / 428 ETag handling | Step 3 — ETag behaviour |
| SAP Note 3778784 | “State of the resource already changed” on PostGoodsIssue | Step 3 — duplicate PGI scenario |
| SAP Note 2899036 | Wrong input parameters on OBD creation — allowed fields list | Step 2 — OBD create payload |
| KBA 3413153 | OBD creation fails for NBIC — value chains not enabled | Step 2 — prerequisite configuration |
| KBA 3744416 | MMPUR_PO_INTERCOMP111 — account assignment not supported in NBIC | Step 1 — STO creation constraint |
| KBA 3699051 | NBIC STOs not appearing in Monitor Value Chains | Operational monitoring |
The features covered in this article are based on SAP S/4HANA Cloud, Public Edition 2608, please refer to the latest information for changes in subsequent versions.
Hope you LIKE it if it addresses your issue. After that, please feel free to comment after following my account and I will reply ASAP.
Source link
