Register a CAP app in the SaaS Registry, configure onSubscription URLs, and secure with the mtcallback scope.
The SaaS Registry is BTP's central directory of multi-tenant SaaS applications. It connects the "subscribe" button in a customer's BTP Cockpit to your provisioning endpoint. Without registering your app in the SaaS Registry, customers cannot subscribe — you would have to manage tenant onboarding manually. The registry also controls which XSUAA scopes are granted to subscribers and what the consumer app looks like in their cockpit.
Customer BTP Cockpit
│ Click "Subscribe"
▼
SaaS Registry
│ PUT /mtx/v1/provisioning/tenant/{tenantId}
▼
Your CAP App (mtxs provisioning endpoint)
│ Create HDI container, deploy schema
│ Return 200 OK
▼
SaaS Registry
│ Store subscription record
│ Redirect customer to app URL
▼
Customer opens your app at:
https://your-app-<customer-subaccount>.cfapps.eu10.hana.ondemand.com
cf create-service saas-registry application my-saas-registry \
-c '{
"appName": "my-cap-saas",
"appUrls": {
"getDependencies": "https://my-cap-app.cfapps.eu10.hana.ondemand.com/mtx/v1/provisioning/dependencies",
"onSubscription": "https://my-cap-app.cfapps.eu10.hana.ondemand.com/mtx/v1/provisioning/tenant/{tenantId}",
"onSubscriptionAsync": false
},
"displayName": "My CAP Learning App",
"description": "Learn SAP BTP development interactively",
"category": "Education",
"xsappname": "my-cap-saas"
}'
The onSubscription URL is what the registry calls — {tenantId} is a literal placeholder that the registry replaces with the actual tenant GUID at call time.
resources:
- name: my-saas-registry
type: org.cloudfoundry.managed-service
parameters:
service: saas-registry
service-plan: application
config:
appName: my-cap-saas
displayName: My CAP Learning App
description: Interactive SAP BTP course platform
category: Education
appUrls:
getDependencies: ~{srv-api/srv-url}/mtx/v1/provisioning/dependencies
onSubscription: ~{srv-api/srv-url}/mtx/v1/provisioning/tenant/{tenantId}
onSubscriptionAsync: false
requires:
- name: srv-api
Using ~{srv-api/srv-url} means the URL is resolved from the CAP module's published URL — no hardcoding.
getDependencies endpointThe SaaS Registry calls this endpoint to discover which XSUAA and Destination services the app depends on. @sap/cds-mtxs implements it automatically:
GET /mtx/v1/provisioning/dependencies
Response:
[
{ "xsappname": "my-cap-saas!t1234" },
{ "xsappname": "destination!b12" }
]
Only the SaaS Registry should call your provisioning endpoint. @sap/cds-mtxs enforces this by requiring the mtcallback scope, which is only granted to the SaaS Registry service principal:
// xs-security.json
{
"scopes": [
{
"name": "$XSAPPNAME.mtcallback",
"description": "SaaS Registry callback scope",
"grant-as-authority-to-apps": ["$XSAPPNAME!b*"]
}
]
}
As the provider, you can see all subscriptions:
1. Navigate to SaaS Registry → your application → Subscriptions
2. Each subscription shows: tenant ID, subaccount name, status, subscribed date
3. You can manually trigger getDependencies to test the endpoint
4. For offboarding: the customer unsubscribes in their cockpit → registry calls DELETE /mtx/v1/provisioning/tenant/{tenantId}
When a tenant subscribes, their dedicated URL is:
https://<tenant-subdomain>-<your-app-name>.cfapps.eu10.hana.ondemand.com
AppRouter resolves the subdomain → XSUAA extracts the zone ID → CAP sets req.tenant. The routing is automatic.
| Mistake | Fix |
|---|---|
| Hardcoding the callback URL | Use ~{srv-api/srv-url} to resolve at deploy time |
Not granting mtcallback scope to the registry |
Provisioning calls will be rejected as unauthorized |
onSubscriptionAsync: true without polling |
Async mode requires a Job ID response; start with sync |
Not implementing getDependencies |
Subscriber subaccounts won't have the correct XSUAA scopes |
Missing saas-registry entitlement |
Provision will fail — check Global Account entitlements first |
You can create a SaaS Registry service instance with the correct onSubscription and getDependencies URLs, bind it in the MTA descriptor using dynamic URL references, secure the provisioning endpoint with the mtcallback scope, and manage subscriptions from the BTP Cockpit.