sap_commerce_cloud_management_apis
A Python client library for the SAP Commerce Cloud — Cloud Portal APIs (a.k.a. the Management APIs). It lets you automate operational tasks against your Commerce Cloud subscription — listing environments, triggering deployments, managing data backups, endpoints, TLS certificates, scaling, scheduled activities, and user role assignments — from Python instead of the Cloud Portal UI or CLI.
Built on httpx and attrs;
every request and response is typed.
- Base URL:
https://portalapi.commerce.ondemand.com/v2 - Format: REST / JSON
- Auth: OAuth2 client-credentials bearer token, sent in the
x-approuter-authorizationheader - Reference: Cloud Portal API Documentation
⚠️ Two things that trip people up — read these before your first call:
- The token goes in
x-approuter-authorization, not the standardAuthorizationheader. You must passauth_header_name="x-approuter-authorization"(see below).- The
base_urlmust include the/v2suffix — endpoint paths are relative to it.
Installation
This project uses Poetry:
poetry install
To use it from another project, either poetry add <path-to-this-client>, or build a wheel
(poetry build -f wheel) and pip install it.
Authentication
1. Obtain a bearer token
Create a technical user in the Cloud Portal (see Technical Users), then exchange its credentials for a token using the OAuth2 client-credentials grant:
import httpx
token_response = httpx.post(
TOKEN_URL, # your token endpoint
headers={"Accept": "application/json"},
data={
"client_id": CLIENT_ID,
"client_secret": CLIENT_SECRET,
"grant_type": "client_credentials",
"resource": RESOURCE, # the required SAP "resource" parameter
},
)
access_token = token_response.json()["access_token"] # valid ~3600s
2. Build an authenticated client
from sap_commerce_cloud_management_apis import AuthenticatedClient
client = AuthenticatedClient(
base_url="https://portalapi.commerce.ondemand.com/v2",
token=access_token,
auth_header_name="x-approuter-authorization", # REQUIRED for Cloud Portal
)
Without auth_header_name, the token defaults to the standard Authorization header, which
the Cloud Portal approuter does not accept.
Loading credentials from .env
Never hard-code secrets. Copy .env.example to .env (gitignored) and load it:
CCV2_SUBSCRIPTION_CODE=your-subscription-code
CCV2_TOKEN_URL=https://<tenant>.accounts.ondemand.com/oauth2/token
CCV2_CLIENT_ID=your-client-id
CCV2_CLIENT_SECRET=your-client-secret
SAP_CCV2_RESOURCE=urn:sap:identity:application:provider:name:...
import os
from dotenv import load_dotenv
load_dotenv()
subscription_code = os.environ["CCV2_SUBSCRIPTION_CODE"]
# ...etc
Calling the API
Every operation lives under sap_commerce_cloud_management_apis.api.<tag>.<operation> and
exposes four functions:
| Function | Blocking? | Returns |
|---|---|---|
sync |
yes | parsed model, or None |
sync_detailed |
yes | Response[T] (status code, headers, raw content, parsed) |
asyncio |
no | parsed model, or None |
asyncio_detailed |
no | Response[T] |
Path/query params and request bodies are keyword/positional arguments. Most operations take
subscription_code and often environment_code.
Example: list environments
from sap_commerce_cloud_management_apis.api.environment import get_environments
from sap_commerce_cloud_management_apis.models import EnvironmentDetailsDTO, ErrorDTO
with client as c:
result = get_environments.sync(subscription_code, client=c)
if isinstance(result, EnvironmentDetailsDTO):
for env in result.value or []:
print(env.code, env.type_, env.status)
elif isinstance(result, ErrorDTO):
print("API error:", result.title, "-", result.detail)
Use sync_detailed when you need the status code or headers:
from sap_commerce_cloud_management_apis.types import Response
resp: Response = get_environments.sync_detailed(subscription_code, client=client)
print(resp.status_code) # e.g. 200
env_details = resp.parsed
Example: trigger a deployment
from sap_commerce_cloud_management_apis.api.deployment import create_deployment
from sap_commerce_cloud_management_apis.models import CreateDeploymentRequestDTO
from sap_commerce_cloud_management_apis.models.create_deployment_request_dto_database_update_mode import (
CreateDeploymentRequestDTODatabaseUpdateMode,
)
from sap_commerce_cloud_management_apis.models.create_deployment_request_dto_strategy import (
CreateDeploymentRequestDTOStrategy,
)
body = CreateDeploymentRequestDTO(
build_code="20240101.1",
environment_code="d1",
database_update_mode=CreateDeploymentRequestDTODatabaseUpdateMode.NONE,
strategy=CreateDeploymentRequestDTOStrategy.ROLLING_UPDATE,
)
deployment = create_deployment.sync(subscription_code, client=client, body=body)
Example: create a data backup
from sap_commerce_cloud_management_apis.api.databackup import create_databackup
from sap_commerce_cloud_management_apis.models import CreateDatabackupRequestDTO
body = CreateDatabackupRequestDTO(description="pre-release snapshot", databackup_type="STANDARD")
created = create_databackup.sync(subscription_code, "d1", client=client, body=body)
Async
Every operation has an async twin — use asyncio / asyncio_detailed inside an async with:
async with client as c:
result = await get_environments.asyncio(subscription_code, client=c)
Pagination
Paginated list endpoints (e.g. deployment.get_deployments) cap out at 100 items per page
and accept OData-style params: top, skip, orderby, count.
from sap_commerce_cloud_management_apis.api.deployment import get_deployments
page = get_deployments.sync(subscription_code, client=client, environment_code="d1", top=100, skip=0)
Available operations
| Tag | Module | Operations |
|---|---|---|
| Environments | api.environment |
list environments |
| Deployments | api.deployment |
create / get / cancel deployments, deployment decisions & progress, traffic split |
| Data backups | api.databackup |
create/get/delete backups, create/get restores, change states |
| Endpoints | api.endpoint |
create / get / update / delete endpoints |
| Scaling | api.environment_scaling |
get / update scaling details & options |
| Scheduled activities | api.scheduled_activity |
create / get / update / cancel scheduled activities |
| Service properties | api.service_properties |
get / put a property |
| TLS certificates | api.ssl_certificate |
create / get / delete certificates |
| User role assignments | api.user_role_assignments |
list roles, create / get / update / delete assignments |
Note:
build-related DTOs exist inmodels/but thebuildAPI module is not generated in this client.
Error handling
Documented failures (4xx/5xx) parse into an ErrorDTO
(RFC 7807) with title and detail fields — always
branch on the return type rather than assuming success.
⚠️ Known caveat: the generated parsers call response.json() on error responses. When the
approuter returns a non-JSON body (for example a plain-text Bad Request on a malformed
token), this raises json.JSONDecodeError. For hard-failure paths (bad/absent token), inspect
the raw HTTP status instead — e.g. issue the request with httpx directly, or wrap the call in
a try/except json.JSONDecodeError.
TLS
Public Cloud Portal APIs require TLS 1.2+. Certificate verification is on by default. To use
a custom CA bundle, pass verify_ssl="/path/to/bundle.pem"; disabling it (verify_ssl=False) is
a security risk and not recommended.
Testing
Tests live in tests/ and split into two kinds:
- Contract tests (offline) — drive the real client through an
httpx.MockTransport, asserting method, URL, thex-approuter-authorizationheader, query/body serialization, and response→DTO parsing. No network, no credentials. - Live integration tests (marked
integration) — perform the real OAuth2 token exchange and hit your tenant. They load credentials from.envand skip automatically if any variable is missing.
poetry run pytest # everything (live tests run only if .env is present)
poetry run pytest -m "not integration" # offline contract tests only
poetry run pytest -m integration # live tests against your tenant (needs .env)
Advanced customization
You can customize the underlying httpx client — e.g. to log every request/response:
def log_request(request):
print(f"→ {request.method} {request.url}")
def log_response(response):
print(f"← {response.status_code} {response.request.url}")
client = AuthenticatedClient(
base_url="https://portalapi.commerce.ondemand.com/v2",
token=access_token,
auth_header_name="x-approuter-authorization",
httpx_args={"event_hooks": {"request": [log_request], "response": [log_response]}},
)
Other useful knobs on AuthenticatedClient: timeout, follow_redirects, raise_on_unexpected_status
(raise on undocumented status codes instead of returning None), plus with_headers() /
with_cookies() / with_timeout() to derive a modified copy. See the class docstring in
client.py for the full list.
Building / publishing
This project uses Poetry:
- Bump
version(and other metadata) inpyproject.toml. - Lint:
ruff check .(line length 120; rulesF,I,UP). - Build a wheel:
poetry build -f wheel. - Publish:
poetry publish --build(add-r <repo>for a private repository configured viapoetry config repositories.<repo> <url>andpoetry config http-basic.<repo> <user> <pass>).
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file sap_commerce_cloud_management_apis-1.0.0.tar.gz.
File metadata
- Download URL: sap_commerce_cloud_management_apis-1.0.0.tar.gz
- Upload date:
- Size: 56.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
poetry/2.4.1 CPython/3.13.13 Darwin/24.6.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
69db0d8e7995acf1a15456ba1c1453fecbefe87c11442533e3d2a655351caa96
|
|
| MD5 |
6d13805302126458297d9a431edd7ddf
|
|
| BLAKE2b-256 |
61f4ffec8fc204104b3a169814ee5bda74f80e775e9730b431ee522c3d059f50
|
File details
Details for the file sap_commerce_cloud_management_apis-1.0.0-py3-none-any.whl.
File metadata
- Download URL: sap_commerce_cloud_management_apis-1.0.0-py3-none-any.whl
- Upload date:
- Size: 179.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
poetry/2.4.1 CPython/3.13.13 Darwin/24.6.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8f725d05d7a5cfdbb49a77402865af67ad61824b0cd4611712a4ad9ac2c865a5
|
|
| MD5 |
74922be278541c556f7693f0d78b9c94
|
|
| BLAKE2b-256 |
92a61a5d3bed0b16829f6e16431cb2aabaa6edd5ca8123e6f115a138580ccaae
|