SAP provides three event-driven messaging services on BTP, each targeting different complexity and scale requirements. SAP Cloud Application Event Hub (Event Broker) is the entry-level option included with BTP licenses, handling up to 75 million transactions. SAP Event Mesh (formerly Enterprise Messaging) is the mid-tier offering supporting open protocols with a 1 MB message size cap. SAP Integration Suite, Advanced Event Mesh (AEM) is the enterprise-grade solution powered by Solace PubSub+ with up to 30 MB messages and on-premise support. [src3]
| Property | SAP Event Mesh | SAP Advanced Event Mesh |
|---|---|---|
| Vendor | SAP | SAP / Solace |
| Platform | SAP BTP | SAP BTP + On-Premise + Multi-Cloud |
| Max Message Size | 1 MB | 10-30 MB (tier-dependent) |
| Max Storage | 10 GB | 25 GB - 1 TB (tier-dependent) |
| Protocols | AMQP 1.0, MQTT 3.1.1, HTTP REST, Webhook | AMQP 1.0, MQTT 3.1.1, JMS, REST, SMF, WebSocket |
| Deployment | Cloud (BTP-only) | Cloud, On-Premise, Hybrid |
| Monitoring | Limited | Advanced (event replay, tracing) |
| API Docs | SAP Help Portal | Solace Cloud Docs |
| Status | GA | GA |
Event Mesh focuses on open web protocols over WebSocket, while AEM adds enterprise protocols (JMS, SMF) and native WebSocket support via the Solace stack. [src4, src2]
| API Surface | Protocol | Available In | Best For | Real-time? | Notes |
|---|---|---|---|---|---|
| AMQP 1.0 | AMQP over WebSocket | Both EM + AEM | Enterprise app-to-app, guaranteed delivery | Yes | Open standard; queuing, routing |
| MQTT 3.1.1 | MQTT over WebSocket | Both EM + AEM | IoT, low-bandwidth, edge | Yes | Lightweight; recommended for non-cloud |
| HTTP REST | HTTPS/JSON | Both EM + AEM | Simple publish/consume | Yes | Stateless; no persistent connection |
| Webhook | HTTP POST | Both EM + AEM | Push notifications | Yes | Use when consumer lacks AMQP/MQTT |
| JMS | Java Message Service | AEM only | Java EE, legacy middleware | Yes | Full JMS 1.1 compliance |
| Solace SMF | SMF over WS/TCP | AEM only | High-throughput, low-latency | Yes | Proprietary; highest performance |
| WebSocket | WS/WSS | AEM only | Browser and real-time web | Yes | Solace Web Messaging library |
| Limit Type | Value | Notes |
|---|---|---|
| Max message size | 1 MB | All protocols; includes headers + payload [src1] |
| Max storage capacity | 10 GB | Total across all queues [src3] |
| Guaranteed throughput | 250 KB/s (min) | Guaranteed minimum [src1] |
| Max consumers per queue | 3 | Per connection [src1] |
| Max webhook unacked messages | 9 | Backpressure applied after 9 [src7] |
| Connections per instance | 50 | Bound by resource units [src6] |
| Limit Type | Broker 100 | Broker 250 | Broker 1K | Broker 5K | Broker 10K | Broker 50K | Broker 100K |
|---|---|---|---|---|---|---|---|
| Max message size | 10 MB | 10 MB | 10-30 MB | 30 MB | 30 MB | 30 MB | 30 MB |
| Message spool | 25 GB | 50 GB | 200 GB | 400 GB | 600 GB | 800 GB | 1,000 GB |
| Client connections | 100 | 250 | 1,000 | 5,000 | 10,000 | 50,000 | 100,000 |
| Enterprise connections | 100 | 250 | 1,000 | 5,000 | 10,000 | 30,000 | 30,000 |
| IoT connections | 100 | 250 | 1,000 | 5,000 | 10,000 | 50,000 | 100,000 |
| Max queued messages | 240M | 240M | 240M | 3B | 3B | 3B | 3B |
| Queues + topic endpoints | 100 | 250 | 1,000 | 5,000 | 10,000 | 50,000 | 100,000 |
| Transacted sessions | 100 | 100 | 1,000 | 5,000 | 10,000 | 10,000 | 10,000 |
| Topic subscriptions | 500K | 500K | 500K | 5M | 5M | 5M | 5M |
Note: Broker 1K supports 10 MB max message on firmware v10.25.8.3179-18 and earlier, 30 MB on v10.25.8.3179-20+. [src2]
| Flow | Service | Use When | Token Lifetime | Notes |
|---|---|---|---|---|
| OAuth 2.0 Client Credentials | Event Mesh | BTP service binding | Configurable (12h typical) | Standard BTP XSUAA binding |
| OAuth 2.0 Client Credentials | AEM | REST API management | Configurable | Solace Cloud API token |
| Client Username + Password | AEM | AMQP/MQTT/SMF clients | Session-based | Per Message VPN |
| Client Certificate (TLS) | AEM | Mutual TLS high-security | Certificate validity | All protocols supported |
httprest, amqp10ws, and mqtt311ws protocol endpoints — use the correct one. [src5]START — Need event-driven integration in SAP landscape
|-- What scale do you need?
| |-- Simple SAP cloud-to-cloud eventing (<10 GB, <1 MB messages)
| | |-- CPEA license? YES -> Event Mesh | NO (BTPEA) -> Event Hub
| | |-- Need webhook push? YES -> EM + webhook (max 9 unacked)
| | '-- No webhook? -> EM with AMQP/MQTT consumer
| |-- Enterprise-grade (>1 MB, >10 GB, or >50 connections)
| | |-- Messages > 1 MB? -> Advanced Event Mesh
| | |-- Need on-prem broker? -> Advanced Event Mesh (only option)
| | '-- Need event replay? -> Advanced Event Mesh
| '-- IoT / high-volume (>10K connections)
| '-- Advanced Event Mesh Broker 10K or higher
|-- Which protocol?
| |-- AMQP 1.0 -> Both EM and AEM
| |-- MQTT 3.1.1 -> Both EM and AEM
| |-- HTTP REST -> Both EM and AEM
| |-- JMS -> AEM only
| '-- SMF -> AEM only (highest throughput)
'-- S/4HANA business events?
|-- Notification (no payload) -> Standard, EM or AEM
|-- Full-state (with payload) -> Need Event Add-On for ERP
'-- Custom events -> RAP business events or CAP-based
| Capability | Event Mesh (Default) | AEM (Broker 1K) | AEM (Broker 100K) |
|---|---|---|---|
| Max message size | 1 MB | 10-30 MB | 30 MB |
| Storage | 10 GB | 200 GB | 1,000 GB |
| Connections | 50/instance | 1,000 | 100,000 |
| Protocols | AMQP, MQTT, REST, Webhook | AMQP, MQTT, JMS, REST, SMF, WS | AMQP, MQTT, JMS, REST, SMF, WS |
| Deployment | BTP cloud only | Cloud + On-Prem + Hybrid | Cloud + On-Prem + Hybrid |
| Event replay | No | Yes | Yes |
| Dynamic Message Routing | No | Yes | Yes |
| Queue depth | Limited by 10 GB | 240M messages | 3B messages |
| Topic subscriptions | Limited by RU | 500K | 5M |
| Monitoring | Basic BTP cockpit | Solace Event Portal | Solace Event Portal |
| Setup complexity | Low | Medium | Medium-High |
| Cost | Low (CPEA credits) | Medium | High |
Create a service instance in the BTP Cockpit or via the CF CLI. [src5]
# Create service instance via CF CLI
cf create-service enterprise-messaging default my-event-mesh \
-c '{"emname":"my-event-mesh","namespace":"sap/btp/myapp","version":"1.1.0","options":{"management":true,"messagingrest":true,"messaging":true}}'
# Create a service key for credentials
cf create-service-key my-event-mesh my-key
cf service-key my-event-mesh my-key
Verify: cf service my-event-mesh -> expected: status: create succeeded
Use the service key credentials to obtain an OAuth token, then publish. [src4, src5]
# Get OAuth token
TOKEN=$(curl -s -X POST "$TOKEN_URL" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials&client_id=$CLIENT_ID&client_secret=$CLIENT_SECRET" \
| jq -r '.access_token')
# Publish a message to a topic
curl -X POST "$REST_URL/messagingrest/v1/topics/sap%2Fbtp%2Fmyapp%2Forders%2Fcreated" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "x-qos: 1" \
-d '{"orderId":"ORD-001","status":"created"}'
Verify: HTTP response 204 No Content -> message published
Create a queue that subscribes to topic patterns. [src5, src7]
# Create a queue
curl -X PUT "$REST_URL/messagingrest/v1/queues/order-processor" \
-H "Authorization: Bearer $TOKEN"
# Subscribe queue to a topic pattern
curl -X PUT "$REST_URL/messagingrest/v1/queues/order-processor/subscriptions/sap%2Fbtp%2Fmyapp%2Forders%2F%2A" \
-H "Authorization: Bearer $TOKEN"
Verify: curl "$REST_URL/messagingrest/v1/queues/order-processor" -> queue details with subscription count
Pull messages from the queue using REST or connect with AMQP/MQTT for push-based consumption. [src4]
# Consume one message (REST pull)
curl -X POST "$REST_URL/messagingrest/v1/queues/order-processor/messages/consumption" \
-H "Authorization: Bearer $TOKEN" \
-H "x-qos: 1"
# Acknowledge the message
curl -X POST "$REST_URL/messagingrest/v1/queues/order-processor/messages/acknowledgement" \
-H "Authorization: Bearer $TOKEN" \
-H "x-qos: 1" \
-H "x-message-id: <message-id-from-response>"
Verify: Queue depth decreases by 1 after acknowledgment
# Input: Service key credentials (AMQP endpoint, OAuth token)
# Output: Published message + consumed message from queue
import requests
from proton import Message
from proton.handlers import MessagingHandler
from proton.reactor import Container
def get_token(token_url, client_id, client_secret):
resp = requests.post(token_url, data={
"grant_type": "client_credentials",
"client_id": client_id,
"client_secret": client_secret
})
resp.raise_for_status()
return resp.json()["access_token"]
class Publisher(MessagingHandler):
def __init__(self, url, address, token):
super().__init__()
self.url = url
self.address = address
self.token = token
def on_start(self, event):
conn = event.container.connect(
self.url, user="$consumer",
password=self.token, sasl_enabled=True)
event.container.create_sender(conn, self.address)
def on_sendable(self, event):
msg = Message(body='{"orderId":"ORD-002"}')
msg.content_type = "application/json"
event.sender.send(msg)
event.sender.close()
event.connection.close()
// Input: Service key with MQTT endpoint + OAuth token
// Output: Real-time event consumption via MQTT
const mqtt = require("mqtt"); // [email protected]
const client = mqtt.connect(MQTT_URL, {
username: "$consumer",
password: TOKEN,
protocolVersion: 4, // MQTT 3.1.1
clean: true,
});
client.on("connect", () => {
client.subscribe("sap/btp/myapp/orders/+", { qos: 1 });
});
client.on("message", (topic, message) => {
console.log(`Topic: ${topic}, Payload: ${message.toString()}`);
});
# Get token
TOKEN=$(curl -s -X POST "$TOKEN_URL" \
-d "grant_type=client_credentials&client_id=$CLIENT_ID&client_secret=$CLIENT_SECRET" \
| jq -r '.access_token')
# Publish test message
curl -v -X POST "$REST_URL/messagingrest/v1/topics/sap%2Fbtp%2Fmyapp%2Ftest" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"test":true}'
# Expected: 204 No Content
# Check queue depth
curl -s "$REST_URL/messagingrest/v1/queues/my-queue" \
-H "Authorization: Bearer $TOKEN" | jq '.queueDepth'
| S/4HANA Object | Event Type | Topic Pattern | Payload | Notes |
|---|---|---|---|---|
| Business Partner | Created/Changed/Deleted | sap/s4/beh/businesspartner/v1/BusinessPartner/Created/v1 | Notification (key only) | Full-state requires Event Add-On |
| Sales Order | Created | sap/s4/beh/salesorder/v1/SalesOrder/Created/v1 | Notification | CloudEvents format |
| Purchase Order | Changed | sap/s4/beh/purchaseorder/v1/PurchaseOrder/Changed/v1 | Notification | Key fields in data |
| Material Document | Posted | sap/s4/beh/materialdocument/v1/MaterialDocument/Posted/v1 | Notification | Goods receipt/issue |
| Code | Meaning | Cause | Resolution |
|---|---|---|---|
| 401 | Unauthorized | Token expired or invalid | Re-authenticate; implement token refresh before expiry |
| 403 | Forbidden | Namespace permission denied | Check service key namespace scope |
| 413 | Payload Too Large | Message exceeds 1 MB (EM) or tier limit (AEM) | Reduce payload; use reference pattern; upgrade to AEM |
| 429 | Too Many Requests | Throughput or connection limit exceeded | Exponential backoff; upgrade tier; distribute load |
| 503 | Service Unavailable | Maintenance or capacity issue | Retry with backoff; circuit breaker pattern |
Monitor queue depth; implement consumer auto-scaling; archive processed messages; consider AEM for larger spool. [src1]Ensure endpoint returns 200 within timeout; configure DLQ for undeliverable messages. [src7]Create multiple queues subscribed to the same topic; or migrate to AEM. [src1]Unique namespace per app/team; BTP subaccounts for isolation. [src5]Reconnect logic with fresh token; set idle timeout shorter than token lifetime. [src4]# BAD — Sends entire 500 KB sales order; hits 1 MB limit
event = {"type": "SalesOrderCreated", "data": get_full_sales_order(order_id)}
# GOOD — Lightweight notification; consumer fetches full object via OData
event = {"type": "SalesOrderCreated", "data": {"SalesOrder": order_id}}
// BAD — Polling every 100ms wastes throughput quota
setInterval(async () => {
const msg = await fetch(`${REST_URL}/queues/myqueue/messages/consumption`);
}, 100);
// GOOD — Persistent connection, push delivery, proper QoS
client.subscribe("sap/btp/myapp/orders/+", { qos: 1 });
client.on("message", (topic, msg) => processMessage(JSON.parse(msg)));
# BAD — No filtering, slow consumers block everything
queue: "catch-all" -> subscription: "sap/btp/myapp/#"
# GOOD — Independent scaling, failure isolation
queue: "order-processor" -> subscription: "sap/btp/myapp/orders/*"
queue: "invoice-generator" -> subscription: "sap/btp/myapp/invoices/*"
queue: "audit-logger" -> subscription: "sap/btp/myapp/+/+/+"
Use decision tree above; start with Event Mesh for simple scenarios. [src3]sap/btp/myapp/orders must be sap%2Fbtp%2Fmyapp%2Forders in REST URLs. Fix: Always encodeURIComponent(); AMQP/MQTT use raw topics. [src4]Design consumer to fetch full object via OData after event. [src3]Multiple queues on same topic; competing consumers pattern; or upgrade to AEM. [src1]Assign unique namespace per application; use BTP subaccounts for hard isolation. [src5]Always configure DLQ for production queues; monitor DLQ depth. [src7]# Check Event Mesh service instance status
cf service my-event-mesh
# List all queues and their depth
curl -s "$REST_URL/messagingrest/v1/queues" \
-H "Authorization: Bearer $TOKEN" | jq '.[]|{name,queueDepth,consumers}'
# Check specific queue details
curl -s "$REST_URL/messagingrest/v1/queues/order-processor" \
-H "Authorization: Bearer $TOKEN" | jq '.'
# List topic subscriptions for a queue
curl -s "$REST_URL/messagingrest/v1/queues/order-processor/subscriptions" \
-H "Authorization: Bearer $TOKEN" | jq '.[]'
# Test publish (should return 204)
curl -v -X POST "$REST_URL/messagingrest/v1/topics/sap%2Fbtp%2Fmyapp%2Fhealth" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"ping":true}'
# Check OAuth token validity
curl -s "$TOKEN_URL/tokeninfo" \
-H "Authorization: Bearer $TOKEN" | jq '.expires_in'
| Version / Milestone | Date | Status | Key Changes |
|---|---|---|---|
| Enterprise Messaging (original) | 2018 | Rebranded | Original name before SAP Event Mesh |
| SAP Event Mesh Default Plan | 2020 | GA | AMQP 1.0, MQTT 3.1.1, REST support |
| SAP Integration Suite, AEM | 2022 | GA | Solace PubSub+; 7 service classes; JMS/SMF/WS |
| SAP Cloud Application Event Hub | 2024 | GA | Basic tier included with BTP license; 75M tx |
| AEM Broker 1K firmware update | 2024 | Update | Max message size 10 MB -> 30 MB on newer firmware |
| Event Mesh CPEA-only | 2024 | Current | Not available standalone under BTPEA |
| Use When | Don't Use When | Use Instead |
|---|---|---|
| Simple S/4HANA-to-BTP events, <1 MB, <50 connections | Messages > 1 MB or need on-prem broker | Advanced Event Mesh (Broker 1K+) |
| Lightweight pub/sub between BTP microservices | Need JMS for Java EE middleware | Advanced Event Mesh (JMS) |
| Small team, <10 queues, CPEA license | Need event replay or advanced monitoring | Advanced Event Mesh (Event Portal) |
| IoT edge-to-cloud with <250 MQTT devices | >250 IoT connections or need SMF | Advanced Event Mesh (Broker 250+) |
| Cross-cloud mesh, multi-region deployment | Only basic cloud-to-cloud in single region | Event Mesh (simpler, lower cost) |
| Enterprise with >1,000 concurrent connections | Budget pilot with <50 connections | Event Mesh Default Plan |
| Capability | SAP Event Mesh | SAP Advanced Event Mesh | SAP Cloud Application Event Hub |
|---|---|---|---|
| Max message size | 1 MB | 10-30 MB | Not published |
| Max storage | 10 GB | 25 GB - 1 TB | N/A (transaction-based) |
| Protocols | AMQP, MQTT, REST, Webhook | AMQP, MQTT, JMS, REST, SMF, WS | HTTP (SAP-native) |
| Max connections | 50/instance | 100-100,000 | N/A |
| On-prem support | No | Yes | No |
| Event replay | No | Yes | No |
| Multi-cloud mesh | No | Yes (DMR) | No |
| Monitoring | Basic | Solace Event Portal | Basic |
| S/4HANA integration | Native | Native + Event Add-On | Native (limited) |
| Licensing | CPEA | CPEA or subscription | Included with BTP |
| Setup complexity | Low | Medium-High | Minimal |
| Max transactions | Usage-based | Tier-based | 75M included |