Triggering SAP Analytics Cloud Multi Actions from external environments

Triggering SAP Analytics Cloud Multi Actions from external environments

Introduction

SAC Multi Actions let you chain planning operations (data actions, allocations, value driver trees) into a single execution sequence. Normally you trigger them from the SAC UI or SAC’s own scheduler.

But what if the trigger needs to come from outside SAC? A data pipeline that just finished loading, an integration flow, a business process in another system?

This post shows how to build a CAP Node.js microservice on BTP that exposes a REST endpoint for triggering SAC Multi Actions. Any authorized system that can make an HTTP call can use it.

Before you start, there is one constraint worth knowing: the SAC Multi Actions API does not support OAuth2 Client Credentials. All calls must run under a business user context.

The OAuth2SAMLBearerAssertion flow with a SystemUser is how you deal with this — BTP issues a SAML assertion for a fixed technical user, which SAC accepts as a valid user context. The calling system never has to touch SAC auth directly.

For a detailed introduction to the SAC Multi Actions Public API itself, refer to the official SAP blog post: Introduction of SAC Multi Actions Public API

────────────────────────────────────────────────────────────

Use cases

Trigger after external data loads

Many organizations feed data into SAC from ERP systems, data warehouses, or ETL pipelines. A planner typically has to manually kick off a Multi Action after the data arrives. With this proxy, the pipeline does it automatically as its last step.

SAP Integration Suite orchestration

If you use SAP Integration Suite, you can include a SAC Multi Action as a step in a larger integration flow: receive an ERP event, transform the data, load it into SAC, then immediately trigger a planning calculation.

Automated planning cycles

Finance and supply chain teams running fixed-schedule planning cycles can use any external scheduler to trigger Multi Actions over HTTP instead of relying on SAC’s internal scheduler alone.

RPA and Power Automate

Power Automate or UiPath workflows can trigger SAC planning steps without manual interaction. An approval flow, for example, could automatically kick off a Multi Action to recalculate affected planning figures.

Coming up: SAP Datasphere integration

A follow-up post will cover calling this proxy from SAP Datasphere, so a Datasphere data flow can trigger a SAC Multi Action on completion.

────────────────────────────────────────────────────────────

Concepts you need to know

SAP CAP (Cloud Application Programming Model)

CAP is SAP’s framework for building services on BTP. You define services declaratively in CDS, and CAP handles authentication, routing, and BTP service integration. Here it acts as the server that receives requests and forwards them to SAC.

XSUAA

XSUAA is BTP’s OAuth2 authorization server. It issues JWT tokens that authenticate service-to-service calls. The caller gets a JWT from XSUAA and includes it in every request to the proxy.

BTP Destination Service

The Destination Service is a centralized store for outbound connection configs. Instead of hardcoding URLs and credentials in the application, you define a destination in BTP Cockpit. The SAP Cloud SDK resolves it at runtime; the application code only references it by name.

OAuth2SAMLBearerAssertion

This is the auth flow for calling the SAC API. BTP creates a signed SAML assertion and exchanges it for a SAC OAuth token. With a SystemUser set in the destination, the token always issues for a fixed technical user, which is what you want for automated, non-interactive scenarios.

────────────────────────────────────────────────────────────

Architecture

Here is the full flow end-to-end:

Caller (external app, scheduler, integration flow)
        │
        │  POST /rest/sac/execute_multiaction
        │  Authorization: Bearer <XSUAA JWT>
        ▼
┌────────────────────────────────────────────┐
│              cap-sac-proxy                 │
│          (CAP on Cloud Foundry)            │
│                                            │
│  1. Validate JWT (XSUAA middleware)        │
│  2. Resolve destination (Cloud SDK)        │
│  3. Fetch CSRF token from SAC              │
│  4. POST to SAC Multi Actions API          │
└───────────────────┬────────────────────────┘
                    │
                    │  OAuth2SAMLBearerAssertion
                    ▼
        BTP Destination Service
                    │
                    ▼
        SAP Analytics Cloud API
        POST /api/v1/multiActions/{id}/executions

A few things to note about this design:

  • The caller only needs a BTP JWT. No knowledge of SAC auth required.
  • SAC credentials stay in the Destination Service, not in application code.
  • CSRF handling is internal. SAC requires a token for all writes, and the proxy fetches one before every call.

────────────────────────────────────────────────────────────

Prerequisites

