Python SDK for the Cyrus Payments API
Project description
cyrus-payments
Official Python SDK for the Cyrus Payments API — customer payment identity infrastructure built on Nomba. Give each of your customers a persistent identity and a dedicated virtual bank account, and let Cyrus handle provisioning, webhook reconciliation, and reporting.
This SDK covers the developer (API-key) surface only — customers, transactions, and payment events. It does not cover the merchant dashboard (JWT-authenticated) endpoints.
Installation
# uv (recommended)
uv add cyrus-payments
# pip
pip install cyrus-payments
Requires Python 3.10+.
Quick start
from cyrus import CyrusClient
client = CyrusClient(api_key="cyrus_live_xxx")
customer = client.customers.create(
reference="cust_123", # your own identifier for this customer (required)
first_name="Ada", # required
last_name="Lovelace", # optional
email="ada@example.com", # optional
)
print(customer.virtual_account.account_number, customer.virtual_account.bank_name)
Use it as a context manager to close the underlying HTTP connection pool automatically:
with CyrusClient(api_key="cyrus_live_xxx") as client:
customer = client.customers.get("cust_123")
By default the client points at production (https://api.trycyrus.app). Override base_url for a
local/staging backend:
client = CyrusClient(api_key="cyrus_test_xxx", base_url="http://localhost:8080")
Customers
# Create
customer = client.customers.create(reference="cust_123", first_name="Ada")
# Get
customer = client.customers.get("cust_123")
# Update profile (partial — only pass what's changing)
customer = client.customers.update("cust_123", first_name="Grace", phone_number="08012345678")
# KYC tier and status
from cyrus import KycTier, CustomerStatus
client.customers.set_kyc_tier("cust_123", tier=KycTier.TIER_2)
client.customers.suspend("cust_123") # or .reactivate(...) / .close(...)
# Statement: identity + reporting summary + paginated transaction history
statement = client.customers.statement("cust_123", page=0, size=20)
print(statement.summary.lifetime_kobo, statement.summary.transaction_count)
for row in statement.transactions.content:
print(row.ref, row.amount_kobo, row.match_status)
# Filter by date range and/or match status
from cyrus import MatchStatus
filtered = client.customers.statement(
"cust_123",
from_date="2025-01-01T00:00:00Z",
to_date="2025-12-31T23:59:59Z",
match_status=MatchStatus.DISCREPANCY,
)
Transactions
Merchant-wide transaction history, across all customers:
from cyrus import TransactionType, TransactionStatus, MatchStatus
page = client.transactions.list(
customer_reference="cust_123", # optional — omit to see all customers
type=TransactionType.CUSTOMER_PAYMENT,
status=TransactionStatus.SUCCESSFUL,
match_status=MatchStatus.DISCREPANCY,
from_date="2025-01-01T00:00:00Z", # optional ISO-8601 instant
to_date="2025-12-31T23:59:59Z", # optional ISO-8601 instant
page=0,
size=20,
)
for txn in page:
print(txn.reference, txn.amount_kobo, txn.fee_kobo)
transaction = client.transactions.get("txn_abc123")
Payment events (exception triage)
Raw inbound payment events, including orphaned/misdirected payments that couldn't be automatically attributed to a customer:
from cyrus import PaymentEventStatus
events = client.payment_events.list(status=PaymentEventStatus.IGNORED)
event = client.payment_events.get(str(events[0].id))
client.payment_events.replay(str(event.id))
client.payment_events.reattribute(str(event.id), customer_reference="cust_123")
Pagination
Every list endpoint returns a Page, which behaves like a list of the current page and also carries
metadata:
page = client.customers.list(page=0, size=20)
list(page) # iterate current page's items
page.total_elements
page.total_pages
page.is_last
To walk every page automatically, use iter_all(...) (available on customers, transactions, and
payment_events):
for customer in client.customers.iter_all(page_size=50):
print(customer.reference)
Errors
All API errors raise a subclass of CyrusAPIError:
from cyrus import (
CyrusAPIError, # base class
ValidationError, # 400 — bad input; .field_errors has per-field details
UnauthorizedError, # 401 — missing/invalid API key
NotFoundError, # 404
ConflictError, # 409 — state conflict (e.g. customer already closed)
ProviderError, # upstream Nomba error
ServerError, # 5xx
)
try:
client.customers.create(reference="cust_123", first_name="Ada", email="not-an-email")
except ValidationError as e:
print(e.message, e.field_errors)
except CyrusAPIError as e:
print(e.code, e.message, e.status_code)
Network-level failures (timeouts, connection errors) are retried automatically with exponential
backoff (max_retries=3 by default, configurable on CyrusClient(...)).
Enums
Request/response fields backed by a fixed set of values are typed enums (CustomerStatus,
KycTier, TransactionType, TransactionStatus, MatchStatus, PaymentEventType,
PaymentEventStatus, ReconciliationFailureReason), all importable from the top-level cyrus
package. Plain strings matching the same values also work, for callers that prefer not to import them:
# equivalent — both work
client.customers.set_status("cust_123", status=CustomerStatus.SUSPENDED)
client.customers.set_status("cust_123", status="SUSPENDED")
Full API reference
For the complete, always-current endpoint/schema/error reference, see the live Scalar docs: https://api.trycyrus.app/docs.
Development
cd sdk/python
uv sync --all-extras
uv run pytest -v
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 cyrus_payments-0.1.2.tar.gz.
File metadata
- Download URL: cyrus_payments-0.1.2.tar.gz
- Upload date:
- Size: 34.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8a9b7e53c47f16352f7d3d1613b7deaadc8ab820b188c8d54c5d55e88ba62925
|
|
| MD5 |
3c46d43ff3d803514d30a96eb6ee3805
|
|
| BLAKE2b-256 |
8b95fd5dfd3c5822e8cacfed59397e0297f66feb9801fa46013e311e00e4f918
|
Provenance
The following attestation bundles were made for cyrus_payments-0.1.2.tar.gz:
Publisher:
sdk-python-publish.yml on ojov/cyrus
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
cyrus_payments-0.1.2.tar.gz -
Subject digest:
8a9b7e53c47f16352f7d3d1613b7deaadc8ab820b188c8d54c5d55e88ba62925 - Sigstore transparency entry: 2161324063
- Sigstore integration time:
-
Permalink:
ojov/cyrus@f32599c27e9cb147bb0a8132621d22da3cc087da -
Branch / Tag:
refs/tags/sdk-python-v0.1.2 - Owner: https://github.com/ojov
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
sdk-python-publish.yml@f32599c27e9cb147bb0a8132621d22da3cc087da -
Trigger Event:
push
-
Statement type:
File details
Details for the file cyrus_payments-0.1.2-py3-none-any.whl.
File metadata
- Download URL: cyrus_payments-0.1.2-py3-none-any.whl
- Upload date:
- Size: 16.0 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 |
976938579f08f3dba4e0f50f6dcce01b3997d82501b2969325506e5a376bc377
|
|
| MD5 |
a468147154de8328df71f49a81f18947
|
|
| BLAKE2b-256 |
247fd539ea4e7375a516dd45fb96a49a7e675c17e7e0f43abbd6422dd41f8e78
|
Provenance
The following attestation bundles were made for cyrus_payments-0.1.2-py3-none-any.whl:
Publisher:
sdk-python-publish.yml on ojov/cyrus
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
cyrus_payments-0.1.2-py3-none-any.whl -
Subject digest:
976938579f08f3dba4e0f50f6dcce01b3997d82501b2969325506e5a376bc377 - Sigstore transparency entry: 2161324220
- Sigstore integration time:
-
Permalink:
ojov/cyrus@f32599c27e9cb147bb0a8132621d22da3cc087da -
Branch / Tag:
refs/tags/sdk-python-v0.1.2 - Owner: https://github.com/ojov
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
sdk-python-publish.yml@f32599c27e9cb147bb0a8132621d22da3cc087da -
Trigger Event:
push
-
Statement type: