Skip to Content
Product Information
Author's profile photo Sindy Zhan

Your First End-to-End Tutorial of Event Mesh on SAP Business Technology Platform (China, Shanghai Region)

Here is an end to end hands-on about using SAP Event Mesh on SAP Business Technology Platform (China, Shanghai region, or cf-cn40 landscape). You can find all the code snippets used in this blog post in this GitHub repository.

The process of renaming (from Enterprise Messaging to Event Mesh) and the rollout of the free service plan is still undergoing, and we will update this tutorial according to the latest changes.


  • Global Account and Subaccount on SAP Business Technology Platform (China, Shanghai Region)
  • Entitlements of SAP Event Mesh, SAP Business Application Studio


Set Up Service Instance and Subscription

First, we go to Cloud Cockpit and set up service entitlements, instances, subscriptions and user authorizations.



Assign one of each following entitlement to your Subaccount:

Service Technical Name Plan
Event Mesh enterprise-messaging default
Enterprise Messaging enterprise-messaging-hub standard

The first entitlement is to create a message client service instance, and the second one is to provide a management cockpit, or dashboard as the name given.

Service instance

In your cf space, create a service instance for Event Mesh (technical name: enterprise-messaging), with the service plan default.

In the wizard, you are asked to fill in a JSON-formatted service descriptor (following the Syntax for Service Descriptor), you can copy and paste the following, and change the parameters:

    "emname": "<message-client-name>", // fill in your message client name
    "namespace": "<your-namespace>", // fill in your namespace
    "version": "1.1.0",
    "options": {
        "management": true,
        "messagingrest": true,
        "messaging": true
    "rules": {
        "queueRules": {
            "publishFilter": [
            "subscribeFilter": [
        "topicRules": {
            "publishFilter": [
            "subscribeFilter": [

ūüĒĖ¬†Besides Cloud Cockpit, you can also do this step via CF CLI.


Service key

To create a service key, in the cockpit, select service instance ūüĎȬ†create service key¬†ūüĎȬ†just type in the name of the service key¬†ūüĎȬ†and hit create.

Take a look at the auto-generated JSON-formatted service key, and find the key-value pairs of:

  • namespace,
  • management API URI, and three messaging API URIs (typically the URI for HTTP REST protocol),
  • client id, client secret and token endpoint.

You will make use of them later.


Subscription and role collections

In the cockpit, subscribe to Enterprise Messaging (technical name: enterprise-messaging-hub), standard service plan.

Add role collections and assign users for the application subscription. Role collections are already created, so you can assign users, i.e. your SAP account to the collections to get access to Enterprise Messaging.

Now you can go to the application “Event Mesh Dashboard” by clicking the link provided in the subscription and have some explorations, for instance, you can go to the message clients¬†page, and find the message client you just created when you create your service instance.


Create a queue and test the messaging via the dashboard


ūü騬†Scenario: you want to watch CCTV New Year’s Gala. You are busy but you don’t want to miss all the comedies. CCTV will send you messages when a performance starts, and the message will tell you if it is comedy.

You need to first create a queue named¬†comedy. On page¬†SAP Event Mesh Dashboard¬†ūüĎȬ†on the left menu, select tab¬†Message Clients¬†ūüĎȬ†go into the tile¬†mymessageclient¬†ūüĎȬ†tab¬†Queues¬†ūüĎȬ†click¬†Create Queue¬†ūüĎČ in the pop-out window, fill in the parameters: queue name = comedy, and leave others as default.


Hit Create, and you can see the queue myorg/mymessageclient/001/category has been created, with zero messages currently.

ūüĒĖ¬†You don’t have to add namespace as a prefix to your queue name as it will be attached.



Go to Test, on the left-hand side, select the queue and message client, fill in the Message Data¬†parameters, hit¬†Publish Message. You can see a “published” notification flashing out, and there is one message in the queue now.

ūüď䬆Sample Message Data

  • Content-Type: application/json
  • Body: {“time”: “20210124 21:00:00”, “category”: “music”}

On the right-hand side, select the queue and message client, and hit Consume Message. You can receive the message data you just published, and the number of messages in the queue goes down to zero.


Create a queue and send message to the queue, via messaging API (postman)

ūü騬†Scenario: your daughter also watches the show. She wants to know when she can see her idols on the screen, so she is interested in CCTV’s messages about the artists screen time.

To create a queue, Event Mesh provides a type of APIs to manage the queue, queue subscription, etc., REST APIs for Management, to publish and consume messages, the type is REST APIs for Messaging.

We use Postman to send HTTP requests. Remember to find URLs and other values from the service key.


Get access token

Before communicating with Event Mesh services via APIs, we need to get the access token from XSUAA. This Help document can guide you through the process of fetching access token by providing client id and client secret to the token endpoint.

What you need to fill in the postman:

Tab Key Value
Bar Method choose GET
Bar URL <tokenendpoint>?grant_type=client_credentials&response_type=token
Authorization Type choose Basic Auth
Authorization Username <clientid>
Authorization Password <clientsecret>

Send the request, you will get 200 OK response, with the access_token. The access token needs to be included in the header of the next few requests: Authorization: Bearer {{access_token}}.


Create a queue

In the document about API, you can find the API to create queue is PUT /hub/rest/api/v1/management/messaging/queues/{queue}.

The parameter in URL: {queue} is the URL encoded queue name with a namespace prefix. e.g.¬†myorg/mymessageclient/001/artist¬†–>¬†myorg%2Fmymessageclient%2F001%2Fartist.

What you need to fill in the postman:

Tab Key Value
Bar Method choose PUT
Bar URL <management[0].uri>/hub/rest/api/v1/management/messaging/queues/{queue}
Authorization Type choose No Auth
Headers Authorization Bearer <access_token>

The body is optional, schema QueueP is defined with default values.

Send the request and you will get the response of 201 Created, with the information of the created queue.

To list the queues, use GET /hub/rest/api/v1/management/messaging/queues. Instead of Method and Path, all other parameters remain the same. Send the request, you can see all the queues.


Send and consume message via message API

To send a message to a queue, we can find the API in messaging API POST /messagingrest/v1/queues/{queue-name}/messages. Parameter {queue-name} in URL again should be URL encoded full name with a namespace prefix.

Tab Key Value
Bar Method choose POST
Bar URL <messaging[protocol=httprest].uri>/messagingrest/v1/queues/{queue-name}/messages
Authorization Type choose No Auth
Headers x-qos sample 0
Headers Authorization Bearer <access_token>
Body raw РJSON sample {"time": "20210124 21:00:00", "artists": [{"name": "TFBOYS"}, {"name": "Ye Zhang"}]}

Header x-qos is the quality of service, 0 means as long as the message is sent, mission completed; 1 means make sure the receiver get at least one message.

For Body, you can choose raw type, and type in any text, for cloud event you can have a defined JSON format.

The ID of the created message will be returned in the header of the response.

To consume a message from the queue, POST /messagingrest/v1/queues/{queue-name}/messages/consumption. Use the same header settings, and leave the body blank.

All postman config files can be found in the folder ./apis.


Create a topic, and publish messages to the topic

ūü騬†Scenario: if CCTV only sends one type of message, performance, containing the performers and category information that you and your daughter care about. to prevent that one message will be retained until you or your daughter consumes it, you two decide to maintain two personal queues. and subscribe to the performance topic, aka queue subscription.


Create a topic (queue subscription) for artist via dashboard

You can subscribe via the dashboard. Note that the queue can only subscribe to topics that follow the rules defined in the service descriptor. here it means the topic should have the same namespace prefix as the queue.


Publish messages to the topic

Send a message to the topic and you will find the queue artist receives the message.

In the postman, we use API POST /messagingrest/v1/topics/{topic-name}/messages to send the request.

Tab Key Value
Bar Method choose POST
Bar URL url encoded <messaging[protocol=httprest].uri>/messagingrest/v1/topics/{topic-name}/messages
Authorization Type choose No Auth
Headers x-qos sample 0
Headers Authorization Bearer <access_token>
Body raw РJSON sample {"time": "20210124 21:05:00", "name": "A Funny Comedy", "category": "comedy", "artists": [{"name": "Ling Jia"}], "status": "START"}

In the dashboard, you will find that the number of messages in the queue artist increases by 1.


Create queue subscription for queue category via APIs

Via management APIs, in postman, we use PUT /hub/rest/api/v1/management/messaging/queues/{queue}/subscriptions/{topic}.

Tab Key Value
Bar Method choose PUT
Bar URL URL encoded <management[0].uri>/hub/rest/api/v1/management/messaging/queues/{queue}/subscriptions/{topic}
Authorization Type choose No Auth
Headers Authorization Bearer <access_token>

Parameters in URL queue and topic they all need to be encoded with prefix. Leave the body blank.

If the topic creation successfully proceeds, the response will be 201 Created, with the names of the queue and the topic as the body.

The current architecture:

architecture: adopted from SAP Help Portal


Publish messages to the topic and consume the messages from two queues via APIs

Send a message to the topic.

Tab Key Value
Bar Method choose POST
Bar URL URL encoded <messaging[protocol=httprest].uri>/messagingrest/v1/topics/{topic-name}/messages
Authorization Type choose No Auth
Headers x-qos sample 0
Headers Authorization Bearer <access_token>
Body raw РJSON sample {"time": "20210124 21:00:00", "name": "Go, Amigo and Hi, Motherland Mash Up", "category": "music", "artists": [{"name": "TFBOYS"}, {"name": "Ye Zhang"}], "status": "START"}

You can see both of the queues can receive the message.

Let’s consume the message from the two queues.


[optional] Develop producer and consumer applications on SAP BTP

In this tutorial Tutorial: Develop a Messaging App on SAP BTP, you can learn how to develop Node.js-based and Java-based message cunsomer or producer applications on BTP.

SAP has provided full collections of development tool sets like Node.js packages, Java libraries, and other programming languages, to help you develop messaging applications logic in XS Advanced (i.e. SAP HANA Extended Application Services advanced model, the Cloud Foundry PaaS with enhancements provided by SAP)).



For development in JavaScript, you can make use of Standard Node.js Packages for XS Advanced:

  • @sap/xb-msg: Provides messaging capabilities with a message broker. This package supports the RabbitMQ message broker with the following protocols: amqp v091, amqp v100, mqtt v311
  • @sap/xb-msg-env: Provides the functions needed to set up messaging client options from Cloud Foundry or XS advanced environment variables
  • @sap/xsenv: Utility for easy setup and access of XS Advanced environment variables and services



For development in Java, you can leverage SAP Cloud Application Programming Model (libraries:*), following Java Message Service (JMS) 2.0 specification, :

  • MessagingService,
  • MessagingServiceJmsConnectionFactory,

For the SAP Cloud SDK, features of Event Mesh for both of JavaScript and Java is planned, and its progress depends on CAP.


Create a webhook (nodejs), deploy it (application studio), and subscribe your webhook to a queue

ūü騬†Scenario: now you don’t want to check the queue from time to time for the comedy performances, you want to build a webhook which can send you notification when there is a performance message from the CCTV, and the performance is comedy.

Instead of consumers fetching messages from the message client, if we add a webhook, the message client will POST the messages from the specified queue to the specified URL. Here we only create a webhook for the queue comedy.

The current architecture of a webhook looks like this:


architecture with webhook: adopted from SAP Help Portal

We provide an as-simple-as-possible webhook application sample here, which is adopted from Part 1 of the series of Youtube: Diving into messaging on SAP Cloud Platform (1-8).


Create and deploy

Go to application studio¬†ūüĎȬ†create a¬†Full Stack Cloud Application¬†Dev Space named¬†emtester, go into the dev space¬†ūüĎȬ†Create Project¬†ūüĎȬ†select template and target location¬†Basic Multitarget Application¬†ūüĎȬ†Start¬†ūüĎȬ†type in the project name¬†webhook, finish.

Delete all the auto-created files and folders. create file manifest.yml. copy and paste the following content:

- name: webhook
  - route: <host-name>.<domain>
  path: .
  memory: 128M

Replace the name with your app name, route with your host name concatenated with your custom domain, e.g.

If you don’t know which domain you can make use of, open terminal¬†ūüĎȬ†cf login¬†ūüĎȬ†choose the targed org and space¬†ūüĎȬ†cf domains¬†ūüĎȬ†find one.

In the terminal, go to the current folder, npm init and hit return to get all the default values. type in ok. You will find the file package.json is created, and it requires a main page index.js.

Install the two Node.js packages we will use, express and body-parser, by npm install express body-parser

ūüĒĖ¬†What do these two packages do?

express is a Node.js package to provide small, robust tooling for HTTP servers, making it a great solution for single page applications, web sites, hybrids, or public HTTP APIs.

body-parser is a Node.js package to parse incoming request bodies in a middleware before your handlers, available under the req.body property.

We want to add start script, so add key-value pair start in the scripts, and delete the test from the scripts. Finally the file package.json will look like:

    "name": "webhook",
    "version": "1.0.0",
    "description": "",
    "main": "index.js",
    "scripts": {
      "start": "node index.js"
    "author": "",
    "license": "ISC",
    "dependencies": {
      "body-parser": "^1.19.0",
      "express": "^4.17.1"

Now let’s work on the file¬†index.js, just create it and copy and paste:

const express = require("express")
const bodyParser = require("body-parser")

const app = express()
const PORT = process.env.PORT || 8080


app.get('/', (req, res) => {
    res.status(401).send('Hey, this is a webhook - we only receive POST!')
})'/', (req, res) => {
    if(req.body.category == "comedy") {
        console.log(' ', req.body)
        res.status(200).send(' ' +
    else {
        console.log(' ', req.body)

app.listen(PORT, () => console.log(`  Server running on port ${PORT}`))

[Optional] If you want to test the app in local terminal, go to the project root, run npm start, and in a new tab use curl to send request to localhost:

curl -H 'Content-Type: application/json' -d'{"time": "20210124 21:00:00", "name": "Go, Amigo and Hi, Motherland Mash Up", "category": "music", "artists": [{"name": "TFBOYS"}, {"name": "Ye Zhang"}], "status": "START"}' http://localhost:8080/

curl -H 'Content-Type: application/json' -d'{"time": "20210124 21:05:00", "name": "A Funny Comedy", "category": "comedy", "artists": [{"name": "Ling Jia"}], "status": "START"}' http://localhost:8080/

The first request will leave a log with sad face, and the second request will make the webhook app to log a happy face.

Deploy it onto BTP. In the terminal, go to project root, run cf push.

Test the webhook by sending a message to the webhook with the postman (or curl from the terminal).

All the code snippets can be found in the folder ./webhook/.


Create webhook subscription

Go to dashboard¬†ūüĎȬ†go into our message client¬†ūüĎȬ†choose¬†webhooks¬†tab¬†ūüĎȬ†creat webhook subscription¬†ūüĎȬ†in the pop-up window, fill in queue name, webhook url and other parameters¬†ūüĎȬ†click¬†create.

The process of activation will be completed by making a handshake between the message client and your webhook application, during the first message exchange.


Send a message to the topic performance, and read the logs of the webhook application webhook

Then publish a message to the topic performance again. You will find, artist queue has lined up with messages to be consumed, while category queue has zero messages. It is because the messages to the latter queue have been forwarded to the webhook.

Go to cf logs webhook to see the logs, you will find that all the two messages have been logged, labelled with an interested or not interested label.

If you bind the SAP Application Logging Service to your webhook application (here is the How-To on SAP Help Portal), you can also observe the logs on the cockpit.



In this blog post, we first set up SAP Event Mesh service instance and subscription on Cloud Cockpit. Then, we tried to create queues via SAP Event Mesh Dashboard and HTTP API calls, and we tested the messaging of the queues by publishing messages to the queues and consuming the messages. We also created a queue subscription and tested it by publishing messages to the topic and consume them from the queues. Next, we build a webhook, deployed it to the BTP CF environment, and subscribe it to one of the queues. By now, you have walked through a full end-to-end scenario of SAP Event Mesh on cn40.

If you have further questions, feel free to leave me a comment here, or submit a question in the Q&A area on this topic.


Assigned Tags

      You must be Logged on to comment or reply to a post.
      Author's profile photo Ajit Kumar Panda
      Ajit Kumar Panda

      Hi Sindy,

      Nicely written and well explained.

      However some images are not clear (probably because of links poiting to SAP's internal github  account ). Also github repository link (first paragraph) is pointing to sap's internal github corporate account. Many wont be able to access that url. not sure if that was intenational.

      Thanks, Ajit

      Author's profile photo Sindy Zhan
      Sindy Zhan
      Blog Post Author

      Hi Ajit,

      Thank you for your comments!

      Also thank you for pointing out the picture issue. I didn't notice that I also attached the link of the internal github repo to some pictures. Now I have removed the hyperlinks.

      Best regards,


      Author's profile photo Ajit Kumar Panda
      Ajit Kumar Panda

      Hi Sindy,

      Pictures are more clear now. Thank you for updating them. Just one last request. GitHub Repository link still points internal account.

      May be you could put the repository here so that everyone can access:

      Best Regards, Ajit

      Author's profile photo Sindy Zhan
      Sindy Zhan
      Blog Post Author

      Hi Ajit,

      Thanks for your suggestions, good idea!

      I will figure out how to put my repo to

      Best regards,


      Author's profile photo Syambabu Allu
      Syambabu Allu

      Hi Sindy,

      Thanks for sharing in detailed blog on SAP Event mesh.

      Thank you,


      Author's profile photo Sindy Zhan
      Sindy Zhan
      Blog Post Author

      Hi Syam,

      Thank you so much for your reading and replying!



      Author's profile photo Minh Tri Le
      Minh Tri Le

      Hi Sindy Zhan ,

      Thanks for great info.

      I have a question: what if I want to consume a particular message, not all messages, do you have any ideas how we can achieve that?

      I've checked the SAP Event Mesh Messaging API, but it seems it can consume all messages. I couldn't see anywhere I can consume a particular message.



      Author's profile photo shyam singh
      shyam singh

      Hi Sindy,

      Very informative and detailed blog. Looking forward to more blogs .



      Author's profile photo ebsor ---
      ebsor ---

      Hi Sindy,

      I don't have Enterprise Messaging in list of entitlements , is there a way to get this?

      best regards,







      Author's profile photo Sindy Zhan
      Sindy Zhan
      Blog Post Author

      Hi Zaby,

      Which landscape are you using? If it is AC-Canary or other SAP-operated landscapes, as an internal user you can assign yourself entitlements in the control center. If it is AC-Live, you should open a ticket to CDC and let them assign entitlements for you.



      Author's profile photo Keerthana Jayathran
      Keerthana Jayathran

      Hi Sindy Zhan

      Is Event Mesh available in trial account(US East) also.I could subscribe to it but when i click on go to application its showing Not Found.I created instance,service key and assigned user to role colllection.

      Best Regards,

      Author's profile photo sachin khedkar
      sachin khedkar

      Hi Sindy,

      I am struggling & confuse on the JSON file creation which we need to uploaded during the creation of DEV instance for SAP Event Mesh.

      Could you pls. help me to create the same by sharing the details like what to put in JSON file & from where i get these required details.


      Author's profile photo Carsten Zimmermann
      Carsten Zimmermann

      Hello colleagues,

      Sindy, thanks for the comprehensive tutorial.

      I keep getting the following error when I try to push the app towards cloud foundry (I removed some stuff in the brackets for security reasons):

      Pushing app <appname> to org <blablabla> / space <myspace> as <myemail>...
      Applying manifest file /home/user/projects/<appname>/manifest.yml...
      For application '<appname>': No domains exist for route <hostname followed by domain>

      What is going wrong? Do I need to create a compatible domain first?



      Author's profile photo Ian Stubbings
      Ian Stubbings

      Hi Sindy

      Thanks for the very comprehensive post. I am finding it very useful.

      I do have one issue though. When PUTing the request to create a message queue via Postman, I get an Unauthorised message, even though I have retrieved a bearer token successfully.

      Any idea why?



      Author's profile photo Sindy Zhan
      Sindy Zhan
      Blog Post Author

      Hi Ian,

      Thank you for reading the blog post and share your feedback!

      Have you tried creating key-value pair for the token as an entry in the header, like this:

      Key: "Authorization"

      Value: "Bearer <the token>"

      Best regards,


      Author's profile photo Ian Stubbings
      Ian Stubbings

      Hi Sindy

      Thanks for the quick response.

      I am using the key value pair as suggested, yes.  I can retrieve the bearer token fine but cannot then interact with the service.



      Author's profile photo Ian Stubbings
      Ian Stubbings

      Hi Sindy

      An update to the above.

      We are using two message clients and I wasn't aware the client id/secret were specific for each one.

      Also, the URL being used was not correct. I needed the enterprise-messaging-pubsub one and the not the one I was using that specified the tenant instance at the beginning.

      All good now.



      Author's profile photo Sensen Wei
      Sensen Wei

      Hi Sindy Zhan

      How interesting!

      One question: Is there any way to reserve the message even after consumed by webhook? Because I want to build a webhook just for logging the messages, and do not want any impact on original subscription.



      Author's profile photo Ribhav Sharma
      Ribhav Sharma

      Hi Sindy,

      Thanks for the informative Blog.

      I was trying to implement EDA through Enterprise Event Enablement by triggering standard S4 events from API business hub to Event Mesh and then passing the payload to the Nodejs service using Webhook subscription i.e. the same service in the blog.

      S4 (on-premise) -> Event Mesh -> Nodejs Service using Webhook subscription.

      When I am trying to trigger a standard event on S4 side (PurchaseOrder), I am only getting an empty payload as an output in the logs of the Nodejs service.

      Expected Payload:


      Also, while trying to publish the event from a rest client (Postman), I am getting the expected payload on the Nodejs logs as described in the blog. I believe, the Nodejs service is not able to extract the payload from the Event Mesh queue hence the empty payload.

      Could you please guide me with the process/steps to be followed for achieving the above.



      Author's profile photo Wolfgang Bauer
      Wolfgang Bauer


      when we use standard S4 events is it possible to configure which fields are transferred with the event?

      I want more than the sales order.