12. Outbound Synchronous Interface: Salesforce Account to SAP CPI (Request/Response)
1. Overview
This document describes how to create an outbound Synchronous interface from Salesforce to SAP CPI that sends a flat Account record and receives a response back in the same transaction, using MessageType-less mapping throughout — no OpenAPI import, no Message Type tree on either interface. Unlike the sibling outbound Asynchronous tutorial's scenario (fire-and-forget, no response expected), this one needs:
- A Request Interface, MessageType-less —
Name/BillingCountry/skyvvasolutions__Tax_ID__cmapped straight fromAccountin the Mapping Tool. - A Response Interface wired to the outbound Request interface, since
Processing Mode = Synchronousexpects CPI's response to be parsed and routed back into Salesforce inbound processing within the same call. MessageType-less with External Mapping checked (§2.2) — no Mapping Tool step needed;AccountNumber(standard) andskyvvasolutions__Credit_Status__c(a new custom field, §2.3) resolve straight from the response by matching field name.
Figure 1: Salesforce Account pushed synchronously to SAP CPI and the response routed back — end-to-end flow.
1.1 Business Context
Use this pattern when the calling Salesforce transaction needs CPI's response back before it can proceed — for example, SAP (or a system behind CPI) validates or enriches a single Account's data — a tax ID check, a credit status lookup, an assigned SAP customer number — and Salesforce needs that result immediately, before the record save completes, not asynchronously.
Keep the payload flat and minimal for this pattern. A synchronous call blocks the calling transaction until CPI answers, so it should carry only the fields the immediate decision actually needs — not a parent record bundled together with a full list of child records. If what you actually need is to keep Account and Contact data consistent between Salesforce and SAP, that's a data-synchronization concern, better served by asynchronous/batch patterns — outbound for pushing Salesforce-side changes, one of the inbound Bulk-mode tutorials for pulling SAP-side changes — than by a blocking synchronous call. Reach for this pattern specifically when a single record's validated/enriched result has to come back in-line, and use the outbound Asynchronous pattern (the sibling tutorial in this series) for anything where Salesforce only needs to notify a downstream system and doesn't need anything back in the same transaction.
1.2 Tutorial Scenario
This tutorial pushes one Account record Synchronously to SAP CPI as JSON and expects a response back in the same call, triggered via Apex (invokeCalloutV3, §4.1) so the flow can be verified end-to-end without waiting for a real business event.
It configures the Salesforce-side outbound adapter (Adapter__c.Type__c = 'SAP CPI'), where Salesforce is the caller. The separate SkyvvaSalesforce CPI adapter (installed inside the SAP CPI iFlow designer) is the opposite direction — see the earlier tutorials in this series covering that CPI-side adapter's Sender/Receiver channel setup instead.
Pre-requisites:
- Skyvva Package installed on Salesforce
- SAP CPI user with the required authorizations for message processing
2. Salesforce Interface Development
2.1 Create the Integration and the Request Interface
Create a SKYVVA Outbound Interface under Integration in Salesforce (create the parent Integration record first if you don't already have one).
Configure the outbound Request interface:
| Field | Value |
|---|---|
| Name | Account_INT_REQ_SFDC2SAP_Sync_OB |
| Integration | Your Integration record |
| Source/Target Name | Account |
| Status | Deployed |
| Interface Direction | Outbound |
| Processing Mode | Synchronous |
| Interface API Version | V3 — required for MessageType-less mapping |
| Response Interface | Account_INT_RES_SFDC2SAP_Sync_IB (create this next, §2.2, then come back and link it here) |
Note: leave MetaData Provider/Repository/Source-Target Message Type blank — this interface is MessageType-less. Skip straight to §2.2 to create the Response interface, then §2.3.1 to map fields; no Message Type build step is needed for either interface.
Figure 2: New Interface form — the outbound Request interface.
2.2 Create the Response Interface
Back on the same Integration record's Interfaces related list, click New Interface again to create a second interface for the inbound Response:
| Field | Value |
|---|---|
| Name | Account_INT_RES_SFDC2SAP_Sync_IB |
| Integration | Your Integration record |
| Source/Target Name | Account |
| Status | Deployed |
| Interface Direction | Inbound |
| Processing Mode | Synchronous |
| Interface API Version | V3 |
| External Mapping | checked |
| Request Interface | Account_INT_REQ_SFDC2SAP_Sync_OB |
Note — MetaData Provider / Repository / Source-Target Message Type: leave these blank, same as the Request interface. Salesforce's response routing goes byMessage Type, notInterface API Version— leaving Message Type blank keeps this response following the MessageType-less path this tutorial uses, regardless of the External Mapping setting.
Figure 3: New Interface form — the inbound Response interface.
Figure 4: New Interface form — the Response interface's External Mapping checkbox, checked.
Go back to the Request interface (§2.1) and set its Response Interface field to this record now that it exists.
Figure 5: Request interface — Response Interface field set, pointing to the Response interface.
The two interfaces now link to each other in both directions — the Response interface's own Request Interface field (set in §2.2) already points back the other way.
Figure 6: Response interface — Request Interface field pointing back to the Request interface.
2.3 Create the Custom Fields This Interface Needs
Two fields carry SAP's response back onto the Account record. AccountNumber is a standard field — nothing to create. skyvvasolutions__Credit_Status__c isn't: create it yourself as a new custom field on Account (any label; the API name is what matters).
With External Mapping checked (§2.2), the Response interface needs no explicit field-mapping step at all (§2.3.1) — AccountNumber/skyvvasolutions__Credit_Status__c resolve directly by matching field name, same as the outbound side. Note skyvvasolutions__Credit_Status__c is a field you create here — don't confuse it with skyvvasolutions__Credit_Check_Result__c, a different, similarly-named field the package ships that this tutorial doesn't use.
2.3.1 Map Account Fields (Request)
Open Open Mapping on the Request interface. On a MessageType-less V3 interface, the Mapping Tool's Source tree mirrors Account all the way down. Drag Name/BillingCountry/skyvvasolutions__Tax_ID__c from the Source tree onto the matching, identically-named Target fields. Map each field and save.
Figure 7: Mapping Tool — field mapping on the Request interface, sourced from the Account.
No mapping step for the Response interface. With External Mapping checked (§2.2), the incoming AccountNumber/skyvvasolutions__Credit_Status__c fields resolve directly by name — there's nothing to drag-and-drop in the Mapping Tool for this interface.
2.4 Create the Skyvva SAP BTP Integration Suite Adapter
Navigate to Integration → Details → SAP Control Board → Adapter, click New, and configure it exactly as documented in the outbound Asynchronous tutorial's §2.4 (full field-by-field reference) — the adapter configuration itself doesn't differ between the asynchronous and synchronous scenarios. For this tutorial:
| Field | Value for this tutorial |
|---|---|
| Adapter Name | Account_ADAPTER_Sync |
| Type | "SAP BTP Integration Suite" in the dropdown (stores SAP CPI) |
| Direction | Outbound |
| Authentication Type | Basic (or another value — see the outbound Asynchronous tutorial's §2.4 for all five options) |
| Protocol | HTTP |
| Operation | POST |
| Request Format | application/json |
| Response Format | application/json |
| API Service Endpoint URL | your CPI iFlow's endpoint URL |
| Salesforce Id Handling | Return Salesforce Id into the response |
Setting Salesforce Id Handling to Return Salesforce Id into the response includes the Account's own Salesforce Id in what comes back, so the response can be matched to the right record.
Why JSON, not this adapter's own default (application/xml): application/xml is this adapter type's coded default, but Salesforce as a REST-style caller typically speaks JSON. Picking JSON here keeps the outbound payload in that native format; CPI bridges it to XML internally via its standard JSON→XML converter step (§3), then maps the response straight back to JSON without a second conversion step.
Figure 8: New Adapter dialog — Account_ADAPTER_Sync.
Save the adapter, then add it to the Request interface's Outbound Setup.
Figure 9: Adapter linked to the Request interface's Outbound Setup.
3. SAP CPI Development
This section describes generic CPI palette steps (HTTPS sender, schema import, JSON→XML, XSLT mapping), not anything specific to the SkyvvaSalesforce adapter — treat it as a starting point and confirm against your own CPI tenant's current palette.
Log on to the CPI tenant, navigate to Design → Packages → Artifacts → Create new integration flow.
- Create the sender HTTPS communication channel and provide the connection parameters.
- Export an OpenAPI schema from the Response interface for Message Mapping's Target side — needed since there's no hand-authored OpenAPI spec to reuse now that the interface is MessageType-less. On the Response interface's record page, click Generate MetaData (header button) and set Format = OpenAPI 3.0. API must be set to
V4/Integrate— this is required, not just a default; picking a different API value here generates the wrong schema shape for a MessageType-less interface. Then download the file. (No export is needed from the Request interface — see the Message Mapping step below for why.)
Note: with no Message Type, the exported schema is generated from the full Account object describe, not a curated field list — it will contain every field on Account, not just AccountNumber/skyvvasolutions__Credit_Status__c. You'll pick the specific elements you actually want when building the Message Mapping step below — the extra fields in the file are just noise.
- Build the flow:
- Payload Script — logs the incoming JSON
- JSON to XML Converter — converts the received JSON payload to XML
- Payload Script — logs the converted XML
- XSLT Mapping — adds the namespace/prefix the converted XML needs
- Content Modifier (
Set SAP Request Content-Type) — setsContent-Type: application/jsonbefore calling SAP (§3 step 4) - Request-Reply (
Call SAP - Tax ID Validation) — the actual call to the SAP backend that produces the Tax ID check / credit status / assigned customer number; nothing else in this flow does (details in §3 step 4) - Message Mapping — the actual source-to-target transformation
- Payload Script — logs the mapped JSON payload before returning the response
- Content Modifier (
Set Response Content-Type) — setsContent-Type: application/jsonright before the End Event
The JSON→XML conversion near the start isn't a workaround — it's CPI's standard bridge pattern for a REST-style caller (Salesforce) sending a request to a platform (CPI/SAP) that processes internally as XML.
Message Mapping's Source isn't the Request interface's schema. Message Mapping sits after Call SAP - Tax ID Validation (§3 step 4) in the flow, so the payload actually in the message body at that point is SAP's own response — not a restatement of the Name/BillingCountry/skyvvasolutions__Tax_ID__c fields Salesforce originally sent. This step exists specifically because SAP's response uses its own field names, not Salesforce's — without this mapping, whatever SAP calls its assigned customer number and credit result would never line up with AccountNumber/skyvvasolutions__Credit_Status__c on the way back.
Open the Message Mapping step's editor (double-click it on the canvas): for Source, the preferred approach is Select → Add, uploading the OpenAPI (or WSDL/XSD) file your SAP backend team provides for this response — most external systems worth integrating with document their own response contract, and importing it is both more accurate and easier to keep in sync than reconstructing it from one sample payload. Only fall back to **Select → Define from Example** (a real sample response payload) if the backend genuinely has no published schema to hand you. A worked example of this backend-provided file — tools/cpi-xsd/tutorial13-sap-backend-response.openapi.json — documents a representative response shape:
{
"ValidationResponse": {
"CustomerNumber": "000010971",
"CreditCheckResult": "APPROVED"
}
}
This response body is flat — no wrapping node — so the Source side of the Message Mapping editor will show CustomerNumber/CreditCheckResult directly, not nested under any parent element. That's expected, not a sign anything imported incorrectly.
Note SAP's own field names (CustomerNumber, CreditCheckResult) are not the same as Salesforce's (AccountNumber, skyvvasolutions__Credit_Status__c) — this mismatch is exactly why the mapping step is needed, not optional. For Target (the shape sent back to Salesforce), click Select → Add, upload the Response interface's exported OpenAPI file (step 2), and pick AccountNumber/skyvvasolutions__Credit_Status__c, under its Account node. Then draw the actual field lines: CustomerNumber → Account/AccountNumber, CreditCheckResult → Account/skyvvasolutions__Credit_Status__c.
Figure 10: CPI Message Mapping — Target structure built from the Response interface's exported OpenAPI schema; Source reflects SAP's actual response shape.
- Configure the
Call SAP - Tax ID Validationstep (the Request-Reply added on the connecting arrow between XSLT Mapping and Message Mapping — hover the arrow, click the+that appears, choose Request-Reply, then rename it via its General tab), then wire its output side to a Receiver pool with an HTTPS adapter:
Connection tab:
| Field | Value |
|---|---|
| Address | Your SAP backend's endpoint |
| Proxy Type | Internet, or On-Premise via Cloud Connector for an on-prem SAP backend |
| Method | POST |
| Authentication | Whatever your real backend requires (Basic, OAuth2 Client Credentials, Client Certificate, ...) — None if calling a plain testing mock. Client Certificate needs a Private Key Alias filled in — leaving it blank while that mode is selected will fail. |
| Timeout | 60000 ms is a reasonable default |
| Streaming | Leave unchecked — this response is small |
Retry tab:
| Field | Value | Why |
|---|---|---|
| Attach Error Details on Failure | checked | Attaches the backend's actual error response to the exception — visible in Message Processing Log, useful for diagnosing failures |
| Retry on Exception | unchecked while testing; consider enabling against a real production backend | A validation failure (e.g. malformed Tax ID) is deterministic — retrying won't change the result. Transient network failures against a real backend are a different story. |
| Retry on Error Response | unchecked | Same reasoning — a 4xx rejection isn't going to succeed on retry |
Add a Content Modifier named Set SAP Request Content-Type immediately before this step, setting Content-Type: application/json — needed for the request body to be parsed correctly by most backends.
Figure 11: SAP CPI iFlow — completed flow overview.
Getting a real synchronous response back doesn't need a dedicated CPI setting — it's a consequence of how the flow is built:
- Keep the whole flow inline (no JMS queue or separate Process Direct hand-off) so the caller's connection stays open until the End Event, and receives whatever is in the message body at that point.
- Make sure the last step before the End Event builds the response shape as JSON, matching the fields mapped on the Response interface (§2.3.1) and
Response Format = application/json(§2.4). - Set
Content-Type: application/jsonvia a Content Modifier namedSet Response Content-Typeright before the End Event.
Figure 12: Set Response Content-Type Content Modifier, setting the response Content-Type header before the End Event.
- HTTP status matters too. CPI returns
200 OKby default when the flow completes without an exception, which is fine for the happy path. To signal a validation failure differently, set a different code via theCamelHttpResponseCodeheader on a Content Modifier — optional for this tutorial.
4. Testing
4.1 Push Data from Salesforce to CPI Using Apex
Use the same customer as the outbound Asynchronous tutorial's §4.1 — this tutorial demonstrates the synchronous counterpart: instead of just notifying SAP the customer exists, this call needs SAP's validated Tax ID check and assigned customer number back before Salesforce proceeds.
| Field | Example value | Role in this call |
|---|---|---|
| Name | Nordwind Fahrzeugteile GmbH | Sent — identifies the customer to SAP |
| BillingCountry | DE | Sent — needed for the Tax ID check |
skyvvasolutions__Tax_ID__c | DE813495681 | Sent — custom field, created in §2.3; API name must be exactly skyvvasolutions__Tax_ID__c for the Mapping Tool to line it up with the JSON key of the same name. The VAT/Tax ID SAP validates. |
| AccountNumber | (blank until the response comes back) | Returned — standard Salesforce field; the Response interface writes SAP's assigned customer number here. |
skyvvasolutions__Credit_Status__c | (blank until the response comes back) | Returned — custom field, created in §2.3 (not the package's skyvvasolutions__Credit_Check_Result__c — that field isn't used in this MessageType-less version). No mapping step needed (§2.3.1) — resolves by matching field name, since the Response interface uses External Mapping (§2.2). SAP's credit-check result. |
Request JSON sent to CPI, reflecting the fields mapped onto the Request interface's Account node in the Mapping Tool (§2.3.1):
{
"Account": {
"Name": "Nordwind Fahrzeugteile GmbH",
"BillingCountry": "DE",
"skyvvasolutions__Tax_ID__c": "DE813495681"
}
}
Response JSON CPI returns, matching the fields mapped on the Response interface (§2.3.1):
{
"Account": {
"AccountNumber": "000010971",
"skyvvasolutions__Credit_Status__c": "Approved"
}
}
Ensure at least one Account exists in Salesforce, matching the shape above.
Figure 13: Salesforce Account record used as test data, before the synchronous call.
Navigate to Developer Console → Debug → Open Execute Anonymous Window and run:
//Account_INT_REQ_SFDC2SAP_Sync_OB --Interface
skyvvasolutions.CallOutControl c = new skyvvasolutions.CallOutControl();
c.returnJSONComplete=true;
c.actionDoIntegrate=true;
c.isCreateMessage=true;
String[] ids= new String[]{'0019K00001tyyd9QAA'}; //Id SObject
List<skyvvasolutions.CalloutResponse> result = skyvvasolutions.Iservices.invokeCalloutV3('SAP CPI Integration','Account_INT_REQ_SFDC2SAP_Sync_OB',ids,'SYNC',c);
Click Execute to push the data, then monitor the message in both Salesforce and CPI.
Figure 14: Running the Apex script in the Execute Anonymous window.
Once the synchronous call returns, reopen the same Account record: AccountNumber and skyvvasolutions__Credit_Status__c — both blank in Figure 13 — are now populated from SAP's response, written by the Response interface's mapping (§2.3.1).
Figure 15: Salesforce Account record after the synchronous call — AccountNumber/skyvvasolutions__Credit_Status__c populated by the returned response.
4.2 Salesforce Message Monitor
Navigate to Message Monitoring and search using the interface/integration details. The outbound message should show a green flag (success) status — and because this is a Synchronous call with a Response Interface configured, you should also see an inbound response message cross-linked to it.
Figure 16: Salesforce Message Monitoring — outbound Request message cross-linked to the inbound Response message.
4.3 CPI Monitoring
Navigate to Message Monitoring in CPI to find the corresponding message with its full log, confirming the Account data was received and a response was returned.
Figure 17: SAP CPI Message Monitoring — request received and response returned.
Summary
| Component | Configuration |
|---|---|
| Request Interface | Outbound, Synchronous, Interface API Version = V3, MessageType-less |
| Response Interface | Inbound, Synchronous, Interface API Version = V3, MessageType-less, External Mapping — see §1's warning on the response-processing caveat |
| Custom fields | skyvvasolutions__Tax_ID__c (Request) and skyvvasolutions__Credit_Status__c (Response) — both resolve by matching field name against the JSON key; no Message Type or explicit Mapping Tool step needed for either (§2.3, §2.3.1). |
| Schema for CPI | Response interface's schema exported via Generate MetaData (§3 step 2) for Message Mapping's Target; Source is SAP's own response shape — imported from the backend's published OpenAPI/WSDL/XSD file if they provide one (preferred), or Define from Example otherwise — never from Skyvva's own schema |
| SAP BTP Integration Suite Adapter | Outbound, Basic auth, HTTP, POST, JSON (Request/Response Format = application/json; CPI bridges the outbound request to XML internally via §3's JSON→XML converter step — the response comes back as JSON directly, no converter needed on that side) |
This is the response-carrying counterpart to the sibling outbound Asynchronous tutorial — the adapter configuration is the same between the two; what differs is the Request/Response interface pairing that Processing Mode = Synchronous requires. Keep the record flat and minimal — just the fields the immediate decision needs. If your use case needs to keep Account and Contact data consistent across systems instead, that's what the asynchronous/batch patterns elsewhere in this series are for.