All three steps below are also covered in detail — including screenshots and field-level guidance — in the official SAP blog post Introduction of SAC Multi Actions Public API. The summaries here reflect the configuration used in this project.

1. Set up the SAC OAuth client

In SAC, navigate to System → Administration → App Integration → OAuth Clients and create a new client:

FieldValue
PurposeAPI Access
Authorization GrantSAML 2.0 Bearer

Note: The OAuth client configuration has changed over time. As of 2025, use Purpose: API Access and Authorization Grant: SAML 2.0 Bearer. Earlier releases used “Interactive Usage”; refer to the SAC Multi Actions Public API blog post for version-specific details.

Note the Client ID and Client Secret. You will need both in the next step.

2. Configure the BTP destination

In BTP Cockpit, navigate to your BTP Subaccount → Connectivity → Destinations → New Destination and configure it as follows:

FieldValue
Name<your-destination-name>
TypeHTTP
URLhttps://<your-sac-tenant>.hanacloudservices.cloud.sap
Proxy TypeInternet
AuthenticationOAuth2SAMLBearerAssertion
Audiencehttps://<your-sac-tenant>.hanacloudservices.cloud.sap
Client Key*(OAuth Client ID from SAC)*
Token Service URLhttps://<your-sac-tenant>.hanacloudservices.cloud.sap/oauth/token
Token Service User*(OAuth Client ID from SAC)*
Token Service Password*(OAuth Client Secret from SAC)*
System User<sac-technical-user-email>

The System User field is what makes this work for automated scenarios. It pins the SAML assertion to a specific technical user, so the integration never requires interactive login.

After saving, click Check Connection to verify BTP can reach SAC successfully.

Important: This destination must be created in the same BTP subaccount where you will deploy the CAP proxy. The SAP Cloud SDK only searches for destinations within the subaccount it is running in.

Note on SystemUser: The SystemUser additional property in BTP destinations is deprecated but can still be added manually as a custom additional property (Name: SystemUser, Value: <sac-user-email>). As an alternative, user propagation via JWT token is the recommended long-term approach. See the SAP Help Portal for details.

3. Configure trust between BTP and SAC

The OAuth2SAMLBearerAssertion flow works by having BTP sign a SAML assertion with its own X.509 certificate and send it to SAC in exchange for an OAuth token. For SAC to accept this, it must first trust the BTP subaccount as an identity provider.

You only do this once, but it must be done before the destination connection check will pass.

Download the IDP metadata and trust certificate from BTP

In BTP Cockpit, navigate to your subaccount → Connectivity → Destinations. At the top of the destinations list, click Download IDP Metadata. Open the downloaded XML file and note the entityID value from the <ns3:EntityDescriptor> element — you will need this as the Provider Name in SAC.

Next, click Download Trust. This downloads the public X.509 certificate that BTP uses to sign SAML assertions. Copy the certificate content — you will paste it into SAC in the next step.

Register BTP as a trusted identity provider in SAC

In your SAC tenant, navigate to System → Administration → App Integration. Scroll to the Trusted Identity Providers section and click Add a Trusted Identity Provider.

FieldValue
NameA descriptive name (e.g. BTP-<subaccount-name>)
Provider NameThe entityID from the IDP metadata XML
Signing CertificateThe certificate content from Download Trust

Save the configuration, then return to BTP Cockpit → Connectivity → Destinations → open your SAC destination → click Check Connection to verify. A Missing key pair for subaccount error usually means the certificate is not registered in SAC yet, or the Provider Name does not match the entityID from the IDP metadata.

────────────────────────────────────────────────────────────

Building the CAP proxy service

Project structure

The service is intentionally minimal:

cap-sac-proxy/
├── srv/
│   ├── sac-service.cds      # Service definition
│   ├── sac-service.js       # Request handler
│   └── lib/
│       └── sac-client.js    # SAC API logic
├── xs-security.json         # XSUAA configuration
└── mta.yaml                 # BTP deployment descriptor

Service definition

CAP uses a declarative language called CDS to define services:

@protocol: ‚rest‘
@requires: ‚authenticated-user‘
service SACService {
  action execute_multiaction(multiaction_id: String) returns {};
  action create_publication(action: String, @open data: {}) returns {};
}

@protocol: ‚rest‘ exposes this as a plain REST API rather than OData. @requires: ‚authenticated-user‘ tells CAP to validate the incoming XSUAA JWT and reject unauthenticated requests with a 401.

The CSRF token flow

SAC protects write operations with CSRF tokens. Before any POST, you need a valid token from SAC to include in the request.

