capawesome-cloud
Python SDK for the Capawesome Cloud API.
It provides a fully typed, synchronous interface for managing organizations, 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.10 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 under client.organizations.*; top-level resources are on the client directly (e.g. client.jobs). Every app-scoped method takes app_id as its first argument, every organization-scoped method organization_id.
Organizations
organizations = client.organizations.list()
organization = client.organizations.get(organization_id)
members = client.organizations.members.list(organization_id)
# Invite a new member
client.organizations.invitations.create(
organization_id, email="jane@example.com", role="member"
)
# Group apps and members in a team
team = client.organizations.teams.create(organization_id, name="Mobile")
client.organizations.teams.apps.create(organization_id, team.id, app_id=app_id)
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):
# Roll out a live update to half of the devices on a channel
client.apps.deployments.create(
app_id,
app_build_id=app_build_id,
app_channel_name="production",
rollout_percentage=0.5,
)
# Publish a native build to an app store with localized release notes
client.apps.deployments.create(
app_id,
app_build_id=app_build_id,
app_destination_name="Google Play",
release_notes={
"default": "Bug fixes and performance improvements.",
"de-DE": "Fehlerbehebungen und Leistungsverbesserungen.",
},
)
Git repositories
Native builds are created from the repository an app is linked to:
connection = client.organizations.git_connections.list_page(organization_id, limit=1)[0]
repositories = client.organizations.git_connections.list_repositories(
organization_id, connection.id
)
client.apps.repository.set(app_id, git_connection_id=connection.id, path=repositories[0].path)
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)
for log in client.jobs.logs(job.id):
print(log.payload)
# Explain a failure
if job.status == "failed":
print(client.jobs.get_failure_summary(job.id).summary)
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.apps.configurations |
Manage native app configurations. |
client.apps.repository |
Link an app to a Git repository. |
client.organizations |
Create, read and update organizations. |
client.organizations.members |
Manage organization members. |
client.organizations.invitations |
Invite users to an organization. |
client.organizations.teams |
Manage teams and their apps and members. |
client.organizations.license_keys |
Manage license keys for Insiders packages. |
client.organizations.git_connections |
Manage Git connections and browse repositories. |
client.jobs |
Inspect background jobs and their logs. |
client.users |
Access the authenticated user. |
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]"
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. |
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 automated with Release Please, which derives the next version and changelog entries from Conventional Commits.
On every push to main, the Release workflow creates or updates a release pull request that bumps the version and updates CHANGELOG.md. Merging that pull request creates the git tag and GitHub release, and publishes the package to PyPI.
License
See LICENSE.
Metadata
Release files for capawesome-cloud 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| capawesome_cloud-0.3.0.tar.gz | 36.6 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| capawesome_cloud-0.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 80.4 kB
Release files / capawesome_cloud-0.3.0.tar.gz
| Download URL | capawesome_cloud-0.3.0.tar.gz |
|---|---|
| Size | 36.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
69625b43b04358fb5aea809e3e5da3a033a7d69523eb840cdc750d14e7c75c33
|
|
BLAKE2b-256 checksum How to use checksums |
8d70e07cd6d5cfe356d6463fa3cd546eee973cef132dd24c94304dc606e3cdbb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.
Transparency logRelease files / capawesome_cloud-0.3.0-py3-none-any.whl
| Download URL | capawesome_cloud-0.3.0-py3-none-any.whl |
|---|---|
| Size | 43.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
410295e579fa7a29747f20bbcedacc3fafbd585ff338cf40f9e0863ea5a202d6
|
|
BLAKE2b-256 checksum How to use checksums |
727b1cd6607861d7210cfd3edfb735a27ca5ba868a0808b35d996017249f9c6e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.
Transparency logSecurity for capawesome-cloud 0.3.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for capawesome_cloud-0.3.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.