vatly-api-python 0.1.0a2__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.
- vatly_api_python-0.1.0a2/.gitignore +9 -0
- vatly_api_python-0.1.0a2/CHANGELOG.md +18 -0
- vatly_api_python-0.1.0a2/LICENSE +21 -0
- vatly_api_python-0.1.0a2/PKG-INFO +144 -0
- vatly_api_python-0.1.0a2/README.md +126 -0
- vatly_api_python-0.1.0a2/docs/API.md +110 -0
- vatly_api_python-0.1.0a2/docs/Chargebacks.md +11 -0
- vatly_api_python-0.1.0a2/docs/Checkouts.md +14 -0
- vatly_api_python-0.1.0a2/docs/Customers.md +12 -0
- vatly_api_python-0.1.0a2/docs/Development.md +55 -0
- vatly_api_python-0.1.0a2/docs/OneOffProducts.md +12 -0
- vatly_api_python-0.1.0a2/docs/Orders.md +15 -0
- vatly_api_python-0.1.0a2/docs/README.md +25 -0
- vatly_api_python-0.1.0a2/docs/Refunds.md +13 -0
- vatly_api_python-0.1.0a2/docs/SubscriptionPlans.md +13 -0
- vatly_api_python-0.1.0a2/docs/Subscriptions.md +21 -0
- vatly_api_python-0.1.0a2/docs/Webhooks.md +34 -0
- vatly_api_python-0.1.0a2/openapi.yaml +8584 -0
- vatly_api_python-0.1.0a2/pyproject.toml +55 -0
- vatly_api_python-0.1.0a2/scripts/check_package.py +21 -0
- vatly_api_python-0.1.0a2/scripts/generate.py +333 -0
- vatly_api_python-0.1.0a2/src/vatly/__init__.py +43 -0
- vatly_api_python-0.1.0a2/src/vatly/_model.py +13 -0
- vatly_api_python-0.1.0a2/src/vatly/_transport.py +284 -0
- vatly_api_python-0.1.0a2/src/vatly/_version.py +1 -0
- vatly_api_python-0.1.0a2/src/vatly/client.py +100 -0
- vatly_api_python-0.1.0a2/src/vatly/errors.py +86 -0
- vatly_api_python-0.1.0a2/src/vatly/models.py +3009 -0
- vatly_api_python-0.1.0a2/src/vatly/pagination.py +29 -0
- vatly_api_python-0.1.0a2/src/vatly/py.typed +1 -0
- vatly_api_python-0.1.0a2/src/vatly/resources.py +2003 -0
- vatly_api_python-0.1.0a2/src/vatly/webhooks.py +146 -0
- vatly_api_python-0.1.0a2/tests/conftest.py +49 -0
- vatly_api_python-0.1.0a2/tests/test_sdk.py +446 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0a2 — Unreleased
|
|
4
|
+
|
|
5
|
+
- Use the PyPI distribution name `vatly-api-python`; imports remain `vatly`.
|
|
6
|
+
- Preserve enabled currency/precision intersections in generated Money models.
|
|
7
|
+
- Reject empty successful resource responses instead of returning untyped None.
|
|
8
|
+
- Include request method and URL in timeout/connection errors in both clients.
|
|
9
|
+
|
|
10
|
+
## 0.1.0a1 — Unreleased
|
|
11
|
+
|
|
12
|
+
> Alpha — under active development. Every alpha release may include breaking changes. Pin exact versions during alpha.
|
|
13
|
+
|
|
14
|
+
- Initial typed Python SDK covering all 50 production OpenAPI operations.
|
|
15
|
+
- Pydantic models, sync/async httpx clients, cursor iterators, retries, idempotency,
|
|
16
|
+
per-request timeouts, typed errors and raw-body webhook verification.
|
|
17
|
+
- Documentation, reproducible code generation, CI, trusted-publishing workflow,
|
|
18
|
+
and cached orb development setup. Nothing published or tagged.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Vatly
|
|
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.
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: vatly-api-python
|
|
3
|
+
Version: 0.1.0a2
|
|
4
|
+
Summary: Python SDK for Vatly, the merchant of record for AI, software and digital products
|
|
5
|
+
Project-URL: Homepage, https://github.com/vatly/vatly-api-python
|
|
6
|
+
Project-URL: Documentation, https://docs.vatly.com
|
|
7
|
+
Project-URL: Issues, https://github.com/vatly/vatly-api-python/issues
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Typing :: Typed
|
|
13
|
+
Requires-Python: >=3.10
|
|
14
|
+
Requires-Dist: httpx<1,>=0.27
|
|
15
|
+
Requires-Dist: pydantic<3,>=2.11
|
|
16
|
+
Requires-Dist: typing-extensions<5,>=4.12
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
|
|
19
|
+
# Vatly Python SDK
|
|
20
|
+
|
|
21
|
+
> **Alpha — under active development. Every alpha release may include breaking changes. Pin exact versions during alpha.**
|
|
22
|
+
|
|
23
|
+
Python SDK for Vatly, the merchant of record for AI, software and digital products.
|
|
24
|
+
Python 3.10+, Pydantic v2 models, and pooled httpx sync/async clients. All 50 production
|
|
25
|
+
OpenAPI operations are covered. No package has been published yet.
|
|
26
|
+
|
|
27
|
+
## Installation
|
|
28
|
+
|
|
29
|
+
Once published:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pip install vatly-api-python==0.1.0a2
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
For local development: `uv sync --locked`.
|
|
36
|
+
|
|
37
|
+
## Quick start
|
|
38
|
+
|
|
39
|
+
Get a test or live API key from [the dashboard](https://my.vatly.com), under Settings > API.
|
|
40
|
+
Keep keys server-side. The client reads `VATLY_API_KEY` by default.
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
from vatly import VatlyClient
|
|
44
|
+
from vatly.models import CreateCheckoutRequest
|
|
45
|
+
|
|
46
|
+
with VatlyClient() as vatly:
|
|
47
|
+
checkout = vatly.checkouts.create(
|
|
48
|
+
CreateCheckoutRequest(
|
|
49
|
+
redirect_url_success="https://example.com/success",
|
|
50
|
+
redirect_url_canceled="https://example.com/canceled",
|
|
51
|
+
products=[{"id": "subscription_plan_abc123", "quantity": 1}],
|
|
52
|
+
)
|
|
53
|
+
)
|
|
54
|
+
print(checkout.links.checkout_url.href)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
from vatly import AsyncVatlyClient
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
async def read_orders():
|
|
62
|
+
async with AsyncVatlyClient() as vatly:
|
|
63
|
+
async for order in vatly.orders.list_all({"limit": 100}):
|
|
64
|
+
print(order.id, order.total)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Pydantic models use snake_case attributes and accept Vatly's camelCase aliases.
|
|
68
|
+
Dictionary inputs use **wire-format camelCase keys**. Model requests serialize with
|
|
69
|
+
aliases and omit unset fields; explicitly supplied `None` is sent as JSON null.
|
|
70
|
+
Money values remain decimal **strings**, never floats.
|
|
71
|
+
|
|
72
|
+
## Configuration and reliability
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
from vatly import VatlyClient, RequestOptions
|
|
76
|
+
|
|
77
|
+
with VatlyClient(timeout=30, max_retries=2) as vatly:
|
|
78
|
+
page = vatly.customers.list({"limit": 25}, options=RequestOptions(timeout=10))
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Timeouts are **seconds per httpx network phase per attempt**, not a total request deadline;
|
|
82
|
+
zero disables timeouts. Default base URL: `https://api.vatly.com/v1`.
|
|
83
|
+
`base_url`, `default_headers`, and `transport` (an httpx transport) can be overridden.
|
|
84
|
+
`mode` exposes `test` or `live`. Test helpers reject live keys before sending.
|
|
85
|
+
|
|
86
|
+
POST/PATCH/PUT/DELETE receive a UUID idempotency key, reused across retries.
|
|
87
|
+
Set `RequestOptions(idempotency_key="your-operation-id")` to reuse a business-operation key
|
|
88
|
+
across separate SDK calls. Set `idempotency_key_generator=None` to disable auto-keys.
|
|
89
|
+
GETs and keyed mutations retry 429, 5xx, timeouts and connection errors with exponential
|
|
90
|
+
backoff (250ms to 8s plus jitter), honoring numeric or HTTP-date `Retry-After`.
|
|
91
|
+
Keyed 409 retries **only** `details.idempotency_outcome == "in_progress"`.
|
|
92
|
+
Never blindly retry an `unknown` idempotency outcome: read/reconcile the resource first.
|
|
93
|
+
Unkeyed mutations do not retry. Redirects are not followed.
|
|
94
|
+
|
|
95
|
+
`Vatly-Client-Info` identifies SDK/runtime/OS/architecture. Pass
|
|
96
|
+
`client_info={"app": "MyApp/1.0", "framework": "Django/5"}` to add sanitized identifiers,
|
|
97
|
+
or `client_info=False` to suppress the SDK-generated header.
|
|
98
|
+
|
|
99
|
+
## Errors
|
|
100
|
+
|
|
101
|
+
All API/transport errors extend `VatlyError`, with `status`, `body`, `url`, `method`,
|
|
102
|
+
`details`, `errors` and `retry_after`. Network errors retain the httpx exception as `__cause__`.
|
|
103
|
+
|
|
104
|
+
| Status | Error |
|
|
105
|
+
| --- | --- |
|
|
106
|
+
| 401 | `VatlyAuthError` |
|
|
107
|
+
| 403 | `VatlyForbiddenError` |
|
|
108
|
+
| 404 | `VatlyNotFoundError` |
|
|
109
|
+
| 409 | `VatlyConflictError` |
|
|
110
|
+
| 422 | `VatlyValidationError` |
|
|
111
|
+
| 429 | `VatlyRateLimitError` |
|
|
112
|
+
| Other / invalid response | `VatlyApiError` |
|
|
113
|
+
| Network / timeout | `VatlyConnectionError` / `VatlyTimeoutError` |
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
from vatly import VatlyValidationError
|
|
117
|
+
|
|
118
|
+
try:
|
|
119
|
+
# Your API call here
|
|
120
|
+
pass
|
|
121
|
+
except VatlyValidationError as error:
|
|
122
|
+
print(error.errors)
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
See [getting started and resource guides](docs/README.md), the generated
|
|
126
|
+
[method inventory](docs/API.md), [webhook guide](docs/Webhooks.md), and
|
|
127
|
+
[production API reference](https://docs.vatly.com/api-reference).
|
|
128
|
+
|
|
129
|
+
## Development
|
|
130
|
+
|
|
131
|
+
Run `.agents/setup` in a fresh orb. It installs uv if missing, provisions Python 3.12,
|
|
132
|
+
syncs locked dependencies and runs an import sanity check. Warm runs reuse uv's caches.
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
uv run python scripts/generate.py --check
|
|
136
|
+
uv run ruff check .
|
|
137
|
+
uv run ruff format --check .
|
|
138
|
+
uv run mypy
|
|
139
|
+
uv run pytest
|
|
140
|
+
uv build
|
|
141
|
+
uv run twine check dist/*
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
See [maintainer notes](docs/Development.md) for spec regeneration, docs sync, and trusted publishing.
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# Vatly Python SDK
|
|
2
|
+
|
|
3
|
+
> **Alpha — under active development. Every alpha release may include breaking changes. Pin exact versions during alpha.**
|
|
4
|
+
|
|
5
|
+
Python SDK for Vatly, the merchant of record for AI, software and digital products.
|
|
6
|
+
Python 3.10+, Pydantic v2 models, and pooled httpx sync/async clients. All 50 production
|
|
7
|
+
OpenAPI operations are covered. No package has been published yet.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
Once published:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pip install vatly-api-python==0.1.0a2
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
For local development: `uv sync --locked`.
|
|
18
|
+
|
|
19
|
+
## Quick start
|
|
20
|
+
|
|
21
|
+
Get a test or live API key from [the dashboard](https://my.vatly.com), under Settings > API.
|
|
22
|
+
Keep keys server-side. The client reads `VATLY_API_KEY` by default.
|
|
23
|
+
|
|
24
|
+
```python
|
|
25
|
+
from vatly import VatlyClient
|
|
26
|
+
from vatly.models import CreateCheckoutRequest
|
|
27
|
+
|
|
28
|
+
with VatlyClient() as vatly:
|
|
29
|
+
checkout = vatly.checkouts.create(
|
|
30
|
+
CreateCheckoutRequest(
|
|
31
|
+
redirect_url_success="https://example.com/success",
|
|
32
|
+
redirect_url_canceled="https://example.com/canceled",
|
|
33
|
+
products=[{"id": "subscription_plan_abc123", "quantity": 1}],
|
|
34
|
+
)
|
|
35
|
+
)
|
|
36
|
+
print(checkout.links.checkout_url.href)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
from vatly import AsyncVatlyClient
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
async def read_orders():
|
|
44
|
+
async with AsyncVatlyClient() as vatly:
|
|
45
|
+
async for order in vatly.orders.list_all({"limit": 100}):
|
|
46
|
+
print(order.id, order.total)
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Pydantic models use snake_case attributes and accept Vatly's camelCase aliases.
|
|
50
|
+
Dictionary inputs use **wire-format camelCase keys**. Model requests serialize with
|
|
51
|
+
aliases and omit unset fields; explicitly supplied `None` is sent as JSON null.
|
|
52
|
+
Money values remain decimal **strings**, never floats.
|
|
53
|
+
|
|
54
|
+
## Configuration and reliability
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
from vatly import VatlyClient, RequestOptions
|
|
58
|
+
|
|
59
|
+
with VatlyClient(timeout=30, max_retries=2) as vatly:
|
|
60
|
+
page = vatly.customers.list({"limit": 25}, options=RequestOptions(timeout=10))
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Timeouts are **seconds per httpx network phase per attempt**, not a total request deadline;
|
|
64
|
+
zero disables timeouts. Default base URL: `https://api.vatly.com/v1`.
|
|
65
|
+
`base_url`, `default_headers`, and `transport` (an httpx transport) can be overridden.
|
|
66
|
+
`mode` exposes `test` or `live`. Test helpers reject live keys before sending.
|
|
67
|
+
|
|
68
|
+
POST/PATCH/PUT/DELETE receive a UUID idempotency key, reused across retries.
|
|
69
|
+
Set `RequestOptions(idempotency_key="your-operation-id")` to reuse a business-operation key
|
|
70
|
+
across separate SDK calls. Set `idempotency_key_generator=None` to disable auto-keys.
|
|
71
|
+
GETs and keyed mutations retry 429, 5xx, timeouts and connection errors with exponential
|
|
72
|
+
backoff (250ms to 8s plus jitter), honoring numeric or HTTP-date `Retry-After`.
|
|
73
|
+
Keyed 409 retries **only** `details.idempotency_outcome == "in_progress"`.
|
|
74
|
+
Never blindly retry an `unknown` idempotency outcome: read/reconcile the resource first.
|
|
75
|
+
Unkeyed mutations do not retry. Redirects are not followed.
|
|
76
|
+
|
|
77
|
+
`Vatly-Client-Info` identifies SDK/runtime/OS/architecture. Pass
|
|
78
|
+
`client_info={"app": "MyApp/1.0", "framework": "Django/5"}` to add sanitized identifiers,
|
|
79
|
+
or `client_info=False` to suppress the SDK-generated header.
|
|
80
|
+
|
|
81
|
+
## Errors
|
|
82
|
+
|
|
83
|
+
All API/transport errors extend `VatlyError`, with `status`, `body`, `url`, `method`,
|
|
84
|
+
`details`, `errors` and `retry_after`. Network errors retain the httpx exception as `__cause__`.
|
|
85
|
+
|
|
86
|
+
| Status | Error |
|
|
87
|
+
| --- | --- |
|
|
88
|
+
| 401 | `VatlyAuthError` |
|
|
89
|
+
| 403 | `VatlyForbiddenError` |
|
|
90
|
+
| 404 | `VatlyNotFoundError` |
|
|
91
|
+
| 409 | `VatlyConflictError` |
|
|
92
|
+
| 422 | `VatlyValidationError` |
|
|
93
|
+
| 429 | `VatlyRateLimitError` |
|
|
94
|
+
| Other / invalid response | `VatlyApiError` |
|
|
95
|
+
| Network / timeout | `VatlyConnectionError` / `VatlyTimeoutError` |
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
from vatly import VatlyValidationError
|
|
99
|
+
|
|
100
|
+
try:
|
|
101
|
+
# Your API call here
|
|
102
|
+
pass
|
|
103
|
+
except VatlyValidationError as error:
|
|
104
|
+
print(error.errors)
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
See [getting started and resource guides](docs/README.md), the generated
|
|
108
|
+
[method inventory](docs/API.md), [webhook guide](docs/Webhooks.md), and
|
|
109
|
+
[production API reference](https://docs.vatly.com/api-reference).
|
|
110
|
+
|
|
111
|
+
## Development
|
|
112
|
+
|
|
113
|
+
Run `.agents/setup` in a fresh orb. It installs uv if missing, provisions Python 3.12,
|
|
114
|
+
syncs locked dependencies and runs an import sanity check. Warm runs reuse uv's caches.
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
uv run python scripts/generate.py --check
|
|
118
|
+
uv run ruff check .
|
|
119
|
+
uv run ruff format --check .
|
|
120
|
+
uv run mypy
|
|
121
|
+
uv run pytest
|
|
122
|
+
uv build
|
|
123
|
+
uv run twine check dist/*
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
See [maintainer notes](docs/Development.md) for spec regeneration, docs sync, and trusted publishing.
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# API reference
|
|
2
|
+
|
|
3
|
+
Generated from `openapi.yaml`. Sync and async clients expose the same methods.
|
|
4
|
+
Pass a Pydantic request/params model or a wire-format dictionary. All methods accept keyword-only `options=RequestOptions(...)`.
|
|
5
|
+
|
|
6
|
+
## `checkouts`
|
|
7
|
+
|
|
8
|
+
| Method | HTTP endpoint | Returns |
|
|
9
|
+
| --- | --- | --- |
|
|
10
|
+
| `list(params=None)` | `GET /checkouts` | `Page[Checkout]` |
|
|
11
|
+
| `create(body)` | `POST /checkouts` | `Checkout` |
|
|
12
|
+
| `get(checkout_id)` | `GET /checkouts/{checkoutId}` | `Checkout` |
|
|
13
|
+
|
|
14
|
+
## `customers`
|
|
15
|
+
|
|
16
|
+
| Method | HTTP endpoint | Returns |
|
|
17
|
+
| --- | --- | --- |
|
|
18
|
+
| `list(params=None)` | `GET /customers` | `Page[Customer]` |
|
|
19
|
+
| `create(body)` | `POST /customers` | `Customer` |
|
|
20
|
+
| `get(customer_id)` | `GET /customers/{customerId}` | `Customer` |
|
|
21
|
+
| `update(customer_id, body)` | `PATCH /customers/{customerId}` | `Customer` |
|
|
22
|
+
| `create_portal_session(customer_id, body)` | `POST /customers/{customerId}/portal-sessions` | `PortalSession` |
|
|
23
|
+
| `list_subscriptions(customer_id, params=None)` | `GET /customers/{customerId}/subscriptions` | `Page[Subscription]` |
|
|
24
|
+
| `get_subscription(customer_id, subscription_id)` | `GET /customers/{customerId}/subscriptions/{subscriptionId}` | `Subscription` |
|
|
25
|
+
|
|
26
|
+
## `one_off_products`
|
|
27
|
+
|
|
28
|
+
| Method | HTTP endpoint | Returns |
|
|
29
|
+
| --- | --- | --- |
|
|
30
|
+
| `list(params=None)` | `GET /one-off-products` | `Page[OneOffProduct]` |
|
|
31
|
+
| `create(body)` | `POST /one-off-products` | `OneOffProduct` |
|
|
32
|
+
| `get(one_off_product_id)` | `GET /one-off-products/{oneOffProductId}` | `OneOffProduct` |
|
|
33
|
+
| `update(one_off_product_id, body)` | `PATCH /one-off-products/{oneOffProductId}` | `OneOffProduct` |
|
|
34
|
+
| `archive(one_off_product_id)` | `POST /one-off-products/{oneOffProductId}/archive` | `None` |
|
|
35
|
+
| `unarchive(one_off_product_id)` | `DELETE /one-off-products/{oneOffProductId}/archive` | `OneOffProduct` |
|
|
36
|
+
|
|
37
|
+
## `subscription_plans`
|
|
38
|
+
|
|
39
|
+
| Method | HTTP endpoint | Returns |
|
|
40
|
+
| --- | --- | --- |
|
|
41
|
+
| `list(params=None)` | `GET /subscription-plans` | `Page[SubscriptionPlan]` |
|
|
42
|
+
| `create(body)` | `POST /subscription-plans` | `SubscriptionPlan` |
|
|
43
|
+
| `get(subscription_plan_id)` | `GET /subscription-plans/{subscriptionPlanId}` | `SubscriptionPlan` |
|
|
44
|
+
| `update(subscription_plan_id, body)` | `PATCH /subscription-plans/{subscriptionPlanId}` | `SubscriptionPlan` |
|
|
45
|
+
| `archive(subscription_plan_id)` | `POST /subscription-plans/{subscriptionPlanId}/archive` | `None` |
|
|
46
|
+
| `unarchive(subscription_plan_id)` | `DELETE /subscription-plans/{subscriptionPlanId}/archive` | `SubscriptionPlan` |
|
|
47
|
+
|
|
48
|
+
## `orders`
|
|
49
|
+
|
|
50
|
+
| Method | HTTP endpoint | Returns |
|
|
51
|
+
| --- | --- | --- |
|
|
52
|
+
| `list(params=None)` | `GET /orders` | `Page[Order]` |
|
|
53
|
+
| `get(order_id)` | `GET /orders/{orderId}` | `Order` |
|
|
54
|
+
| `create_invoice_update_link(order_id)` | `POST /orders/{orderId}/invoice-update-link` | `InvoiceUpdateLink` |
|
|
55
|
+
| `list_refunds(order_id, params=None)` | `GET /orders/{orderId}/refunds` | `Page[Refund]` |
|
|
56
|
+
| `create_refund(order_id, body)` | `POST /orders/{orderId}/refunds` | `Refund` |
|
|
57
|
+
| `create_full_refund(order_id, body)` | `POST /orders/{orderId}/refunds/full` | `Refund` |
|
|
58
|
+
| `get_refund(order_id, refund_id)` | `GET /orders/{orderId}/refunds/{refundId}` | `Refund` |
|
|
59
|
+
| `cancel_refund(order_id, refund_id)` | `DELETE /orders/{orderId}/refunds/{refundId}` | `None` |
|
|
60
|
+
| `list_chargebacks(order_id, params=None)` | `GET /orders/{orderId}/chargebacks` | `Page[Chargeback]` |
|
|
61
|
+
| `get_chargeback(order_id, chargeback_id)` | `GET /orders/{orderId}/chargebacks/{chargebackId}` | `Chargeback` |
|
|
62
|
+
|
|
63
|
+
## `refunds`
|
|
64
|
+
|
|
65
|
+
| Method | HTTP endpoint | Returns |
|
|
66
|
+
| --- | --- | --- |
|
|
67
|
+
| `list(params=None)` | `GET /refunds` | `Page[Refund]` |
|
|
68
|
+
| `get(refund_id)` | `GET /refunds/{refundId}` | `Refund` |
|
|
69
|
+
|
|
70
|
+
## `chargebacks`
|
|
71
|
+
|
|
72
|
+
| Method | HTTP endpoint | Returns |
|
|
73
|
+
| --- | --- | --- |
|
|
74
|
+
| `list(params=None)` | `GET /chargebacks` | `Page[Chargeback]` |
|
|
75
|
+
| `get(chargeback_id)` | `GET /chargebacks/{chargebackId}` | `Chargeback` |
|
|
76
|
+
|
|
77
|
+
## `webhook_events`
|
|
78
|
+
|
|
79
|
+
| Method | HTTP endpoint | Returns |
|
|
80
|
+
| --- | --- | --- |
|
|
81
|
+
| `get(event_id)` | `GET /webhook-events/{eventId}` | `WebhookEvent` |
|
|
82
|
+
|
|
83
|
+
## `webhook_endpoints`
|
|
84
|
+
|
|
85
|
+
| Method | HTTP endpoint | Returns |
|
|
86
|
+
| --- | --- | --- |
|
|
87
|
+
| `list(params=None)` | `GET /webhook-endpoints` | `Page[WebhookEndpoint]` |
|
|
88
|
+
| `create(body)` | `POST /webhook-endpoints` | `WebhookEndpoint` |
|
|
89
|
+
| `get(webhook_endpoint_id)` | `GET /webhook-endpoints/{webhookEndpointId}` | `WebhookEndpoint` |
|
|
90
|
+
| `update(webhook_endpoint_id, body)` | `PATCH /webhook-endpoints/{webhookEndpointId}` | `WebhookEndpoint` |
|
|
91
|
+
| `delete(webhook_endpoint_id)` | `DELETE /webhook-endpoints/{webhookEndpointId}` | `None` |
|
|
92
|
+
|
|
93
|
+
## `subscriptions`
|
|
94
|
+
|
|
95
|
+
| Method | HTTP endpoint | Returns |
|
|
96
|
+
| --- | --- | --- |
|
|
97
|
+
| `list(params=None)` | `GET /subscriptions` | `Page[Subscription]` |
|
|
98
|
+
| `get(subscription_id)` | `GET /subscriptions/{subscriptionId}` | `Subscription` |
|
|
99
|
+
| `update(subscription_id, body)` | `PATCH /subscriptions/{subscriptionId}` | `Subscription` |
|
|
100
|
+
| `cancel(subscription_id, params=None)` | `DELETE /subscriptions/{subscriptionId}` | `None` |
|
|
101
|
+
| `create_billing_update_link(subscription_id, body)` | `POST /subscriptions/{subscriptionId}/billing-update-link` | `CreateSubscriptionBillingUpdateLinkResponse` |
|
|
102
|
+
| `resume(subscription_id)` | `POST /subscriptions/{subscriptionId}/resume` | `Subscription` |
|
|
103
|
+
|
|
104
|
+
## `test_helpers`
|
|
105
|
+
|
|
106
|
+
| Method | HTTP endpoint | Returns |
|
|
107
|
+
| --- | --- | --- |
|
|
108
|
+
| `fast_forward_renewal(subscription_id, body)` | `POST /test-helpers/subscriptions/{subscriptionId}/fast-forward-renewal` | `Subscription` |
|
|
109
|
+
| `simulate_order_payment(order_id, body)` | `POST /test-helpers/orders/{orderId}/simulate-payment` | `Order` |
|
|
110
|
+
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Chargebacks
|
|
2
|
+
|
|
3
|
+
Read disputes via `client.chargebacks.list`, `list_all`, and `get`, or the
|
|
4
|
+
order-scoped `orders.list_chargebacks`, `list_all_chargebacks`, and `get_chargeback`.
|
|
5
|
+
All are read-only. Preserve the signed financial snapshot for accounting.
|
|
6
|
+
|
|
7
|
+
`order.chargeback_received` and `order.chargeback_reversed` webhook deliveries
|
|
8
|
+
carry a Chargeback in `object` even though `entity_type` is `order`.
|
|
9
|
+
Use `object.resource`, not the envelope entity type, to inspect the resource.
|
|
10
|
+
|
|
11
|
+
See [method inventory](API.md#chargebacks) and [chargeback fields](https://docs.vatly.com/api-reference/chargebacks).
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Checkouts
|
|
2
|
+
|
|
3
|
+
Use `client.checkouts.create(CreateCheckoutRequest(...))` to create a hosted payment
|
|
4
|
+
page. Redirect to `checkout.links.checkout_url.href`. Products use Vatly catalogue
|
|
5
|
+
IDs and quantities. Both success/cancellation redirect URLs are required. Use
|
|
6
|
+
`customer_id` for an existing authorized customer, or omit it for an anonymous flow.
|
|
7
|
+
|
|
8
|
+
Read one checkout with `get(id)`, one page with `list(params)`, or iterate with
|
|
9
|
+
`list_all(params)`. Async clients use `await` and `async for` respectively.
|
|
10
|
+
Reuse a business-operation idempotency key across caller retries. Confirm payment
|
|
11
|
+
through signed webhooks, not a browser redirect. A checkout's `order_id` links to
|
|
12
|
+
the order; the checkout itself intentionally has no products array.
|
|
13
|
+
|
|
14
|
+
See [method inventory](API.md#checkouts) and [production checkout fields](https://docs.vatly.com/api-reference/checkouts).
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Customers
|
|
2
|
+
|
|
3
|
+
`client.customers` provides `create`, `get`, `update`, `list`, `list_all`,
|
|
4
|
+
`create_portal_session`, `list_subscriptions`, `list_all_subscriptions`, and
|
|
5
|
+
`get_subscription`. Filter by email via `ListCustomersParams(email=...)` or
|
|
6
|
+
`{"email": "person@example.com"}`. Email identity is storefront- and mode-scoped.
|
|
7
|
+
|
|
8
|
+
`update` changes identity name/email, not billing-address details. Portal sessions
|
|
9
|
+
are short-lived and single-use: redirect to the returned `url`; do not store it as
|
|
10
|
+
a permanent link. Pass `{}` for an empty portal-session request body.
|
|
11
|
+
|
|
12
|
+
See [method inventory](API.md#customers) and [customer fields](https://docs.vatly.com/api-reference/customers).
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Development and release
|
|
2
|
+
|
|
3
|
+
## Sources
|
|
4
|
+
|
|
5
|
+
`openapi.yaml` was fetched on 2026-11-09 from https://docs.vatly.com/openapi.yaml,
|
|
6
|
+
byte-identical to `vatly/vatly-docs/public/openapi.yaml` at
|
|
7
|
+
`7e71ade32d311b10f79e10c15d0ac03ae32bde97`.
|
|
8
|
+
SHA-256: `69640ac82bbba8d101e6aa882facd5f13aa5cf67f38508e5cea18abef03c4940`.
|
|
9
|
+
The spec's generated-at date is not the retrieval date.
|
|
10
|
+
JS design reference: `vatly/vatly-api-js` at `8b0b920c29f7e7b30bf71b9e379ec0a8a1a22028`.
|
|
11
|
+
|
|
12
|
+
To update, deliberately replace the pinned spec from that production URL, update
|
|
13
|
+
this provenance, run `uv run python scripts/generate.py`, inspect the diff, and
|
|
14
|
+
run all README checks. CI checks regeneration offline. Do not edit generated files.
|
|
15
|
+
|
|
16
|
+
Output objects preserve extra fields. URLs/emails remain strings. Input enums
|
|
17
|
+
and constraints are generated from the spec. Two observed production differences
|
|
18
|
+
are handled explicitly: empty metadata `[]` normalizes to `{}`, and the output
|
|
19
|
+
OneOffProduct.productType is an open string (production returns `digital_download`).
|
|
20
|
+
The checked-in spec is not patched. Response validation failures surface as
|
|
21
|
+
`VatlyApiError` with the Pydantic cause; response bodies may contain personal data.
|
|
22
|
+
Do not log them indiscriminately.
|
|
23
|
+
|
|
24
|
+
## Documentation sync
|
|
25
|
+
|
|
26
|
+
`docs/README.md`, `API.md`, and the resource guides mirror the JS SDK's layout.
|
|
27
|
+
They are self-contained Markdown for a future vatly-docs Python SDK sync; no sync
|
|
28
|
+
job or changes to the docs repository are made here. Keep relative links portable.
|
|
29
|
+
`API.md` is generated; prose guides are maintained manually.
|
|
30
|
+
|
|
31
|
+
## Orb setup
|
|
32
|
+
|
|
33
|
+
`.agents/setup` is executable, provisions uv 0.12.9 and Python 3.12, syncs the
|
|
34
|
+
lockfile, and checks imports. It does not read API credentials or make API calls.
|
|
35
|
+
Run twice to check warm behavior. No services or resume repairs are needed.
|
|
36
|
+
It will become available to project orbs once pushed to the default branch and
|
|
37
|
+
the repository is connected as an Amp project.
|
|
38
|
+
|
|
39
|
+
## Future PyPI release (not executed)
|
|
40
|
+
|
|
41
|
+
The distribution name `vatly-api-python` returned 404 from PyPI's JSON API on 2026-11-12.
|
|
42
|
+
Availability is not a reservation or guarantee of PyPI name approval.
|
|
43
|
+
|
|
44
|
+
1. Push reviewed code to the private `vatly/vatly-api-python` repository's main branch.
|
|
45
|
+
2. Configure a PyPI pending trusted publisher for project `vatly-api-python`, owner `vatly`,
|
|
46
|
+
repository `vatly-api-python`, workflow `release.yml`; leave the environment field blank.
|
|
47
|
+
No GitHub environment or PyPI token is needed.
|
|
48
|
+
3. Update both package-version sources and changelog, run CI, and only with approval
|
|
49
|
+
create a `v0.1.0a2` tag and publish its GitHub release (mark it a prerelease).
|
|
50
|
+
4. The release workflow validates/builds first, then exchanges GitHub OIDC for PyPI
|
|
51
|
+
credentials. Anyone able to publish a matching GitHub release can initiate publication;
|
|
52
|
+
restrict repository release access accordingly. Publish the SDK before Fluent.
|
|
53
|
+
|
|
54
|
+
**A private GitHub repository does not make PyPI artifacts private.** Publishing
|
|
55
|
+
uploads the wheel and sdist publicly, including their source and packaged docs.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# One-off products
|
|
2
|
+
|
|
3
|
+
`client.one_off_products` provides `list`, `list_all`, `get`, `create`, `update`,
|
|
4
|
+
`archive`, and `unarchive`. Use `CreateOneOffProductRequest` and
|
|
5
|
+
`UpdateOneOffProductRequest` from `vatly.models`. `archive` returns `None`;
|
|
6
|
+
`unarchive` returns the resource. Mode and approval behavior follow the API key.
|
|
7
|
+
|
|
8
|
+
The output `product_type` preserves catalogue strings not yet listed in the
|
|
9
|
+
production spec (including observed `digital_download`). Request validation still
|
|
10
|
+
follows the production request schema. Unknown response fields are preserved.
|
|
11
|
+
|
|
12
|
+
See [method inventory](API.md#one_off_products) and [product fields](https://docs.vatly.com/api-reference/one-off-products).
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Orders
|
|
2
|
+
|
|
3
|
+
`client.orders` provides read/pagination plus nested refunds and chargebacks.
|
|
4
|
+
`create_invoice_update_link(order_id)` returns a hosted billing-details update
|
|
5
|
+
link. Financial money values remain decimal strings. Tax and metadata fields are
|
|
6
|
+
retained; do not reconstruct invoices from rounded float calculations.
|
|
7
|
+
|
|
8
|
+
An order line's `product_type` selects what `product_id` refers to. Readers must
|
|
9
|
+
support historical `subscription_plan` lines (plan IDs) and `subscription` lines
|
|
10
|
+
(subscription IDs). Pending core PR #2910 also stamps initial checkout lines with
|
|
11
|
+
the intended subscription ID. An intended ID may temporarily 404 or never resolve
|
|
12
|
+
for a skipped trial start: use bounded recovery and inspect status, not ID presence,
|
|
13
|
+
before granting access. One-off lines are unaffected.
|
|
14
|
+
|
|
15
|
+
See [method inventory](API.md#orders) and [order fields](https://docs.vatly.com/api-reference/orders).
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Getting started
|
|
2
|
+
|
|
3
|
+
> **Alpha — under active development. Every alpha release may include breaking changes. Pin exact versions during alpha.**
|
|
4
|
+
|
|
5
|
+
Use `vatly-api-python==0.1.0a2` (once published), imported as `vatly`. Requires Python 3.10+.
|
|
6
|
+
Create `VatlyClient` inside `with`, or `AsyncVatlyClient` inside `async with`, to
|
|
7
|
+
close the connection pool. Both accept an explicit API key or `VATLY_API_KEY`.
|
|
8
|
+
Test and live modes use the same API host; keys select the mode.
|
|
9
|
+
|
|
10
|
+
See the [SDK README](../README.md) for runnable initialization, checkout,
|
|
11
|
+
pagination, retries, configuration, errors and development commands.
|
|
12
|
+
|
|
13
|
+
| Guide | Purpose |
|
|
14
|
+
| --- | --- |
|
|
15
|
+
| [API](API.md) | Every method, endpoint and response |
|
|
16
|
+
| [Checkouts](Checkouts.md) | Hosted payment pages |
|
|
17
|
+
| [Customers](Customers.md) | Customer identity and portal sessions |
|
|
18
|
+
| [Subscriptions](Subscriptions.md) | Recurring billing |
|
|
19
|
+
| [Subscription Plans](SubscriptionPlans.md) | Subscription catalogue |
|
|
20
|
+
| [One-Off Products](OneOffProducts.md) | One-time catalogue |
|
|
21
|
+
| [Orders](Orders.md) | Transactions and nested financial operations |
|
|
22
|
+
| [Refunds](Refunds.md) | Reversals |
|
|
23
|
+
| [Chargebacks](Chargebacks.md) | Disputes |
|
|
24
|
+
| [Webhooks](Webhooks.md) | Signatures, events and endpoints |
|
|
25
|
+
| [Development](Development.md) | Spec/docs sync, setup, release |
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Refunds
|
|
2
|
+
|
|
3
|
+
Read refunds via `client.refunds.list`, `list_all`, and `get`. Order-scoped
|
|
4
|
+
operations are `orders.list_refunds`, `list_all_refunds`, `get_refund`,
|
|
5
|
+
`create_refund`, `create_full_refund`, and `cancel_refund`.
|
|
6
|
+
|
|
7
|
+
Partial refunds use `CreateRefundRequest` with order lines and amounts; full
|
|
8
|
+
refunds accept `CreateFullRefundRequest` or `{}`. A cancellation returns `None`.
|
|
9
|
+
Use stable idempotency keys for business operations; a timeout does not prove
|
|
10
|
+
that a refund failed. Read/reconcile before issuing another mutation when the
|
|
11
|
+
server reports an unknown outcome. Webhooks confirm completion/failure/cancellation.
|
|
12
|
+
|
|
13
|
+
See [method inventory](API.md#refunds) and [refund fields](https://docs.vatly.com/api-reference/refunds).
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Subscription plans
|
|
2
|
+
|
|
3
|
+
`client.subscription_plans` provides `list`, `list_all`, `get`, `create`, `update`,
|
|
4
|
+
`archive`, and `unarchive`. Use `CreateSubscriptionPlanRequest` and
|
|
5
|
+
`UpdateSubscriptionPlanRequest` from `vatly.models`. Money is a decimal string
|
|
6
|
+
plus currency; preserve both. Archiving returns `None`, unarchiving the plan.
|
|
7
|
+
|
|
8
|
+
Test-mode catalogue changes are auto-approved; live changes may await approval.
|
|
9
|
+
Archive prevents new sales, not existing subscription renewals. Select
|
|
10
|
+
`includeArchived` in dictionary filters when needed, or the corresponding model's
|
|
11
|
+
`include_archived` field.
|
|
12
|
+
|
|
13
|
+
See [method inventory](API.md#subscription_plans) and [plan fields](https://docs.vatly.com/api-reference/subscription-plans).
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Subscriptions
|
|
2
|
+
|
|
3
|
+
Use `client.subscriptions.get(id)`, `list(params)`, or `list_all(params)` to read.
|
|
4
|
+
Create a subscription through a checkout containing a subscription-plan ID.
|
|
5
|
+
|
|
6
|
+
`update(id, body)` changes plan, price or quantity. At least one is required;
|
|
7
|
+
`trial_until` cannot be sent alone. By default updates are scheduled; request
|
|
8
|
+
`apply_immediately` and the relevant invoice/proration flags deliberately.
|
|
9
|
+
`cancel(id, {"immediately": True})` encodes the boolean as `1`, as the server expects.
|
|
10
|
+
`resume(id)` is only valid during the cancellation grace period.
|
|
11
|
+
|
|
12
|
+
`create_billing_update_link(id, body)` returns a hosted link for billing details;
|
|
13
|
+
both redirect URLs are required. Check remote status before granting entitlements.
|
|
14
|
+
The signed subscription events carry full snapshots, including scheduled changes,
|
|
15
|
+
mandate, cancellation reason, and renewal periods.
|
|
16
|
+
|
|
17
|
+
Test-only helpers live under `client.test_helpers`: `fast_forward_renewal` and
|
|
18
|
+
`simulate_order_payment`. They mutate remote data and must never run in CI/live
|
|
19
|
+
verification without explicit intent. This repository's tests use mocks only.
|
|
20
|
+
|
|
21
|
+
See [method inventory](API.md#subscriptions) and [subscription fields](https://docs.vatly.com/api-reference/subscriptions).
|