ralio 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. ralio-0.1.0/.github/CODEOWNERS +8 -0
  2. ralio-0.1.0/.github/ISSUE_TEMPLATE/bug_report.yml +47 -0
  3. ralio-0.1.0/.github/ISSUE_TEMPLATE/config.yml +8 -0
  4. ralio-0.1.0/.github/ISSUE_TEMPLATE/feature_request.yml +25 -0
  5. ralio-0.1.0/.github/PULL_REQUEST_TEMPLATE.md +21 -0
  6. ralio-0.1.0/.github/dependabot.yml +16 -0
  7. ralio-0.1.0/.github/workflows/ci.yml +24 -0
  8. ralio-0.1.0/.github/workflows/release.yml +43 -0
  9. ralio-0.1.0/.gitignore +25 -0
  10. ralio-0.1.0/.pre-commit-config.yaml +16 -0
  11. ralio-0.1.0/CHANGELOG.md +28 -0
  12. ralio-0.1.0/CODE_OF_CONDUCT.md +46 -0
  13. ralio-0.1.0/CONTRIBUTING.md +57 -0
  14. ralio-0.1.0/LICENSE +21 -0
  15. ralio-0.1.0/PKG-INFO +210 -0
  16. ralio-0.1.0/README.md +181 -0
  17. ralio-0.1.0/SECURITY.md +37 -0
  18. ralio-0.1.0/examples/quickstart.py +47 -0
  19. ralio-0.1.0/pyproject.toml +55 -0
  20. ralio-0.1.0/src/ralio/__init__.py +59 -0
  21. ralio-0.1.0/src/ralio/_crypto.py +161 -0
  22. ralio-0.1.0/src/ralio/_store.py +105 -0
  23. ralio-0.1.0/src/ralio/auth.py +111 -0
  24. ralio-0.1.0/src/ralio/client.py +131 -0
  25. ralio-0.1.0/src/ralio/errors.py +106 -0
  26. ralio-0.1.0/src/ralio/py.typed +0 -0
  27. ralio-0.1.0/src/ralio/registration.py +225 -0
  28. ralio-0.1.0/src/ralio/resources/__init__.py +11 -0
  29. ralio-0.1.0/src/ralio/resources/agents.py +21 -0
  30. ralio-0.1.0/src/ralio/resources/chat.py +81 -0
  31. ralio-0.1.0/src/ralio/resources/payment_intents.py +34 -0
  32. ralio-0.1.0/src/ralio/resources/transactions.py +31 -0
  33. ralio-0.1.0/src/ralio/transport.py +151 -0
  34. ralio-0.1.0/src/ralio/types.py +247 -0
  35. ralio-0.1.0/tests/conftest.py +43 -0
  36. ralio-0.1.0/tests/test_client.py +316 -0
  37. ralio-0.1.0/tests/test_crypto.py +81 -0
  38. ralio-0.1.0/tests/test_registration.py +188 -0
  39. ralio-0.1.0/tests/test_zero_config.py +85 -0
@@ -0,0 +1,8 @@
1
+ # Default owners for everything in the repo.
2
+ * @lgrosales
3
+
4
+ # Security-sensitive crypto and auth lifecycle get explicit ownership.
5
+ /src/ralio/_crypto.py @lgrosales
6
+ /src/ralio/auth.py @lgrosales
7
+ /src/ralio/transport.py @lgrosales
8
+ /src/ralio/registration.py @lgrosales
@@ -0,0 +1,47 @@
1
+ name: Bug report
2
+ description: Report a problem with the Ralio Python SDK
3
+ labels: ["bug"]
4
+ body:
5
+ - type: markdown
6
+ attributes:
7
+ value: |
8
+ Thanks for reporting a bug. **Do not include private keys, access
9
+ tokens, or registration tickets** in this issue. For security
10
+ vulnerabilities, see [SECURITY.md](../../SECURITY.md) instead.
11
+ - type: textarea
12
+ id: what-happened
13
+ attributes:
14
+ label: What happened?
15
+ description: A clear description of the bug, including the full traceback if any.
16
+ placeholder: When I call client.chat.send(...), I get ...
17
+ validations:
18
+ required: true
19
+ - type: textarea
20
+ id: reproduce
21
+ attributes:
22
+ label: Steps to reproduce
23
+ description: A minimal code snippet that triggers the issue (redact secrets).
24
+ render: python
25
+ validations:
26
+ required: true
27
+ - type: input
28
+ id: sdk-version
29
+ attributes:
30
+ label: SDK version
31
+ placeholder: "0.1.0 (python -c 'import ralio; print(ralio.__version__)')"
32
+ validations:
33
+ required: true
34
+ - type: input
35
+ id: python-version
36
+ attributes:
37
+ label: Python version
38
+ placeholder: "3.12"
39
+ validations:
40
+ required: true
41
+ - type: textarea
42
+ id: extra
43
+ attributes:
44
+ label: Anything else?
45
+ description: OS, networking setup (proxies), or other context.
46
+ validations:
47
+ required: false
@@ -0,0 +1,8 @@
1
+ blank_issues_enabled: false
2
+ contact_links:
3
+ - name: Security vulnerability
4
+ url: https://github.com/Ralioco/ralio-python/security/advisories/new
5
+ about: Report security issues privately — never in a public issue.
6
+ - name: Ralio documentation
7
+ url: https://docs.ralio.co
8
+ about: API reference, authentication guides, and concepts.
@@ -0,0 +1,25 @@
1
+ name: Feature request
2
+ description: Suggest a new capability or improvement
3
+ labels: ["enhancement"]
4
+ body:
5
+ - type: textarea
6
+ id: problem
7
+ attributes:
8
+ label: What problem are you trying to solve?
9
+ description: Describe the use case, not just the proposed solution.
10
+ validations:
11
+ required: true
12
+ - type: textarea
13
+ id: proposal
14
+ attributes:
15
+ label: Proposed solution
16
+ description: What would the API look like? A code sketch helps.
17
+ render: python
18
+ validations:
19
+ required: false
20
+ - type: textarea
21
+ id: alternatives
22
+ attributes:
23
+ label: Alternatives considered
24
+ validations:
25
+ required: false
@@ -0,0 +1,21 @@
1
+ ## Summary
2
+
3
+ <!-- What does this change and why? -->
4
+
5
+ ## Changes
6
+
7
+ <!-- Bullet the notable changes. -->
8
+ -
9
+
10
+ ## Checklist
11
+
12
+ - [ ] `ruff check .` passes
13
+ - [ ] `mypy` passes
14
+ - [ ] `pytest -q` passes (new behavior has tests; network is mocked)
15
+ - [ ] `CHANGELOG.md` updated under the unreleased section
16
+ - [ ] No new runtime dependencies (or discussed in the issue)
17
+ - [ ] This PR touches security-sensitive code (key handling / signing / tokens) — flagged for extra review: yes / no
18
+
19
+ ## Related issues
20
+
21
+ <!-- Closes #123 -->
@@ -0,0 +1,16 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "pip"
4
+ directory: "/"
5
+ schedule:
6
+ interval: "weekly"
7
+ groups:
8
+ python-dependencies:
9
+ patterns:
10
+ - "*"
11
+ open-pull-requests-limit: 5
12
+
13
+ - package-ecosystem: "github-actions"
14
+ directory: "/"
15
+ schedule:
16
+ interval: "weekly"
@@ -0,0 +1,24 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ test:
10
+ runs-on: ubuntu-latest
11
+ strategy:
12
+ fail-fast: false
13
+ matrix:
14
+ python-version: ["3.10", "3.11", "3.12", "3.13"]
15
+ steps:
16
+ - uses: actions/checkout@v6
17
+ - uses: actions/setup-python@v6
18
+ with:
19
+ python-version: ${{ matrix.python-version }}
20
+ - run: python -m pip install --upgrade pip
21
+ - run: pip install -e ".[dev]"
22
+ - run: ruff check .
23
+ - run: mypy
24
+ - run: pytest -q
@@ -0,0 +1,43 @@
1
+ name: Release
2
+
3
+ # Publishes to PyPI via Trusted Publishing (OIDC) when a version tag is pushed.
4
+ # No API token is stored — PyPI verifies the workflow identity directly.
5
+ # One-time setup: add this repo + workflow as a trusted publisher for the
6
+ # `ralio` project at https://pypi.org/manage/account/publishing/.
7
+
8
+ on:
9
+ push:
10
+ tags:
11
+ - "v*"
12
+
13
+ permissions:
14
+ contents: read
15
+
16
+ jobs:
17
+ build:
18
+ runs-on: ubuntu-latest
19
+ steps:
20
+ - uses: actions/checkout@v6
21
+ - uses: actions/setup-python@v6
22
+ with:
23
+ python-version: "3.12"
24
+ - run: python -m pip install --upgrade pip build twine
25
+ - run: python -m build
26
+ - run: twine check dist/*
27
+ - uses: actions/upload-artifact@v7
28
+ with:
29
+ name: dist
30
+ path: dist/
31
+
32
+ publish:
33
+ needs: build
34
+ runs-on: ubuntu-latest
35
+ environment: pypi
36
+ permissions:
37
+ id-token: write # required for Trusted Publishing
38
+ steps:
39
+ - uses: actions/download-artifact@v8
40
+ with:
41
+ name: dist
42
+ path: dist/
43
+ - uses: pypa/gh-action-pypi-publish@release/v1
ralio-0.1.0/.gitignore ADDED
@@ -0,0 +1,25 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ .eggs/
6
+ build/
7
+ dist/
8
+ .venv/
9
+ venv/
10
+
11
+ # Tooling
12
+ .mypy_cache/
13
+ .ruff_cache/
14
+ .pytest_cache/
15
+ .coverage
16
+ htmlcov/
17
+
18
+ # Secrets — never commit private keys
19
+ *.pem
20
+ ralio-key*
21
+
22
+ # OS / editor
23
+ .DS_Store
24
+ .idea/
25
+ .vscode/
@@ -0,0 +1,16 @@
1
+ repos:
2
+ - repo: https://github.com/astral-sh/ruff-pre-commit
3
+ rev: v0.6.9
4
+ hooks:
5
+ - id: ruff
6
+ args: [--fix]
7
+ - id: ruff-format
8
+
9
+ - repo: https://github.com/pre-commit/pre-commit-hooks
10
+ rev: v5.0.0
11
+ hooks:
12
+ - id: end-of-file-fixer
13
+ - id: trailing-whitespace
14
+ - id: check-yaml
15
+ - id: check-added-large-files
16
+ - id: detect-private-key
@@ -0,0 +1,28 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0 (unreleased)
4
+
5
+ Initial release.
6
+
7
+ - OAuth 2.1 `client_credentials` + `private_key_jwt` + DPoP authentication.
8
+ - One-time credential-binding registration (`ralio.register`) with
9
+ **synchronous activation** (server PR agentic-payment-gateway#1182): the
10
+ binding is active as soon as the submit call returns — no owner-approval
11
+ step, no polling. Ticket errors (`invalid_ticket`, `ticket_expired`,
12
+ `ticket_already_consumed` with `used_at`/`used_by_host` context,
13
+ `public_key_already_in_use`, `invalid_public_key`, `invalid_scope`,
14
+ `scope_exceeds_ticket_ceiling`) map into `RalioRegistrationError`.
15
+ - Zero-config onboarding, in lockstep with the Node SDK
16
+ (Ralioco/ralio-node#15): `register()` defaults its ticket to
17
+ `RALIO_REGISTRATION_TICKET`, mints the first access token, and persists
18
+ credentials to `~/.ralio/` (the CLI's store, so `register()` and
19
+ `ralio auth agent` are interchangeable; private key at
20
+ `~/.ralio/keys/<jkt>.pem`, `private_key_path` overrides). `RalioClient()`
21
+ then constructs with no arguments. Env overrides: `RALIO_API_URL`,
22
+ `RALIO_CONFIG_DIR`. `CredentialBinding` gained `key_path`; `scopes` now
23
+ reflects the granted token scope.
24
+ - `client.chat.send` and `client.chat.stream` (SSE). `agent_id` is optional —
25
+ when omitted, the SDK resolves the single agent the credential is bound to
26
+ (via `client.agents.list()`) and caches it.
27
+ - `client.agents.list` and the `Agent` type.
28
+ - `client.transactions.list` and `client.payment_intents.list` (paginated).
@@ -0,0 +1,46 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ We as members, contributors, and leaders pledge to make participation in our
6
+ community a harassment-free experience for everyone, regardless of age, body
7
+ size, visible or invisible disability, ethnicity, sex characteristics, gender
8
+ identity and expression, level of experience, education, socio-economic status,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ We pledge to act and interact in ways that contribute to an open, welcoming,
13
+ diverse, inclusive, and healthy community.
14
+
15
+ ## Our Standards
16
+
17
+ Examples of behavior that contributes to a positive environment:
18
+
19
+ - Demonstrating empathy and kindness toward other people
20
+ - Being respectful of differing opinions, viewpoints, and experiences
21
+ - Giving and gracefully accepting constructive feedback
22
+ - Accepting responsibility and apologizing to those affected by our mistakes
23
+ - Focusing on what is best for the overall community
24
+
25
+ Examples of unacceptable behavior:
26
+
27
+ - The use of sexualized language or imagery, and sexual attention or advances
28
+ - Trolling, insulting or derogatory comments, and personal or political attacks
29
+ - Public or private harassment
30
+ - Publishing others' private information without their explicit permission
31
+ - Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Enforcement
35
+
36
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
37
+ reported to the maintainers at **conduct@ralio.co**. All complaints will be
38
+ reviewed and investigated promptly and fairly.
39
+
40
+ ## Attribution
41
+
42
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage],
43
+ version 2.1, available at
44
+ https://www.contributor-covenant.org/version/2/1/code_of_conduct.html.
45
+
46
+ [homepage]: https://www.contributor-covenant.org
@@ -0,0 +1,57 @@
1
+ # Contributing
2
+
3
+ Thanks for your interest in improving the Ralio Python SDK.
4
+
5
+ ## Development setup
6
+
7
+ Requires Python 3.10+.
8
+
9
+ ```bash
10
+ git clone https://github.com/Ralioco/ralio-python
11
+ cd ralio-python
12
+ python -m venv .venv && source .venv/bin/activate
13
+ pip install -e ".[dev]"
14
+ ```
15
+
16
+ ## Checks
17
+
18
+ All of these must pass before a PR is merged; CI runs them on Python 3.10–3.13.
19
+
20
+ ```bash
21
+ ruff check . # lint
22
+ mypy # static types (strict)
23
+ pytest -q # tests
24
+ ```
25
+
26
+ Optionally install the pre-commit hooks so the lint/type checks run on every
27
+ commit:
28
+
29
+ ```bash
30
+ pip install pre-commit && pre-commit install
31
+ ```
32
+
33
+ ## Guidelines
34
+
35
+ - **Public API.** Anything importable from `ralio` (not underscore-prefixed) is
36
+ public and follows SemVer. Modules like `ralio._crypto` are internal and may
37
+ change without notice.
38
+ - **Types.** Every public function, method, and attribute is type-annotated;
39
+ `mypy --strict` must pass.
40
+ - **Tests.** New behavior needs tests. Network is mocked with `respx` — tests
41
+ must not hit a live API. Crypto correctness (DPoP/assertion claims and
42
+ signatures) is tested with real keys.
43
+ - **No new runtime dependencies** without discussion. The SDK intentionally
44
+ depends only on `httpx` and `PyJWT[crypto]`.
45
+ - **Security-sensitive code** (key handling, signing, token lifecycle) gets
46
+ extra review. If your change touches it, call that out in the PR.
47
+
48
+ ## Commit messages & PRs
49
+
50
+ - Write a clear subject line in the imperative mood.
51
+ - Keep PRs focused; update `CHANGELOG.md` under the unreleased section.
52
+ - Link any related issue.
53
+
54
+ ## Reporting bugs / requesting features
55
+
56
+ Use the issue templates. For security issues, see [SECURITY.md](SECURITY.md) —
57
+ do not file a public issue.
ralio-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ralio
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
ralio-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,210 @@
1
+ Metadata-Version: 2.4
2
+ Name: ralio
3
+ Version: 0.1.0
4
+ Summary: Official Python SDK for the Ralio agentic payment API.
5
+ Project-URL: Homepage, https://ralio.co
6
+ Project-URL: Documentation, https://docs.ralio.co
7
+ Author: Ralio
8
+ License: MIT
9
+ License-File: LICENSE
10
+ Keywords: agents,dpop,oauth,payments,ralio,sdk
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Typing :: Typed
20
+ Requires-Python: >=3.10
21
+ Requires-Dist: httpx>=0.27
22
+ Requires-Dist: pyjwt[crypto]>=2.8
23
+ Provides-Extra: dev
24
+ Requires-Dist: mypy>=1.10; extra == 'dev'
25
+ Requires-Dist: pytest>=8.0; extra == 'dev'
26
+ Requires-Dist: respx>=0.21; extra == 'dev'
27
+ Requires-Dist: ruff>=0.5; extra == 'dev'
28
+ Description-Content-Type: text/markdown
29
+
30
+ # Ralio Python SDK
31
+
32
+ [![PyPI version](https://img.shields.io/pypi/v/ralio.svg)](https://pypi.org/project/ralio/)
33
+ [![Python versions](https://img.shields.io/pypi/pyversions/ralio.svg)](https://pypi.org/project/ralio/)
34
+ [![CI](https://github.com/Ralioco/ralio-python/actions/workflows/ci.yml/badge.svg)](https://github.com/Ralioco/ralio-python/actions/workflows/ci.yml)
35
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
36
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
37
+
38
+ The official Python client for the [Ralio](https://ralio.co) agentic payment API.
39
+
40
+ It handles the machine-authentication path end to end — OAuth 2.1
41
+ `client_credentials` with `private_key_jwt` and DPoP-bound access tokens — so
42
+ your integration can talk to an agent without hand-rolling JWT signing, proof
43
+ generation, or token refresh.
44
+
45
+ > **Scope.** This SDK targets autonomous integrations (CI jobs, agent hosts,
46
+ > server-side callers). It authenticates as a **credential binding**, which can
47
+ > hold the `agents:execute` and `transactions:read` scopes. Agent and binding
48
+ > management (`agents:config`) is a human-only operation in the console and is
49
+ > intentionally not part of this SDK.
50
+
51
+ ## Installation
52
+
53
+ ```bash
54
+ pip install ralio
55
+ ```
56
+
57
+ Requires Python 3.10+.
58
+
59
+ ## Authentication model
60
+
61
+ Ralio's machine path has no shared secrets. Each credential is a P-256 private
62
+ key that lives on exactly one host:
63
+
64
+ 1. The **owner** mints a one-time registration ticket in the console
65
+ (**Settings → Credentials → New credential**), choosing the target agent and
66
+ a scope ceiling. That is where consent happens. They send you the
67
+ `ralio-reg-…` ticket.
68
+ 2. You call `ralio.register()` once on the agent host. It generates a keypair
69
+ locally and submits the public key with the ticket; the binding is active
70
+ as soon as the server responds — no approval step, no polling. The owner
71
+ gets an email receipt with a revoke link. The credentials are persisted to
72
+ `~/.ralio/` — the same store the `ralio` CLI uses, so `register()` and
73
+ `ralio auth agent` are interchangeable.
74
+ 3. From then on, `RalioClient` mints and refreshes DPoP-bound access tokens
75
+ transparently and signs a fresh proof for every request.
76
+
77
+ See the [API authentication guide](https://docs.ralio.co/api-reference/authentication)
78
+ for the protocol details.
79
+
80
+ ## Quickstart
81
+
82
+ With the owner's ticket in `RALIO_REGISTRATION_TICKET`, onboarding is two
83
+ calls:
84
+
85
+ ```python
86
+ import ralio
87
+
88
+ ralio.register() # run once; the binding is active when this returns
89
+
90
+ client = ralio.RalioClient() # zero-config: reads the persisted credentials
91
+ reply = client.chat.send(message="What is my current balance?")
92
+ ```
93
+
94
+ `register()` activates the binding in a single call (or raises
95
+ `RalioRegistrationError` if the ticket is invalid, expired, or already
96
+ consumed). The private key is generated locally, written to
97
+ `~/.ralio/keys/<jkt>.pem`, and never leaves the host.
98
+
99
+ Everything is overridable when you want to manage credentials yourself:
100
+
101
+ ```python
102
+ import ralio
103
+
104
+ binding = ralio.register(
105
+ ticket="ralio-reg-...", # instead of RALIO_REGISTRATION_TICKET
106
+ private_key_path="ralio-key.pem", # generated and written here
107
+ requested_scopes=["agents:execute", "transactions:read"],
108
+ )
109
+ print(binding.client_id) # cb_... — store this alongside the key
110
+ ```
111
+
112
+ ## Use the client
113
+
114
+ ```python
115
+ import ralio
116
+
117
+ # Zero-config: reads the credentials persisted by register() / `ralio auth agent`.
118
+ client = ralio.RalioClient()
119
+
120
+ # Or manage credentials yourself:
121
+ # client = ralio.RalioClient(
122
+ # client_id="cb_...",
123
+ # private_key_path="ralio-key.pem",
124
+ # )
125
+
126
+ # Synchronous chat — agent_id is resolved automatically for a single-agent
127
+ # credential; pass agent_id explicitly to target one of several agents.
128
+ reply = client.chat.send(message="What is my current balance?")
129
+ print(reply.reply)
130
+
131
+ # Streaming chat (server-sent events)
132
+ for event in client.chat.stream(message="List my recent payments"):
133
+ if event.event == "text_delta":
134
+ print(event.text, end="", flush=True)
135
+ elif event.event == "tool_started":
136
+ print(f"\n[tool] {event.data['tool_name']}")
137
+
138
+ # Transactions — list endpoints are paginated; a Page is iterable and sized.
139
+ page = client.transactions.list(per_page=20)
140
+ print(f"showing {len(page)} of {page.total} transactions (page {page.page})")
141
+ for txn in page:
142
+ print(txn.date, txn.amount, txn.currency, txn.creditor, txn.status)
143
+
144
+ # Payment intents — what the agent proposed, with per-leg execution detail.
145
+ for intent in client.payment_intents.list(per_page=20):
146
+ print(intent.created_at, intent.total_amount, intent.currency, intent.approval_status)
147
+
148
+ client.close()
149
+ ```
150
+
151
+ `RalioClient` is also a context manager:
152
+
153
+ ```python
154
+ with ralio.RalioClient() as client:
155
+ ...
156
+ ```
157
+
158
+ ## Environment variables
159
+
160
+ | Variable | Meaning |
161
+ | --------------------------- | -------------------------------------------------------------------- |
162
+ | `RALIO_REGISTRATION_TICKET` | Default ticket for `register()` — same variable the CLI reads |
163
+ | `RALIO_API_URL` | API origin (default `https://api.ralio.co`) |
164
+ | `RALIO_CONFIG_DIR` | Credential store location (default `~/.ralio`, shared with the CLI) |
165
+
166
+ ## Payments
167
+
168
+ There is no `payments.create()` method by design. Payments are executed by the
169
+ **agent**, not by direct REST calls: drive the agent with `chat.send` /
170
+ `chat.stream` ("Pay £500 to Bob for the April invoice") and it will create the
171
+ payment, subject to its spend limits and approval rules. Use
172
+ `transactions.list` (executed payments) and `payment_intents.list` (what the
173
+ agent proposed, with per-leg status) to read what the agent did.
174
+
175
+ ## Errors
176
+
177
+ All errors subclass `ralio.RalioError`:
178
+
179
+ | Exception | When |
180
+ |-----------|------|
181
+ | `RalioAuthError` (401) | Missing/invalid token, failed assertion, or rejected DPoP proof |
182
+ | `RalioPermissionError` (403) | Token lacks the required scope, or resource not owned |
183
+ | `RalioNotFoundError` (404) | Resource doesn't exist |
184
+ | `RalioValidationError` (422) | Invalid field values or business-rule violation |
185
+ | `RalioRateLimitError` (429) | Rate limited — back off and retry |
186
+ | `RalioAPIError` | Any other HTTP error (carries `status_code`, `detail`) |
187
+ | `RalioRegistrationError` | Registration failed (invalid / expired / consumed ticket) |
188
+ | `RalioConfigError` | Local configuration problem |
189
+
190
+ ```python
191
+ import ralio
192
+
193
+ try:
194
+ client.chat.send(agent_id="...", message="...")
195
+ except ralio.RalioPermissionError as exc:
196
+ print("scope problem:", exc.detail)
197
+ ```
198
+
199
+ ## Development
200
+
201
+ ```bash
202
+ pip install -e ".[dev]"
203
+ ruff check .
204
+ mypy
205
+ pytest -q
206
+ ```
207
+
208
+ ## License
209
+
210
+ MIT — see [LICENSE](LICENSE).