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
Metadata
Release files for cyrus-payments 0.1.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| cyrus_payments-0.1.2.tar.gz | 34.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| cyrus_payments-0.1.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 50.4 kB
Release files / cyrus_payments-0.1.2.tar.gz
| Download URL | cyrus_payments-0.1.2.tar.gz |
|---|---|
| Size | 34.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8a9b7e53c47f16352f7d3d1613b7deaadc8ab820b188c8d54c5d55e88ba62925
|
|
BLAKE2b-256 checksum How to use checksums |
8b95fd5dfd3c5822e8cacfed59397e0297f66feb9801fa46013e311e00e4f918
|
| 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 13, 2026.
Transparency logRelease files / cyrus_payments-0.1.2-py3-none-any.whl
| Download URL | cyrus_payments-0.1.2-py3-none-any.whl |
|---|---|
| Size | 16.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
976938579f08f3dba4e0f50f6dcce01b3997d82501b2969325506e5a376bc377
|
|
BLAKE2b-256 checksum How to use checksums |
247fd539ea4e7375a516dd45fb96a49a7e675c17e7e0f43abbd6422dd41f8e78
|
| 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 13, 2026.
Transparency log