The proxy handles this automatically:

async function getCsrfToken() {
  const destination = await resolveDestination();
  const response = await executeHttpRequest(destination, {
    method: ‚GET‘,
    url: ‚/api/v1/csrf‘,
    headers: { ‚x-csrf-token‘: ‚fetch‘ }
  });
  return {
    csrfToken: response.headers[‚x-csrf-token‘],
    cookies: response.headers[’set-cookie‘]
  };
}

The caller never touches CSRF tokens. Every execute_multiaction call fetches a fresh one first.

Triggering a Multi Action

async function triggerMultiAction(multiactionId) {
  const { csrfToken, cookies } = await getCsrfToken();
  const response = await executeHttpRequest(
    { destinationName: DESTINATION },
    {
      method: ‚POST‘,
      url: `/api/v1/multiActions/${multiactionId}/executions`,
      headers: {
        ‚x-csrf-token‘: csrfToken,
        ‚Cookie‘: [].concat(cookies).join(‚; ‚),
        ‚Content-Type‘: ‚application/json‘
      },
      data: { parameterValues: [] }
    }
  );
  return response.data;
}

The SAP Cloud SDK’s executeHttpRequest handles the OAuth2 token exchange automatically. It reads the destination, runs the SAML Bearer Assertion flow, and attaches the correct Authorization header to the outgoing SAC request.

────────────────────────────────────────────────────────────

Deploying to BTP Cloud Foundry

What gets deployed

The mta.yaml file describes the full deployment. The MTA deployer creates everything automatically:

  • The CAP application itself on Cloud Foundry
  • An XSUAA service instance for validating incoming JWT tokens
  • A Destination service instance for resolving the SAC destination at runtime

Before you build

Open srv/lib/sac-client.js and update the destination name to match what you configured in BTP Cockpit:

const DESTINATION = ‚<your-destination-name>‘;

Build and deploy

# Install build tool (once)
npm install -g mbt

# Log in to Cloud Foundry
cf login –sso -a https://api.cf.<region>.hana.ondemand.com
cf target -o „<your-org>“ -s „<your-space>“

# Build
mbt build

# Deploy
cf deploy mta_archives/cap-sac-proxy_1.0.0.mtar

The deploy output will show your application URL:

Verify the deployment

cf apps        # check app is running
cf logs cap-sac-proxy-srv –recent   # check startup logs

────────────────────────────────────────────────────────────

Calling the API

The repository includes a ready-to-use Postman collection at cap-sac-proxy.postman_collection.json. Import it into Postman and fill in the collection variables before running any request.

Collection variables

VariableWhere to get it
baseUrlYour deployed app URL + /rest/sac (from cf apps)
xsuaaUrlThe url field from the XSUAA service key
clientIdThe clientid field from the XSUAA service key
clientSecretThe clientsecret field from the XSUAA service key
multiactionIdYour Multi Action ID from SAC (see below)

To get the XSUAA service key values:

cf create-service-key xsuaa-cap-sac-proxy my-key
cf service-key xsuaa-cap-sac-proxy my-key

The accessToken variable is filled in automatically after running the Auth request.

Auth — Get Token

Sends a client credentials request to XSUAA:

POST {{xsuaaUrl}}/oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials
&client_id={{clientId}}
&client_secret={{clientSecret}}

The test script in the request automatically writes the returned access_token into the accessToken collection variable, so subsequent requests pick it up without any manual copy-paste.

Execute Multiaction

Triggers a SAC Multi Action by ID:

POST {{baseUrl}}/execute_multiaction
Authorization: Bearer {{accessToken}}
Content-Type: application/json

{
  „multiaction_id“: „{{multiactionId}}“
}

The Multi Action ID can be found in SAC under Multi Actions → [your action] → URL or API details.

A successful response looks like:

{
  „status“: „RUNNING“,
  „executionId“: „…“
}

────────────────────────────────────────────────────────────

Conclusion

That is it. An external system makes one HTTP call with a BTP JWT, and SAC runs the Multi Action. The proxy handles CSRF tokens, SAML assertions, and OAuth exchanges without exposing any of it to the caller.

If you are setting this up for the first time, the trust configuration between BTP and SAC is where things tend to go wrong. It can fail silently, and the error messages are not always helpful. Get that working first and the rest is straightforward.

What’s next

The next post covers calling this proxy from SAP Datasphere to close the loop between data integration and planning.

Full source: cap-sac-proxy