cruxpass
Python client for the CruxPass API.
CruxPass publishes subscribable, per-user calendar feeds on behalf of SaaS platforms. This package is a nimble, fully typed client for the CruxPass publisher REST surface: projects, groups, feeds, subscribers, one-off events, recurring schedules, and public ICS feed reads.
- Python 3.13+
- Sync (
CruxPass) and async (AsyncCruxPass) clients backed byhttpx - Automatic retries with backoff for connect errors, 429, and transient 5xx
responses (safe because CruxPass writes are idempotent upserts keyed by
external_id) - Typed exceptions:
AuthenticationError,NotFoundError,RateLimitError,ServerError, all subclasses ofAPIError CRUXPASS_API_KEYandCRUXPASS_BASE_URLenvironment fallbacks, matching the CruxPass server tooling
Install
uv add cruxpass
# or
pip install cruxpass
Usage
from cruxpass import CruxPass
client = CruxPass(api_key="pk_...") # or set CRUXPASS_API_KEY
client.update_project(feed_domain="feeds.example.com")
client.create_feed(name="Events", slug="events")
client.upsert_event(
feed_slug="events",
external_id="event-123",
summary="Demo Event",
start="2026-07-20T23:00:00Z",
end="2026-07-21T00:30:00Z",
)
subscriber = client.create_subscriber(
label="Demo subscriber",
feeds=["events"],
)
print(subscriber["subscribe_url"])
start, end, and other temporal fields accept ISO 8601 strings or
datetime.date / datetime.datetime objects; timezone-aware UTC datetimes
serialize with a Z suffix.
list_groups(), list_feeds(), list_events(), and list_subscribers()
return plain Python lists. When the server returns a paginated REST envelope,
the helpers follow next links and collect every page for you.
Detail helpers are available for mutable resources: get_*, update_*, and
delete_* for groups and feeds, plus get_subscriber() and
update_subscriber(). Feed and group slugs are immutable server-side; subscriber
tokens and active state are managed through explicit lifecycle helpers.
Recurring schedules accept an optional IANA timezone. Omit it to preserve UTC
recurrence behavior. All-day event and schedule writes must use UTC-midnight
datetimes.
Async
Every helper is also available on AsyncCruxPass with the same signatures:
import asyncio
from cruxpass import AsyncCruxPass
async def main() -> None:
async with AsyncCruxPass(api_key="pk_...") as client:
feeds = await client.list_feeds()
print(feeds)
asyncio.run(main())
Feed reads
Public subscriber feeds are fetched without auth and support conditional
requests via ETag and Last-Modified validators:
feed = client.get_subscriber_feed(subscriber["token"])
print(feed.status_code)
print(feed.etag)
print(feed.text)
not_modified = client.get_subscriber_feed(
subscriber["token"],
etag=feed.etag,
)
assert not_modified.status_code == 304
Recurring schedules
client.upsert_recurring_schedule(
feed_slug="events",
external_id="weekly-meeting",
summary="Weekly Meeting",
first_start="2026-07-20T23:00:00Z",
first_end="2026-07-21T00:00:00Z",
frequency="weekly",
timezone="America/Denver",
count=12,
)
client.move_recurring_occurrence(
feed_slug="events",
schedule_external_id="weekly-meeting",
occurrence_number=2,
start="2026-07-28T01:00:00Z",
end="2026-07-28T02:00:00Z",
)
client.cancel_recurring_occurrence(
feed_slug="events",
schedule_external_id="weekly-meeting",
occurrence_number=3,
)
Errors and retries
API failures raise typed exceptions carrying status_code, message, and the
underlying httpx.Response:
from cruxpass import AuthenticationError, NotFoundError
try:
client.get_project()
except AuthenticationError:
... # bad or revoked API key
except NotFoundError:
...
Connect errors and 429/502/503/504 responses are retried with exponential
backoff (honoring Retry-After). Tune or disable with
CruxPass(..., max_retries=0).
Configuration
| Option | Constructor | Environment | Default |
|---|---|---|---|
| API key | api_key |
CRUXPASS_API_KEY |
required |
| Base URL | base_url |
CRUXPASS_BASE_URL |
https://cruxpass.com |
| Timeout | timeout |
10.0 seconds |
|
| Retries | max_retries |
2 |
|
| HTTP client | http_client |
owned httpx.Client |
Helpers
The client exposes the documented REST surface:
get_project()/update_project(feed_domain=...)list_groups()/create_group(...)/get_group(...)/update_group(...)/delete_group(...)list_feeds()/create_feed(...)/get_feed(...)/update_feed(...)/delete_feed(...)list_subscribers()/create_subscriber(...)/get_subscriber(...)/update_subscriber(...)deactivate_subscriber(token)/rotate_subscriber_token(token)/refresh_subscriber_artifact(token)list_events(feed_slug=...)/upsert_event(...)/cancel_event(...)upsert_recurring_schedule(...)upsert_recurring_exception(...)move_recurring_occurrence(...)skip_recurring_occurrence(...)cancel_recurring_occurrence(...)get_subscriber_feed(token, etag=..., modified_since=...)get_subscriber_feed_ics(token)
For API paths that do not have a named helper yet, use client.request(method, path, json=..., params=...).
Development
The project is managed with uv end to end:
uv sync --group dev
uv run python -m unittest discover -s tests
uv run --group dev ruff check .
uv run --group dev ruff format --check .
uv run --group dev ty check src tests
uv build
Status
Pre-1.0 package. Releases are coordinated with the matching CruxPass server
contract; see CHANGELOG.md for endpoint, client method, and migration notes.
PyPI publishing is GitHub-only; see RELEASE.md.
Metadata
Release files for cruxpass 0.0.4
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 | |
|---|---|---|---|---|
| cruxpass-0.0.4.tar.gz | 9.4 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| cruxpass-0.0.4-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 22.9 kB
Release files / cruxpass-0.0.4.tar.gz
| Download URL | cruxpass-0.0.4.tar.gz |
|---|---|
| Size | 9.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e1171f68cc8500d652e33ae5f15542d2b9eab2f0a0fd6841dc4325a28c5344d4
|
|
BLAKE2b-256 checksum How to use checksums |
f0f90e0487f17b3de7e371f6e43a48f5d4cfb84128e2633835769135e7b70f00
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
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 Jul 7, 2026.
Transparency logRelease files / cruxpass-0.0.4-py3-none-any.whl
| Download URL | cruxpass-0.0.4-py3-none-any.whl |
|---|---|
| Size | 13.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
33f4a3bf11270a16782510d702f7e48a2c4d5c45a953146e59f3cff777799ff8
|
|
BLAKE2b-256 checksum How to use checksums |
e0e74a52c8e05c569a2c34979552e555354c74969bc1f1b361a93b16c76620a5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
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 Jul 7, 2026.
Transparency logSecurity for cruxpass 0.0.4
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 cruxpass-0.0.4.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 Jul 7, 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.