Python SDK for the Capawesome Cloud API
Project description
capawesome-cloud
Python SDK for the Capawesome Cloud API.
It provides a fully typed, synchronous interface for managing apps, live update channels and deployments, native builds, app store destinations, and more.
Note: The Capawesome Cloud API is still in development and may change without notice. Response types intentionally expose only the most relevant properties to minimize breaking changes.
SDKs
Official SDKs for the Capawesome Cloud API:
| Language | Package | Repository |
|---|---|---|
| Node.js | @capawesome/cloud-sdk |
cloud-node |
| Python | capawesome-cloud |
cloud-python |
Installation
pip install capawesome-cloud
Requirements: Python 3.9 or later.
Getting started
Create an API token in the Capawesome Cloud Console and pass it to the client (or set the CAPAWESOME_CLOUD_TOKEN environment variable, with CAPAWESOME_TOKEN accepted as a fallback):
from capawesome_cloud import CapawesomeCloud
client = CapawesomeCloud(token="cap_...")
for app in client.apps.list():
print(app.id, app.name)
The client holds a connection pool, so reuse a single instance. Use it as a context manager (or call client.close()) to release connections when done:
with CapawesomeCloud(token="cap_...") as client:
...
Configuration
| Option | Type | Default | Description |
|---|---|---|---|
token |
str |
CAPAWESOME_CLOUD_TOKEN env var |
API token used to authenticate. |
base_url |
str |
https://api.cloud.capawesome.io |
Base URL of the API (for self-hosting/testing). |
timeout |
float |
30.0 |
Request timeout in seconds. |
max_retries |
int |
2 |
Retries with exponential backoff. 429 is retried for any request; network/5xx failures are retried only for idempotent methods (GET/PUT/DELETE), never POST/PATCH. |
backoff_factor |
float |
0.5 |
Base delay for the retry backoff. |
http_client |
httpx.Client |
None |
Bring your own pre-configured httpx.Client. |
Usage
Resources mirror the API's path hierarchy. App-scoped resources are nested under client.apps.*; organization-scoped resources are on the client directly (e.g. client.jobs). Every app-scoped method takes app_id as its first argument.
Apps
apps = client.apps.list()
app = client.apps.get(app_id)
created = client.apps.create(name="My App", type="capacitor")
client.apps.update(app_id, name="Renamed App")
client.apps.delete(app_id)
Live updates
# Create a channel
channel = client.apps.channels.create(app_id, name="production")
# Pause / resume a channel
client.apps.channels.pause(app_id, channel.id)
client.apps.channels.resume(app_id, channel.id)
Deployments
Promote a build to a channel (live updates) or a destination (app store publishing):
deployment = client.apps.deployments.create(
app_id,
app_build_id=app_build_id,
app_channel_name="production",
rollout_percentage=0.5,
)
Native builds
build = client.apps.builds.create(app_id, platform="ios", git_ref="main")
# Poll the job that processes the build until it finishes
job = client.jobs.wait(build.job_id)
print(job.status)
logs = client.jobs.logs(job.id)
Build artifacts
Binary downloads return bytes:
data = client.apps.builds.artifacts.download(app_id, build_id, artifact_id)
with open("artifact.ipa", "wb") as file:
file.write(data)
You can also obtain a signed, time-limited download URL:
result = client.apps.builds.artifacts.get_download_url(app_id, build_id, artifact_id)
print(result["url"])
Certificates
file may be a path, raw bytes, or an open binary file object:
import os
certificate = client.apps.certificates.create(
app_id,
name="Distribution Certificate",
platform="ios",
type="production",
file="distribution.p12",
password=os.environ["CERT_PASSWORD"],
)
Environments, secrets & variables
environment = client.apps.environments.create(app_id, name="production")
client.apps.environments.secrets.create(
app_id,
environment.id,
key="API_KEY",
value=os.environ["API_KEY"],
)
client.apps.environments.variables.create(
app_id,
environment.id,
key="API_URL",
value="https://api.example.com",
)
Pagination
List methods return an iterator that lazily pages through all results:
for device in client.apps.devices.list(app_id):
print(device.id, device.app_version_name)
# Collect everything into a list
channels = client.apps.channels.list(app_id).to_list()
To fetch a single page (for manual offset control), use list_page():
page = client.apps.channels.list_page(app_id, limit=20, offset=0)
Responses
Responses are typed Pydantic models. App-scoped models are prefixed with App (AppChannel, AppWebhook, AppBuild, ...) to match the API's entity names. Only the most relevant fields are declared; any additional fields the API returns are still accessible (e.g. via model_dump()) but are not part of the public contract and should not be relied upon.
channel = client.apps.channels.get(app_id, channel_id)
print(channel.name, channel.created_at) # documented fields
print(channel.model_dump()) # full raw payload, incl. extra fields
Clearing fields
Update methods only send the arguments you pass. To clear a nullable field, pass None; omit the argument to leave it unchanged.
# Pin a device to a channel
client.apps.devices.update(app_id, device_id, forced_app_channel_id="ch_123")
# Unpin it again (sends null)
client.apps.devices.update(app_id, device_id, forced_app_channel_id=None)
Available resources
| Resource | Description |
|---|---|
client.apps |
Create, read, update, delete and transfer apps. |
client.apps.channels |
Manage live update channels (incl. pause/resume). |
client.apps.deployments |
Promote builds to channels or destinations. |
client.apps.builds |
Trigger and manage native builds. |
client.apps.builds.artifacts |
List and download build artifacts. |
client.apps.build_sources |
Register and download native build sources. |
client.apps.certificates |
Manage signing certificates. |
client.apps.destinations |
Manage app store publishing destinations. |
client.apps.environments |
Manage environments, secrets and variables. |
client.apps.automations |
Manage build automations. |
client.apps.devices |
Manage registered devices. |
client.apps.webhooks |
Manage app webhooks. |
client.jobs |
Inspect background jobs and their logs. |
Error handling
Any non-2xx response is raised as a CapawesomeCloudError, carrying the HTTP status, the message from the API, and the raw body:
from capawesome_cloud import CapawesomeCloud, CapawesomeCloudError
client = CapawesomeCloud(token="cap_...")
try:
client.apps.get("unknown")
except CapawesomeCloudError as error:
print(error.status) # 404
print(error.status_text) # "Not Found"
print(error.message) # "App not found."
print(error.body) # {"message": "App not found."}
Network failures raise APIConnectionError / APITimeoutError, which also derive from CapawesomeCloudError — so a single except CapawesomeCloudError catches every error the SDK can raise. For those, status is None.
Development
Setup
Clone the repository, then create a virtual environment and install the package with its development dependencies:
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
Commit messages follow Conventional Commits, enforced by Commitizen — run cz commit for a guided prompt. Common commands during development:
| Command | Description |
|---|---|
python -m build |
Build the package into dist/. |
ruff check src tests examples |
Run the linter. |
ruff format src tests examples |
Auto-format the code. |
ruff format --check src tests examples |
Check formatting without writing. |
mypy |
Type-check the package. |
cz commit |
Create a Conventional Commit. |
Testing
Tests are written with pytest and live in the tests/ directory. HTTP is mocked with RESPX, so the suite makes no network calls:
pytest # run the suite once
pytest --cov=capawesome_cloud # run with a coverage report
Publishing
Releases are driven by Commitizen, which derives the next version and changelog entries from Conventional Commits.
- Make sure
mainis up to date and CI is green. - Run
cz bump. This bumps the version (in[tool.commitizen]andsrc/capawesome_cloud/_version.py), regeneratesCHANGELOG.md, and creates a release commit plus a matchingvX.Y.Ztag — all locally, nothing is pushed yet. Pass--dry-runfirst if you want to preview the result. - Review the generated commit and changelog, then push everything:
git push --follow-tags origin main. - Build the distributions with
python -m build, then publish to PyPI withpython -m twine upload dist/*(requirespip install build twine).
License
See LICENSE.
Project details
Release history Release notifications | RSS feed
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 capawesome_cloud-0.1.0.tar.gz.
File metadata
- Download URL: capawesome_cloud-0.1.0.tar.gz
- Upload date:
- Size: 26.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b102c959c28fb86ccdbc03c398cf4af6bd2ae3fda0df7aa67440b05cc8e9422b
|
|
| MD5 |
e138855e28bc136ecd31f0551ba58d47
|
|
| BLAKE2b-256 |
c69abad17ab07501d326571aa4d38840ab2557c7841c70d77c48bb2caa2e0fbd
|
Provenance
The following attestation bundles were made for capawesome_cloud-0.1.0.tar.gz:
Publisher:
release.yml on capawesome-team/cloud-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
capawesome_cloud-0.1.0.tar.gz -
Subject digest:
b102c959c28fb86ccdbc03c398cf4af6bd2ae3fda0df7aa67440b05cc8e9422b - Sigstore transparency entry: 1702508398
- Sigstore integration time:
-
Permalink:
capawesome-team/cloud-python@3d3be0057b1be8c5a00c4ba2f6ada3fd6acb9849 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/capawesome-team
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@3d3be0057b1be8c5a00c4ba2f6ada3fd6acb9849 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file capawesome_cloud-0.1.0-py3-none-any.whl.
File metadata
- Download URL: capawesome_cloud-0.1.0-py3-none-any.whl
- Upload date:
- Size: 31.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b3b647b1a1de5cebb878adf4dc7e2e738ec3b1b68cf9cea2fbcc39f2c4508cb7
|
|
| MD5 |
c5263b8f5932450a001af283bcc84d7c
|
|
| BLAKE2b-256 |
d151f1283d27926fbaea8a7f4935d4ec3b40eed5bb2abc30e5d83cad72829ce5
|
Provenance
The following attestation bundles were made for capawesome_cloud-0.1.0-py3-none-any.whl:
Publisher:
release.yml on capawesome-team/cloud-python
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
capawesome_cloud-0.1.0-py3-none-any.whl -
Subject digest:
b3b647b1a1de5cebb878adf4dc7e2e738ec3b1b68cf9cea2fbcc39f2c4508cb7 - Sigstore transparency entry: 1702508412
- Sigstore integration time:
-
Permalink:
capawesome-team/cloud-python@3d3be0057b1be8c5a00c4ba2f6ada3fd6acb9849 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/capawesome-team
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@3d3be0057b1be8c5a00c4ba2f6ada3fd6acb9849 -
Trigger Event:
workflow_dispatch
-
Statement type: