agentref 1.0.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.
@@ -0,0 +1,24 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ ci:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ python-version: ['3.9', '3.11', '3.12']
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+ - uses: actions/setup-python@v5
18
+ with:
19
+ python-version: ${{ matrix.python-version }}
20
+ - run: python -m pip install --upgrade pip
21
+ - run: pip install -e .[dev]
22
+ - run: mypy agentref
23
+ - run: pytest -v
24
+ - run: python -m build
@@ -0,0 +1,24 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ push:
5
+ tags: ['v*']
6
+
7
+ jobs:
8
+ publish:
9
+ runs-on: ubuntu-latest
10
+ environment: pypi
11
+ permissions:
12
+ id-token: write
13
+ contents: read
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+ - uses: actions/setup-python@v5
17
+ with:
18
+ python-version: '3.12'
19
+ - run: python -m pip install --upgrade pip
20
+ - run: pip install -e .[dev]
21
+ - run: mypy agentref
22
+ - run: pytest -v
23
+ - run: python -m build
24
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,8 @@
1
+ __pycache__/
2
+ *.pyc
3
+ .venv/
4
+ dist/
5
+ build/
6
+ *.egg-info/
7
+ .mypy_cache/
8
+ .pytest_cache/
@@ -0,0 +1,8 @@
1
+ # Changelog
2
+
3
+ ## 1.0.0
4
+
5
+ - Initial release.
6
+ - Sync + async clients.
7
+ - Typed resources for merchant-facing REST API v1 surfaces.
8
+ - Retry/idempotency safeguards aligned with API safety model.
@@ -0,0 +1,101 @@
1
+ Metadata-Version: 2.4
2
+ Name: agentref
3
+ Version: 1.0.0
4
+ Summary: Official Python SDK for the AgentRef Affiliate API
5
+ Author-email: AgentRef <hi@agentref.dev>
6
+ License: MIT
7
+ Requires-Python: >=3.9
8
+ Requires-Dist: httpx>=0.27.0
9
+ Requires-Dist: pydantic>=2.0.0
10
+ Provides-Extra: dev
11
+ Requires-Dist: build>=1.2.0; extra == 'dev'
12
+ Requires-Dist: mypy>=1.9.0; extra == 'dev'
13
+ Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
14
+ Requires-Dist: pytest>=8.0.0; extra == 'dev'
15
+ Requires-Dist: respx>=0.21.0; extra == 'dev'
16
+ Description-Content-Type: text/markdown
17
+
18
+ # AgentRef Python SDK
19
+
20
+ Official Python SDK for the AgentRef REST API v1.
21
+
22
+ ## Install
23
+
24
+ ```bash
25
+ pip install agentref
26
+ ```
27
+
28
+ ## Quickstart
29
+
30
+ ```python
31
+ from agentref import AgentRef
32
+
33
+ client = AgentRef(api_key="ak_live_...")
34
+ programs = client.programs.list()
35
+ print(programs.meta.request_id)
36
+ ```
37
+
38
+ ## Async quickstart
39
+
40
+ ```python
41
+ from agentref import AsyncAgentRef
42
+
43
+ async with AsyncAgentRef(api_key="ak_live_...") as client:
44
+ programs = await client.programs.list()
45
+ ```
46
+
47
+ ## Authentication
48
+
49
+ - Uses `Authorization: Bearer <key>`.
50
+ - Supports `ak_live_*`, `ak_aff_*`, `ak_onb_*`.
51
+ - Provide `api_key` directly or set `AGENTREF_API_KEY`.
52
+
53
+ ## Resources
54
+
55
+ - `client.programs`: `list`, `list_all`, `get`, `create`, `update`, `delete`, `stats`, `list_affiliates`, `list_coupons`, `create_coupon`, `create_invite`
56
+ - `client.affiliates`: `list`, `get`, `approve`, `block`, `unblock`
57
+ - `client.conversions`: `list`, `stats`, `recent`
58
+ - `client.payouts`: `list`, `list_pending`, `stats`
59
+ - `client.flags`: `list`, `stats`, `resolve`
60
+ - `client.billing`: `current`, `tiers`, `subscribe`
61
+ - `client.merchant`: `get`, `domain_status`
62
+
63
+ ## Pagination
64
+
65
+ List endpoints return `PaginatedResponse[T]` with:
66
+
67
+ - `meta.total`
68
+ - `meta.page`
69
+ - `meta.page_size`
70
+ - `meta.has_more`
71
+ - `meta.next_cursor`
72
+ - `meta.request_id`
73
+
74
+ Auto-pagination (`list_all`) stops on `has_more is False`.
75
+
76
+ ## Idempotency and retry behavior
77
+
78
+ - GET/HEAD: auto-retry on 429/5xx.
79
+ - POST: auto-retry only when `idempotency_key` is provided.
80
+ - PATCH/DELETE: never auto-retry.
81
+ - `Idempotency-Key` header is sent only for POST requests.
82
+
83
+ ## Error handling
84
+
85
+ ```python
86
+ from agentref import AgentRef
87
+ from agentref.errors import ForbiddenError, NotFoundError, RateLimitError, AgentRefError
88
+
89
+ client = AgentRef(api_key="ak_live_...")
90
+
91
+ try:
92
+ client.programs.get("missing-id")
93
+ except ForbiddenError as e:
94
+ print(e.code, e.request_id)
95
+ except NotFoundError as e:
96
+ print(e.request_id)
97
+ except RateLimitError as e:
98
+ print(e.retry_after)
99
+ except AgentRefError as e:
100
+ print(e.status)
101
+ ```
@@ -0,0 +1,84 @@
1
+ # AgentRef Python SDK
2
+
3
+ Official Python SDK for the AgentRef REST API v1.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pip install agentref
9
+ ```
10
+
11
+ ## Quickstart
12
+
13
+ ```python
14
+ from agentref import AgentRef
15
+
16
+ client = AgentRef(api_key="ak_live_...")
17
+ programs = client.programs.list()
18
+ print(programs.meta.request_id)
19
+ ```
20
+
21
+ ## Async quickstart
22
+
23
+ ```python
24
+ from agentref import AsyncAgentRef
25
+
26
+ async with AsyncAgentRef(api_key="ak_live_...") as client:
27
+ programs = await client.programs.list()
28
+ ```
29
+
30
+ ## Authentication
31
+
32
+ - Uses `Authorization: Bearer <key>`.
33
+ - Supports `ak_live_*`, `ak_aff_*`, `ak_onb_*`.
34
+ - Provide `api_key` directly or set `AGENTREF_API_KEY`.
35
+
36
+ ## Resources
37
+
38
+ - `client.programs`: `list`, `list_all`, `get`, `create`, `update`, `delete`, `stats`, `list_affiliates`, `list_coupons`, `create_coupon`, `create_invite`
39
+ - `client.affiliates`: `list`, `get`, `approve`, `block`, `unblock`
40
+ - `client.conversions`: `list`, `stats`, `recent`
41
+ - `client.payouts`: `list`, `list_pending`, `stats`
42
+ - `client.flags`: `list`, `stats`, `resolve`
43
+ - `client.billing`: `current`, `tiers`, `subscribe`
44
+ - `client.merchant`: `get`, `domain_status`
45
+
46
+ ## Pagination
47
+
48
+ List endpoints return `PaginatedResponse[T]` with:
49
+
50
+ - `meta.total`
51
+ - `meta.page`
52
+ - `meta.page_size`
53
+ - `meta.has_more`
54
+ - `meta.next_cursor`
55
+ - `meta.request_id`
56
+
57
+ Auto-pagination (`list_all`) stops on `has_more is False`.
58
+
59
+ ## Idempotency and retry behavior
60
+
61
+ - GET/HEAD: auto-retry on 429/5xx.
62
+ - POST: auto-retry only when `idempotency_key` is provided.
63
+ - PATCH/DELETE: never auto-retry.
64
+ - `Idempotency-Key` header is sent only for POST requests.
65
+
66
+ ## Error handling
67
+
68
+ ```python
69
+ from agentref import AgentRef
70
+ from agentref.errors import ForbiddenError, NotFoundError, RateLimitError, AgentRefError
71
+
72
+ client = AgentRef(api_key="ak_live_...")
73
+
74
+ try:
75
+ client.programs.get("missing-id")
76
+ except ForbiddenError as e:
77
+ print(e.code, e.request_id)
78
+ except NotFoundError as e:
79
+ print(e.request_id)
80
+ except RateLimitError as e:
81
+ print(e.retry_after)
82
+ except AgentRefError as e:
83
+ print(e.status)
84
+ ```
@@ -0,0 +1,24 @@
1
+ from .client import AgentRef, AsyncAgentRef
2
+ from .errors import (
3
+ AgentRefError,
4
+ AuthError,
5
+ ConflictError,
6
+ ForbiddenError,
7
+ NotFoundError,
8
+ RateLimitError,
9
+ ServerError,
10
+ ValidationError,
11
+ )
12
+
13
+ __all__ = [
14
+ "AgentRef",
15
+ "AsyncAgentRef",
16
+ "AgentRefError",
17
+ "AuthError",
18
+ "ForbiddenError",
19
+ "ValidationError",
20
+ "NotFoundError",
21
+ "ConflictError",
22
+ "RateLimitError",
23
+ "ServerError",
24
+ ]
@@ -0,0 +1,252 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ import importlib.metadata
5
+ import os
6
+ import time
7
+ from email.utils import parsedate_to_datetime
8
+ from typing import Any, Dict, Mapping, Optional, Set, cast
9
+
10
+ import httpx
11
+
12
+ from .errors import (
13
+ AgentRefError,
14
+ AuthError,
15
+ ConflictError,
16
+ ForbiddenError,
17
+ NotFoundError,
18
+ RateLimitError,
19
+ ServerError,
20
+ ValidationError,
21
+ )
22
+
23
+ _SAFE_METHODS: Set[str] = {"GET", "HEAD"}
24
+ _DEFAULT_BASE_URL = "https://www.agentref.dev/api/v1"
25
+ _DEFAULT_TIMEOUT = 30.0
26
+ _DEFAULT_MAX_RETRIES = 2
27
+
28
+
29
+ def _sdk_version() -> str:
30
+ try:
31
+ return importlib.metadata.version("agentref")
32
+ except importlib.metadata.PackageNotFoundError:
33
+ return "unknown"
34
+
35
+
36
+ def _parse_retry_after_seconds(value: Optional[str]) -> int:
37
+ if not value:
38
+ return 60
39
+
40
+ try:
41
+ parsed = float(value)
42
+ if parsed >= 0:
43
+ return int(parsed) if parsed.is_integer() else int(parsed) + 1
44
+ except ValueError:
45
+ pass
46
+
47
+ try:
48
+ parsed_date = parsedate_to_datetime(value)
49
+ delta = parsed_date.timestamp() - time.time()
50
+ if delta <= 0:
51
+ return 0
52
+ return int(delta) if float(delta).is_integer() else int(delta) + 1
53
+ except (TypeError, ValueError, OverflowError):
54
+ return 60
55
+
56
+
57
+ def _can_retry(method: str, idempotency_key: Optional[str]) -> bool:
58
+ upper = method.upper()
59
+ return upper in _SAFE_METHODS or (upper == "POST" and idempotency_key is not None)
60
+
61
+
62
+ def _json_object(response: httpx.Response) -> Dict[str, Any]:
63
+ try:
64
+ payload = response.json()
65
+ except ValueError:
66
+ return {}
67
+
68
+ if isinstance(payload, dict):
69
+ return cast(Dict[str, Any], payload)
70
+
71
+ return {}
72
+
73
+
74
+ class _BaseHttpClient:
75
+ def __init__(
76
+ self,
77
+ *,
78
+ api_key: Optional[str] = None,
79
+ base_url: str = _DEFAULT_BASE_URL,
80
+ timeout: float = _DEFAULT_TIMEOUT,
81
+ max_retries: int = _DEFAULT_MAX_RETRIES,
82
+ ) -> None:
83
+ resolved_key = api_key or os.environ.get("AGENTREF_API_KEY")
84
+ if not resolved_key:
85
+ raise ValueError(
86
+ "[AgentRef] API key is required. Pass it as api_key or set AGENTREF_API_KEY environment variable."
87
+ )
88
+
89
+ self._api_key = resolved_key
90
+ self._base_url = base_url.rstrip("/")
91
+ self._timeout = timeout
92
+ self._max_retries = max_retries
93
+ self._user_agent = f"agentref-python/{_sdk_version()}"
94
+
95
+ def _headers(self, method: str, idempotency_key: Optional[str]) -> Dict[str, str]:
96
+ headers: Dict[str, str] = {
97
+ "Authorization": f"Bearer {self._api_key}",
98
+ "Content-Type": "application/json",
99
+ "User-Agent": self._user_agent,
100
+ }
101
+
102
+ if method.upper() == "POST" and idempotency_key is not None:
103
+ headers["Idempotency-Key"] = idempotency_key
104
+
105
+ return headers
106
+
107
+ def _parse_error(self, response: httpx.Response) -> AgentRefError:
108
+ payload = _json_object(response)
109
+ raw_error = payload.get("error")
110
+ raw_meta = payload.get("meta")
111
+
112
+ error = raw_error if isinstance(raw_error, dict) else {}
113
+ meta = raw_meta if isinstance(raw_meta, dict) else {}
114
+
115
+ code = cast(str, error.get("code", "UNKNOWN_ERROR"))
116
+ message = cast(str, error.get("message", response.reason_phrase))
117
+ request_id = cast(str, meta.get("requestId", ""))
118
+ details = error.get("details")
119
+
120
+ if response.status_code == 400:
121
+ return ValidationError(message, code, request_id, details)
122
+ if response.status_code == 401:
123
+ return AuthError(message, code, request_id)
124
+ if response.status_code == 403:
125
+ return ForbiddenError(message, code, request_id)
126
+ if response.status_code == 404:
127
+ return NotFoundError(message, code, request_id)
128
+ if response.status_code == 409:
129
+ return ConflictError(message, code, request_id)
130
+ if response.status_code == 429:
131
+ retry_after = _parse_retry_after_seconds(response.headers.get("Retry-After"))
132
+ return RateLimitError(message, code, request_id, retry_after)
133
+
134
+ return ServerError(message, code, response.status_code, request_id)
135
+
136
+ @staticmethod
137
+ def _is_retryable(status: int) -> bool:
138
+ return status == 429 or status >= 500
139
+
140
+ @staticmethod
141
+ def _backoff_seconds(attempt: int) -> float:
142
+ return float(0.5 * (2 ** attempt))
143
+
144
+
145
+ class SyncHttpClient(_BaseHttpClient):
146
+ def __init__(self, **kwargs: Any) -> None:
147
+ super().__init__(**kwargs)
148
+ self._client = httpx.Client(base_url=self._base_url, timeout=self._timeout)
149
+
150
+ def close(self) -> None:
151
+ self._client.close()
152
+
153
+ def request(
154
+ self,
155
+ method: str,
156
+ path: str,
157
+ *,
158
+ params: Optional[Mapping[str, Any]] = None,
159
+ json: Optional[Dict[str, Any]] = None,
160
+ idempotency_key: Optional[str] = None,
161
+ ) -> Dict[str, Any]:
162
+ can_retry = _can_retry(method, idempotency_key)
163
+ attempts = self._max_retries + 1 if can_retry else 1
164
+
165
+ for attempt in range(attempts):
166
+ try:
167
+ response = self._client.request(
168
+ method,
169
+ path,
170
+ params={k: v for k, v in (params or {}).items() if v is not None},
171
+ json=json,
172
+ headers=self._headers(method, idempotency_key),
173
+ )
174
+ except httpx.HTTPError:
175
+ if can_retry and attempt < attempts - 1:
176
+ time.sleep(self._backoff_seconds(attempt))
177
+ continue
178
+ raise
179
+
180
+ if response.is_error:
181
+ parsed = self._parse_error(response)
182
+ if can_retry and self._is_retryable(response.status_code) and attempt < attempts - 1:
183
+ delay = (
184
+ float(_parse_retry_after_seconds(response.headers.get("Retry-After")))
185
+ if response.status_code == 429
186
+ else self._backoff_seconds(attempt)
187
+ )
188
+ time.sleep(delay)
189
+ continue
190
+ raise parsed
191
+
192
+ return _json_object(response)
193
+
194
+ raise ServerError("Request failed after retries", "REQUEST_RETRY_EXHAUSTED", 500, "")
195
+
196
+
197
+ class AsyncHttpClient(_BaseHttpClient):
198
+ def __init__(self, **kwargs: Any) -> None:
199
+ super().__init__(**kwargs)
200
+ self._client = httpx.AsyncClient(base_url=self._base_url, timeout=self._timeout)
201
+
202
+ async def __aenter__(self) -> "AsyncHttpClient":
203
+ return self
204
+
205
+ async def __aexit__(self, *args: Any) -> None:
206
+ await self._client.aclose()
207
+
208
+ async def aclose(self) -> None:
209
+ await self._client.aclose()
210
+
211
+ async def request(
212
+ self,
213
+ method: str,
214
+ path: str,
215
+ *,
216
+ params: Optional[Mapping[str, Any]] = None,
217
+ json: Optional[Dict[str, Any]] = None,
218
+ idempotency_key: Optional[str] = None,
219
+ ) -> Dict[str, Any]:
220
+ can_retry = _can_retry(method, idempotency_key)
221
+ attempts = self._max_retries + 1 if can_retry else 1
222
+
223
+ for attempt in range(attempts):
224
+ try:
225
+ response = await self._client.request(
226
+ method,
227
+ path,
228
+ params={k: v for k, v in (params or {}).items() if v is not None},
229
+ json=json,
230
+ headers=self._headers(method, idempotency_key),
231
+ )
232
+ except httpx.HTTPError:
233
+ if can_retry and attempt < attempts - 1:
234
+ await asyncio.sleep(self._backoff_seconds(attempt))
235
+ continue
236
+ raise
237
+
238
+ if response.is_error:
239
+ parsed = self._parse_error(response)
240
+ if can_retry and self._is_retryable(response.status_code) and attempt < attempts - 1:
241
+ delay = (
242
+ float(_parse_retry_after_seconds(response.headers.get("Retry-After")))
243
+ if response.status_code == 429
244
+ else self._backoff_seconds(attempt)
245
+ )
246
+ await asyncio.sleep(delay)
247
+ continue
248
+ raise parsed
249
+
250
+ return _json_object(response)
251
+
252
+ raise ServerError("Request failed after retries", "REQUEST_RETRY_EXHAUSTED", 500, "")
@@ -0,0 +1,83 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, Optional
4
+
5
+ from ._http import AsyncHttpClient, SyncHttpClient
6
+ from .resources import (
7
+ AffiliatesResource,
8
+ AsyncAffiliatesResource,
9
+ AsyncBillingResource,
10
+ AsyncConversionsResource,
11
+ AsyncFlagsResource,
12
+ AsyncMerchantResource,
13
+ AsyncPayoutsResource,
14
+ AsyncProgramsResource,
15
+ BillingResource,
16
+ ConversionsResource,
17
+ FlagsResource,
18
+ MerchantResource,
19
+ PayoutsResource,
20
+ ProgramsResource,
21
+ )
22
+
23
+
24
+ class AgentRef:
25
+ def __init__(
26
+ self,
27
+ api_key: Optional[str] = None,
28
+ *,
29
+ base_url: str = "https://www.agentref.dev/api/v1",
30
+ timeout: float = 30.0,
31
+ max_retries: int = 2,
32
+ ) -> None:
33
+ self._http = SyncHttpClient(
34
+ api_key=api_key,
35
+ base_url=base_url,
36
+ timeout=timeout,
37
+ max_retries=max_retries,
38
+ )
39
+
40
+ self.programs = ProgramsResource(self._http)
41
+ self.affiliates = AffiliatesResource(self._http)
42
+ self.conversions = ConversionsResource(self._http)
43
+ self.payouts = PayoutsResource(self._http)
44
+ self.flags = FlagsResource(self._http)
45
+ self.billing = BillingResource(self._http)
46
+ self.merchant = MerchantResource(self._http)
47
+
48
+ def close(self) -> None:
49
+ self._http.close()
50
+
51
+
52
+ class AsyncAgentRef:
53
+ """Async variant — use as async context manager for connection pooling."""
54
+
55
+ def __init__(
56
+ self,
57
+ api_key: Optional[str] = None,
58
+ *,
59
+ base_url: str = "https://www.agentref.dev/api/v1",
60
+ timeout: float = 30.0,
61
+ max_retries: int = 2,
62
+ ) -> None:
63
+ self._http = AsyncHttpClient(
64
+ api_key=api_key,
65
+ base_url=base_url,
66
+ timeout=timeout,
67
+ max_retries=max_retries,
68
+ )
69
+
70
+ self.programs = AsyncProgramsResource(self._http)
71
+ self.affiliates = AsyncAffiliatesResource(self._http)
72
+ self.conversions = AsyncConversionsResource(self._http)
73
+ self.payouts = AsyncPayoutsResource(self._http)
74
+ self.flags = AsyncFlagsResource(self._http)
75
+ self.billing = AsyncBillingResource(self._http)
76
+ self.merchant = AsyncMerchantResource(self._http)
77
+
78
+ async def __aenter__(self) -> "AsyncAgentRef":
79
+ await self._http.__aenter__()
80
+ return self
81
+
82
+ async def __aexit__(self, *args: Any) -> None:
83
+ await self._http.__aexit__(*args)
@@ -0,0 +1,62 @@
1
+ from typing import Optional
2
+
3
+
4
+ class AgentRefError(Exception):
5
+ code: str
6
+ status: int
7
+ request_id: str
8
+
9
+ def __init__(self, message: str, code: str, status: int, request_id: str) -> None:
10
+ super().__init__(message)
11
+ self.code = code
12
+ self.status = status
13
+ self.request_id = request_id
14
+
15
+
16
+ class AuthError(AgentRefError):
17
+ def __init__(self, message: str, code: str, request_id: str) -> None:
18
+ super().__init__(message, code, 401, request_id)
19
+
20
+
21
+ class ForbiddenError(AgentRefError):
22
+ """403 — authenticated but not authorized: wrong scope, ownerType, or trust level."""
23
+
24
+ def __init__(self, message: str, code: str, request_id: str) -> None:
25
+ super().__init__(message, code, 403, request_id)
26
+
27
+
28
+ class ValidationError(AgentRefError):
29
+ details: Optional[object]
30
+
31
+ def __init__(
32
+ self,
33
+ message: str,
34
+ code: str,
35
+ request_id: str,
36
+ details: Optional[object] = None,
37
+ ) -> None:
38
+ super().__init__(message, code, 400, request_id)
39
+ self.details = details
40
+
41
+
42
+ class NotFoundError(AgentRefError):
43
+ def __init__(self, message: str, code: str, request_id: str) -> None:
44
+ super().__init__(message, code, 404, request_id)
45
+
46
+
47
+ class ConflictError(AgentRefError):
48
+ def __init__(self, message: str, code: str, request_id: str) -> None:
49
+ super().__init__(message, code, 409, request_id)
50
+
51
+
52
+ class RateLimitError(AgentRefError):
53
+ retry_after: int
54
+
55
+ def __init__(self, message: str, code: str, request_id: str, retry_after: int) -> None:
56
+ super().__init__(message, code, 429, request_id)
57
+ self.retry_after = retry_after
58
+
59
+
60
+ class ServerError(AgentRefError):
61
+ def __init__(self, message: str, code: str, status: int, request_id: str) -> None:
62
+ super().__init__(message, code, status, request_id)
@@ -0,0 +1,24 @@
1
+ from .affiliates import AffiliatesResource, AsyncAffiliatesResource
2
+ from .billing import AsyncBillingResource, BillingResource
3
+ from .conversions import AsyncConversionsResource, ConversionsResource
4
+ from .flags import AsyncFlagsResource, FlagsResource
5
+ from .merchant import AsyncMerchantResource, MerchantResource
6
+ from .payouts import AsyncPayoutsResource, PayoutsResource
7
+ from .programs import AsyncProgramsResource, ProgramsResource
8
+
9
+ __all__ = [
10
+ "ProgramsResource",
11
+ "AffiliatesResource",
12
+ "ConversionsResource",
13
+ "PayoutsResource",
14
+ "FlagsResource",
15
+ "BillingResource",
16
+ "MerchantResource",
17
+ "AsyncProgramsResource",
18
+ "AsyncAffiliatesResource",
19
+ "AsyncConversionsResource",
20
+ "AsyncPayoutsResource",
21
+ "AsyncFlagsResource",
22
+ "AsyncBillingResource",
23
+ "AsyncMerchantResource",
24
+ ]