authpi-admin 0.3.0__tar.gz → 0.8.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.
- {authpi_admin-0.3.0 → authpi_admin-0.8.0}/.gitignore +9 -0
- authpi_admin-0.8.0/CHANGELOG.md +78 -0
- {authpi_admin-0.3.0 → authpi_admin-0.8.0}/PKG-INFO +69 -12
- {authpi_admin-0.3.0 → authpi_admin-0.8.0}/README.md +68 -11
- {authpi_admin-0.3.0 → authpi_admin-0.8.0}/authpi_admin/__init__.py +4 -2
- {authpi_admin-0.3.0 → authpi_admin-0.8.0}/authpi_admin/_version.py +1 -1
- {authpi_admin-0.3.0 → authpi_admin-0.8.0}/authpi_admin/client.py +33 -16
- authpi_admin-0.8.0/authpi_admin/generated/__init__.py +38 -0
- authpi_admin-0.8.0/authpi_admin/generated/models.py +801 -0
- authpi_admin-0.3.0/authpi_admin/generated/resources/api_keys.py → authpi_admin-0.8.0/authpi_admin/generated/resources/account_api_keys.py +94 -13
- authpi_admin-0.8.0/authpi_admin/generated/resources/account_tokens.py +86 -0
- {authpi_admin-0.3.0 → authpi_admin-0.8.0}/authpi_admin/generated/resources/accounts.py +12 -55
- authpi_admin-0.3.0/authpi_admin/generated/resources/verifiers.py → authpi_admin-0.8.0/authpi_admin/generated/resources/agent_verifiers.py +9 -7
- {authpi_admin-0.3.0 → authpi_admin-0.8.0}/authpi_admin/generated/resources/agents.py +14 -9
- {authpi_admin-0.3.0 → authpi_admin-0.8.0}/authpi_admin/generated/resources/approvals.py +6 -4
- {authpi_admin-0.3.0 → authpi_admin-0.8.0}/authpi_admin/generated/resources/auth_methods.py +14 -9
- {authpi_admin-0.3.0 → authpi_admin-0.8.0}/authpi_admin/generated/resources/clients.py +9 -6
- authpi_admin-0.8.0/authpi_admin/generated/resources/credits.py +102 -0
- {authpi_admin-0.3.0 → authpi_admin-0.8.0}/authpi_admin/generated/resources/deliveries.py +3 -2
- {authpi_admin-0.3.0 → authpi_admin-0.8.0}/authpi_admin/generated/resources/domains.py +16 -10
- {authpi_admin-0.3.0 → authpi_admin-0.8.0}/authpi_admin/generated/resources/events.py +57 -4
- {authpi_admin-0.3.0 → authpi_admin-0.8.0}/authpi_admin/generated/resources/groups.py +17 -15
- {authpi_admin-0.3.0 → authpi_admin-0.8.0}/authpi_admin/generated/resources/invitations.py +19 -15
- authpi_admin-0.3.0/authpi_admin/generated/resources/sessions.py → authpi_admin-0.8.0/authpi_admin/generated/resources/issuer_sessions.py +11 -10
- authpi_admin-0.8.0/authpi_admin/generated/resources/issuer_tokens.py +46 -0
- {authpi_admin-0.3.0 → authpi_admin-0.8.0}/authpi_admin/generated/resources/issuers.py +14 -9
- {authpi_admin-0.3.0 → authpi_admin-0.8.0}/authpi_admin/generated/resources/members.py +15 -12
- {authpi_admin-0.3.0 → authpi_admin-0.8.0}/authpi_admin/generated/resources/notes.py +68 -9
- authpi_admin-0.3.0/authpi_admin/generated/resources/trusted_devices.py → authpi_admin-0.8.0/authpi_admin/generated/resources/organization_api_keys.py +25 -31
- {authpi_admin-0.3.0 → authpi_admin-0.8.0}/authpi_admin/generated/resources/organizations.py +13 -73
- authpi_admin-0.8.0/authpi_admin/generated/resources/payment_methods.py +95 -0
- authpi_admin-0.8.0/authpi_admin/generated/resources/sso.py +135 -0
- authpi_admin-0.8.0/authpi_admin/generated/resources/trusted_devices.py +135 -0
- authpi_admin-0.8.0/authpi_admin/generated/resources/user_sessions.py +63 -0
- authpi_admin-0.3.0/authpi_admin/generated/resources/tokens.py → authpi_admin-0.8.0/authpi_admin/generated/resources/user_tokens.py +9 -7
- authpi_admin-0.3.0/authpi_admin/generated/resources/users.py → authpi_admin-0.8.0/authpi_admin/generated/resources/user_verifiers.py +86 -64
- authpi_admin-0.8.0/authpi_admin/generated/resources/users.py +164 -0
- {authpi_admin-0.3.0 → authpi_admin-0.8.0}/authpi_admin/generated/resources/webhooks.py +11 -9
- {authpi_admin-0.3.0 → authpi_admin-0.8.0}/authpi_admin/generated/scopes/agent_scope.py +8 -9
- {authpi_admin-0.3.0 → authpi_admin-0.8.0}/authpi_admin/generated/scopes/issuer_scope.py +19 -39
- authpi_admin-0.8.0/authpi_admin/generated/scopes/organization_scope.py +107 -0
- {authpi_admin-0.3.0 → authpi_admin-0.8.0}/authpi_admin/generated/scopes/user_scope.py +15 -16
- {authpi_admin-0.3.0 → authpi_admin-0.8.0}/authpi_admin/generated/scopes/webhook_scope.py +10 -8
- {authpi_admin-0.3.0 → authpi_admin-0.8.0}/authpi_admin/http_client.py +98 -17
- authpi_admin-0.8.0/authpi_admin/types.py +7 -0
- {authpi_admin-0.3.0 → authpi_admin-0.8.0}/pyproject.toml +1 -1
- authpi_admin-0.8.0/tests/test_client.py +176 -0
- {authpi_admin-0.3.0 → authpi_admin-0.8.0}/tests/test_http_client.py +60 -18
- {authpi_admin-0.3.0 → authpi_admin-0.8.0}/tests/test_integration.py +55 -31
- authpi_admin-0.8.0/tests/test_public_types.py +22 -0
- {authpi_admin-0.3.0 → authpi_admin-0.8.0}/uv.lock +240 -238
- authpi_admin-0.3.0/CHANGELOG.md +0 -25
- authpi_admin-0.3.0/authpi_admin/generated/__init__.py +0 -28
- authpi_admin-0.3.0/authpi_admin/generated/models.py +0 -380
- authpi_admin-0.3.0/tests/test_client.py +0 -90
- {authpi_admin-0.3.0 → authpi_admin-0.8.0}/authpi_admin/errors.py +0 -0
- {authpi_admin-0.3.0 → authpi_admin-0.8.0}/authpi_admin/generated/resources/__init__.py +0 -0
- {authpi_admin-0.3.0 → authpi_admin-0.8.0}/authpi_admin/generated/scopes/__init__.py +0 -0
- {authpi_admin-0.3.0 → authpi_admin-0.8.0}/authpi_admin/pagination.py +0 -0
- {authpi_admin-0.3.0 → authpi_admin-0.8.0}/authpi_admin/py.typed +0 -0
- {authpi_admin-0.3.0 → authpi_admin-0.8.0}/authpi_admin/user_agent.py +0 -0
- {authpi_admin-0.3.0 → authpi_admin-0.8.0}/tests/__init__.py +0 -0
- {authpi_admin-0.3.0 → authpi_admin-0.8.0}/tests/test_errors.py +0 -0
- {authpi_admin-0.3.0 → authpi_admin-0.8.0}/tests/test_pagination.py +0 -0
|
@@ -13,6 +13,7 @@ vite.config.js.timestamp-*
|
|
|
13
13
|
vite.config.ts.timestamp-*
|
|
14
14
|
oracle.sql
|
|
15
15
|
dist
|
|
16
|
+
dist-templates/
|
|
16
17
|
storybook-static
|
|
17
18
|
target
|
|
18
19
|
filter/
|
|
@@ -29,6 +30,7 @@ test-adyen/
|
|
|
29
30
|
stripe-authpi/
|
|
30
31
|
stripe-app/
|
|
31
32
|
secrets.json
|
|
33
|
+
*.secrets.json
|
|
32
34
|
.idea
|
|
33
35
|
.vscode
|
|
34
36
|
*.pem
|
|
@@ -49,3 +51,10 @@ worker-configuration.d.ts
|
|
|
49
51
|
.worktrees/
|
|
50
52
|
docs/plans/
|
|
51
53
|
docs/superpowers/
|
|
54
|
+
docs/.obsidian/
|
|
55
|
+
|
|
56
|
+
# Operator-rendered ops-db bootstrap files — carry environment-specific
|
|
57
|
+
# captured `agt_*` IDs from the Step 3 bootstrap. The .template stays in
|
|
58
|
+
# git as the canonical structure; rendered copies stay local.
|
|
59
|
+
d1-databases/ops-db/0005_bootstrap_capability_matrix.sql
|
|
60
|
+
d1-databases/ops-db/0005_bootstrap_capability_matrix_prod.sql
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.8.0](https://github.com/arbfay/authpi/compare/authpi-admin-v0.7.0...authpi-admin-v0.8.0) (2026-06-21)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* **admin-py:** release public SDK types module ([20a8329](https://github.com/arbfay/authpi/commit/20a8329e8ddda0923a8dfd7e8d2380c060ea3f42))
|
|
9
|
+
|
|
10
|
+
## [0.7.0](https://github.com/arbfay/authpi/compare/authpi-admin-v0.6.0...authpi-admin-v0.7.0) (2026-06-12)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Features
|
|
14
|
+
|
|
15
|
+
* GET /v1/me — caller identity endpoint (AUT-163) ([#286](https://github.com/arbfay/authpi/issues/286)) ([b9f35dc](https://github.com/arbfay/authpi/commit/b9f35dc2fd8cca77cdd2079b240ce28dc11d35a8))
|
|
16
|
+
|
|
17
|
+
## [0.6.0](https://github.com/arbfay/authpi/compare/authpi-admin-v0.4.0...authpi-admin-v0.6.0) (2026-06-11)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### ⚠ BREAKING CHANGES
|
|
21
|
+
|
|
22
|
+
* **admin-py:** collision-split class renames (see admin-ts) and issuer-scope org-subresource accessors moved to the organization scope; the removed accessors 404'd on every call, so no working integration is affected.
|
|
23
|
+
* **admin-ts:** cross-scope name collisions split generated classes (ApiKeysResource -> AccountApiKeysResource, TokensResource -> AccountTokensResource, SessionsResource -> IssuerSessionsResource / UserSessionsResource, VerifiersResource -> AgentVerifiersResource / UserVerifiersResource), and the issuer-scope groups/invitations/ members/domains accessors moved to the new organization scope. The removed accessors produced 404s on every call, so no working integration is affected.
|
|
24
|
+
|
|
25
|
+
### Features
|
|
26
|
+
|
|
27
|
+
* **admin-py:** regenerate from corrected spec — org scope, restored endpoints, credits ([2cd96b5](https://github.com/arbfay/authpi/commit/2cd96b569ab9ddff8589fd8e786a16052af4c7e5))
|
|
28
|
+
* **admin-ts:** regenerate from corrected spec — org scope, restored endpoints, credits ([3932b60](https://github.com/arbfay/authpi/commit/3932b609116f059dff36a73f9a5378c21d69eb3d))
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
### Bug Fixes
|
|
32
|
+
|
|
33
|
+
* **generators:** action methods carry query params and real response types ([0f515ec](https://github.com/arbfay/authpi/commit/0f515ecc1ce737f62e2826ff3106b97c35a6ce2f))
|
|
34
|
+
|
|
35
|
+
## [0.4.0](https://github.com/arbfay/authpi/compare/authpi-admin-v0.3.1...authpi-admin-v0.4.0) (2026-06-11)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
### ⚠ BREAKING CHANGES
|
|
39
|
+
|
|
40
|
+
* **admin-py:** `account_id` is now required for both auth modes. The api_key-without-account_id form never produced a successful request.
|
|
41
|
+
* **admin-py:** `api_key` changed from `str` to `tuple[str, str]`. The previous form never authenticated successfully, so no working integration is affected.
|
|
42
|
+
|
|
43
|
+
### Bug Fixes
|
|
44
|
+
|
|
45
|
+
* **admin-py:** default base_url to the production API ([4351942](https://github.com/arbfay/authpi/commit/4351942f3c40d75b24e079e1d83553e6f58a3303))
|
|
46
|
+
* **admin-py:** require account_id — the API has no accounts/me alias ([58cf3a2](https://github.com/arbfay/authpi/commit/58cf3a2a1af402f7b9e20ceadd84e6015957f000))
|
|
47
|
+
* **admin-py:** send API keys as HTTP Basic id:secret pair ([aee64f7](https://github.com/arbfay/authpi/commit/aee64f777abb18ecb4578c178cf55a4c72abb046))
|
|
48
|
+
|
|
49
|
+
## [0.3.1](https://github.com/arbfay/authpi/compare/authpi-admin-v0.3.0...authpi-admin-v0.3.1) (2026-06-10)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
### Bug Fixes
|
|
53
|
+
|
|
54
|
+
* **admin-py:** unwrap the single-resource data envelope in generated methods ([d63ee44](https://github.com/arbfay/authpi/commit/d63ee44d9db64e46dbfbfc5da66c4a1aaeacf609))
|
|
55
|
+
|
|
56
|
+
## [0.3.0](https://github.com/arbfay/authpi/compare/authpi-admin-v0.2.0...authpi-admin-v0.3.0) (2026-04-17)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
### Features
|
|
60
|
+
|
|
61
|
+
* **sdks:** set custom User-Agent for SDK identification ([#213](https://github.com/arbfay/authpi/issues/213)) ([dec716c](https://github.com/arbfay/authpi/commit/dec716c99d877e99496f45c69f3625341bb2bc2e))
|
|
62
|
+
|
|
63
|
+
## [0.2.0](https://github.com/arbfay/authpi/compare/authpi-admin-v0.1.0...authpi-admin-v0.2.0) (2026-04-13)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
### ⚠ BREAKING CHANGES
|
|
67
|
+
|
|
68
|
+
* All generated type interfaces now use snake_case field names. Consumers referencing camelCase fields (e.g., Client.issuerId) must update to snake_case (Client.issuer_id).
|
|
69
|
+
|
|
70
|
+
### Features
|
|
71
|
+
|
|
72
|
+
* Admin SDK code generator + Python SDK (authpi-admin) ([#186](https://github.com/arbfay/authpi/issues/186)) ([3eb5055](https://github.com/arbfay/authpi/commit/3eb505579f34a49769e0b01eb59f6d571ad74571))
|
|
73
|
+
* SDK convergence — extend @authpi/admin, migrate console, security fixes ([#197](https://github.com/arbfay/authpi/issues/197)) ([3d4896f](https://github.com/arbfay/authpi/commit/3d4896fa60381542d99a66e394809d128bf6bcf0))
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
### Bug Fixes
|
|
77
|
+
|
|
78
|
+
* **admin:** fix custom action paths, security hardening for TS and Python SDKs ([#189](https://github.com/arbfay/authpi/issues/189)) ([bf5e0b4](https://github.com/arbfay/authpi/commit/bf5e0b430a76173a1a1b4ff188095a96b337a74c))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: authpi-admin
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.0
|
|
4
4
|
Summary: Official Python Admin SDK for AuthPI Core API
|
|
5
5
|
Project-URL: Homepage, https://authpi.com
|
|
6
6
|
Project-URL: Documentation, https://docs.authpi.com/sdk/python/admin
|
|
@@ -46,8 +46,9 @@ pip install authpi-admin
|
|
|
46
46
|
|
|
47
47
|
```python
|
|
48
48
|
from authpi_admin import AuthPIAdmin
|
|
49
|
+
from authpi_admin.types import WebhookEventType
|
|
49
50
|
|
|
50
|
-
async with AuthPIAdmin(api_key="key_xxx") as admin:
|
|
51
|
+
async with AuthPIAdmin(api_key=("key_xxx", "your_key_secret"), account_id="acc_xxx") as admin:
|
|
51
52
|
# List issuers
|
|
52
53
|
page = await admin.issuers.list(limit=10)
|
|
53
54
|
print(page.data)
|
|
@@ -64,14 +65,41 @@ async with AuthPIAdmin(api_key="key_xxx") as admin:
|
|
|
64
65
|
"email": "alice@example.com",
|
|
65
66
|
"display_name": "Alice",
|
|
66
67
|
})
|
|
68
|
+
|
|
69
|
+
# Create a webhook with typed event subscriptions
|
|
70
|
+
webhook = await admin.webhooks.create({
|
|
71
|
+
"name": "Lifecycle events",
|
|
72
|
+
"url": "https://example.com/webhooks/authpi",
|
|
73
|
+
"auth": {"type": "signature"},
|
|
74
|
+
"events": [
|
|
75
|
+
WebhookEventType.ORGANIZATION_CREATED,
|
|
76
|
+
WebhookEventType.USER_CREATED,
|
|
77
|
+
],
|
|
78
|
+
})
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Generated response models, request `TypedDict`s, and enums are exported from `authpi_admin.types`. Most calls can use dict literals directly; import request types only when you want to annotate a reusable payload:
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
from authpi_admin.types import CreateWebhookInput, WebhookEventType
|
|
85
|
+
|
|
86
|
+
payload: CreateWebhookInput = {
|
|
87
|
+
"name": "Lifecycle events",
|
|
88
|
+
"url": "https://example.com/webhooks/authpi",
|
|
89
|
+
"auth": {"type": "signature"},
|
|
90
|
+
"events": [WebhookEventType.USER_CREATED],
|
|
91
|
+
}
|
|
92
|
+
await admin.webhooks.create(payload)
|
|
67
93
|
```
|
|
68
94
|
|
|
69
95
|
## Authentication
|
|
70
96
|
|
|
71
97
|
### API Key (default)
|
|
72
98
|
|
|
99
|
+
API keys are issued as an **id + secret pair** — both parts are shown once when you create the key in the dashboard. The SDK sends them as HTTP Basic credentials (`key_id:key_secret`):
|
|
100
|
+
|
|
73
101
|
```python
|
|
74
|
-
admin = AuthPIAdmin(api_key="key_xxx")
|
|
102
|
+
admin = AuthPIAdmin(api_key=("key_xxx", "your_key_secret"), account_id="acc_xxx")
|
|
75
103
|
```
|
|
76
104
|
|
|
77
105
|
### Bearer Token
|
|
@@ -101,6 +129,24 @@ admin = AuthPIAdmin(
|
|
|
101
129
|
|
|
102
130
|
When `on_token_expired` is provided, the SDK calls it on 401 responses and retries the request with the new token. Concurrent 401s are deduplicated — only one refresh runs at a time.
|
|
103
131
|
|
|
132
|
+
### Account resolution
|
|
133
|
+
|
|
134
|
+
`account_id` is optional. When omitted, the SDK resolves it once via `GET /v1/me` on the first request and caches it — an API key always maps to exactly one account:
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
async with AuthPIAdmin(api_key=("key_xxx", "your_key_secret")) as admin:
|
|
138
|
+
issuers = await admin.issuers.list() # resolves the account transparently
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
If the credential can act on zero or multiple accounts (possible with user bearer tokens), the SDK raises a `ConfigurationError` naming the choices — pass `account_id` explicitly in that case.
|
|
142
|
+
|
|
143
|
+
You can also ask directly who the API considers you to be:
|
|
144
|
+
|
|
145
|
+
```python
|
|
146
|
+
me = await admin.whoami()
|
|
147
|
+
# {"type": "api_key", "key_id": "key_...", "issuer_id": "i_...", "accounts": [{"account_id": ..., "org_id": ..., "scopes": [...]}]}
|
|
148
|
+
```
|
|
149
|
+
|
|
104
150
|
## Scoped Client Pattern
|
|
105
151
|
|
|
106
152
|
The SDK mirrors the API's resource hierarchy. Navigate with chained accessors:
|
|
@@ -108,7 +154,12 @@ The SDK mirrors the API's resource hierarchy. Navigate with chained accessors:
|
|
|
108
154
|
```python
|
|
109
155
|
# Account-level resources
|
|
110
156
|
await admin.issuers.list()
|
|
111
|
-
await admin.webhooks.create({
|
|
157
|
+
await admin.webhooks.create({
|
|
158
|
+
"name": "Lifecycle events",
|
|
159
|
+
"url": "https://...",
|
|
160
|
+
"auth": {"type": "signature"},
|
|
161
|
+
"events": [WebhookEventType.USER_CREATED],
|
|
162
|
+
})
|
|
112
163
|
await admin.events.list(limit=50)
|
|
113
164
|
|
|
114
165
|
# Issuer scope
|
|
@@ -119,6 +170,10 @@ await iss.clients.list()
|
|
|
119
170
|
await iss.organizations.list()
|
|
120
171
|
|
|
121
172
|
# User scope (nested under issuer)
|
|
173
|
+
org = admin.issuer("iss_xxx").organization("org_xxx")
|
|
174
|
+
await org.members.list()
|
|
175
|
+
await org.sso.add_domain(domain="acme.com")
|
|
176
|
+
|
|
122
177
|
usr = admin.issuer("iss_xxx").user("usr_xxx")
|
|
123
178
|
await usr.get()
|
|
124
179
|
await usr.sessions.list()
|
|
@@ -160,14 +215,15 @@ Read-only requests (GET, HEAD, OPTIONS) are automatically retried on 429, 502, 5
|
|
|
160
215
|
|
|
161
216
|
```python
|
|
162
217
|
# Default: retries enabled (3 attempts, 1s base delay, exponential backoff)
|
|
163
|
-
admin = AuthPIAdmin(api_key="key_xxx")
|
|
218
|
+
admin = AuthPIAdmin(api_key=("key_xxx", "your_key_secret"), account_id="acc_xxx")
|
|
164
219
|
|
|
165
220
|
# Disable retries
|
|
166
|
-
admin = AuthPIAdmin(api_key="key_xxx", retries=False)
|
|
221
|
+
admin = AuthPIAdmin(api_key=("key_xxx", "your_key_secret"), retries=False)
|
|
167
222
|
|
|
168
223
|
# Custom retry config
|
|
169
224
|
admin = AuthPIAdmin(
|
|
170
|
-
api_key="key_xxx",
|
|
225
|
+
api_key=("key_xxx", "your_key_secret"),
|
|
226
|
+
account_id="acc_xxx",
|
|
171
227
|
retries={"limit": 5, "delay": 0.5, "backoff": "linear"},
|
|
172
228
|
)
|
|
173
229
|
```
|
|
@@ -239,8 +295,9 @@ except AuthenticationError:
|
|
|
239
295
|
from authpi_admin import AuthPIAdmin
|
|
240
296
|
|
|
241
297
|
admin = AuthPIAdmin(
|
|
242
|
-
api_key="key_xxx",
|
|
243
|
-
|
|
298
|
+
api_key=("key_xxx", "your_key_secret"), # or access_token instead
|
|
299
|
+
account_id="acc_xxx", # optional — resolved via GET /v1/me when omitted
|
|
300
|
+
base_url="https://api.authpi.com", # default
|
|
244
301
|
timeout=30.0, # default, in seconds
|
|
245
302
|
default_headers={"X-Custom": "value"}, # optional extra headers
|
|
246
303
|
retries=True, # default (or False, or dict)
|
|
@@ -256,7 +313,7 @@ import httpx
|
|
|
256
313
|
from authpi_admin import AuthPIAdmin
|
|
257
314
|
|
|
258
315
|
async with httpx.AsyncClient(proxies="http://proxy:8080") as http:
|
|
259
|
-
admin = AuthPIAdmin(api_key="key_xxx", http_client=http)
|
|
316
|
+
admin = AuthPIAdmin(api_key=("key_xxx", "your_key_secret"), http_client=http)
|
|
260
317
|
await admin.issuers.list()
|
|
261
318
|
```
|
|
262
319
|
|
|
@@ -265,7 +322,7 @@ async with httpx.AsyncClient(proxies="http://proxy:8080") as http:
|
|
|
265
322
|
The SDK supports async context managers for clean resource cleanup:
|
|
266
323
|
|
|
267
324
|
```python
|
|
268
|
-
async with AuthPIAdmin(api_key="key_xxx") as admin:
|
|
325
|
+
async with AuthPIAdmin(api_key=("key_xxx", "your_key_secret"), account_id="acc_xxx") as admin:
|
|
269
326
|
await admin.issuers.list()
|
|
270
327
|
# httpx client is closed automatically
|
|
271
328
|
```
|
|
@@ -273,7 +330,7 @@ async with AuthPIAdmin(api_key="key_xxx") as admin:
|
|
|
273
330
|
Or close manually:
|
|
274
331
|
|
|
275
332
|
```python
|
|
276
|
-
admin = AuthPIAdmin(api_key="key_xxx")
|
|
333
|
+
admin = AuthPIAdmin(api_key=("key_xxx", "your_key_secret"), account_id="acc_xxx")
|
|
277
334
|
try:
|
|
278
335
|
await admin.issuers.list()
|
|
279
336
|
finally:
|
|
@@ -14,8 +14,9 @@ pip install authpi-admin
|
|
|
14
14
|
|
|
15
15
|
```python
|
|
16
16
|
from authpi_admin import AuthPIAdmin
|
|
17
|
+
from authpi_admin.types import WebhookEventType
|
|
17
18
|
|
|
18
|
-
async with AuthPIAdmin(api_key="key_xxx") as admin:
|
|
19
|
+
async with AuthPIAdmin(api_key=("key_xxx", "your_key_secret"), account_id="acc_xxx") as admin:
|
|
19
20
|
# List issuers
|
|
20
21
|
page = await admin.issuers.list(limit=10)
|
|
21
22
|
print(page.data)
|
|
@@ -32,14 +33,41 @@ async with AuthPIAdmin(api_key="key_xxx") as admin:
|
|
|
32
33
|
"email": "alice@example.com",
|
|
33
34
|
"display_name": "Alice",
|
|
34
35
|
})
|
|
36
|
+
|
|
37
|
+
# Create a webhook with typed event subscriptions
|
|
38
|
+
webhook = await admin.webhooks.create({
|
|
39
|
+
"name": "Lifecycle events",
|
|
40
|
+
"url": "https://example.com/webhooks/authpi",
|
|
41
|
+
"auth": {"type": "signature"},
|
|
42
|
+
"events": [
|
|
43
|
+
WebhookEventType.ORGANIZATION_CREATED,
|
|
44
|
+
WebhookEventType.USER_CREATED,
|
|
45
|
+
],
|
|
46
|
+
})
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Generated response models, request `TypedDict`s, and enums are exported from `authpi_admin.types`. Most calls can use dict literals directly; import request types only when you want to annotate a reusable payload:
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
from authpi_admin.types import CreateWebhookInput, WebhookEventType
|
|
53
|
+
|
|
54
|
+
payload: CreateWebhookInput = {
|
|
55
|
+
"name": "Lifecycle events",
|
|
56
|
+
"url": "https://example.com/webhooks/authpi",
|
|
57
|
+
"auth": {"type": "signature"},
|
|
58
|
+
"events": [WebhookEventType.USER_CREATED],
|
|
59
|
+
}
|
|
60
|
+
await admin.webhooks.create(payload)
|
|
35
61
|
```
|
|
36
62
|
|
|
37
63
|
## Authentication
|
|
38
64
|
|
|
39
65
|
### API Key (default)
|
|
40
66
|
|
|
67
|
+
API keys are issued as an **id + secret pair** — both parts are shown once when you create the key in the dashboard. The SDK sends them as HTTP Basic credentials (`key_id:key_secret`):
|
|
68
|
+
|
|
41
69
|
```python
|
|
42
|
-
admin = AuthPIAdmin(api_key="key_xxx")
|
|
70
|
+
admin = AuthPIAdmin(api_key=("key_xxx", "your_key_secret"), account_id="acc_xxx")
|
|
43
71
|
```
|
|
44
72
|
|
|
45
73
|
### Bearer Token
|
|
@@ -69,6 +97,24 @@ admin = AuthPIAdmin(
|
|
|
69
97
|
|
|
70
98
|
When `on_token_expired` is provided, the SDK calls it on 401 responses and retries the request with the new token. Concurrent 401s are deduplicated — only one refresh runs at a time.
|
|
71
99
|
|
|
100
|
+
### Account resolution
|
|
101
|
+
|
|
102
|
+
`account_id` is optional. When omitted, the SDK resolves it once via `GET /v1/me` on the first request and caches it — an API key always maps to exactly one account:
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
async with AuthPIAdmin(api_key=("key_xxx", "your_key_secret")) as admin:
|
|
106
|
+
issuers = await admin.issuers.list() # resolves the account transparently
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
If the credential can act on zero or multiple accounts (possible with user bearer tokens), the SDK raises a `ConfigurationError` naming the choices — pass `account_id` explicitly in that case.
|
|
110
|
+
|
|
111
|
+
You can also ask directly who the API considers you to be:
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
me = await admin.whoami()
|
|
115
|
+
# {"type": "api_key", "key_id": "key_...", "issuer_id": "i_...", "accounts": [{"account_id": ..., "org_id": ..., "scopes": [...]}]}
|
|
116
|
+
```
|
|
117
|
+
|
|
72
118
|
## Scoped Client Pattern
|
|
73
119
|
|
|
74
120
|
The SDK mirrors the API's resource hierarchy. Navigate with chained accessors:
|
|
@@ -76,7 +122,12 @@ The SDK mirrors the API's resource hierarchy. Navigate with chained accessors:
|
|
|
76
122
|
```python
|
|
77
123
|
# Account-level resources
|
|
78
124
|
await admin.issuers.list()
|
|
79
|
-
await admin.webhooks.create({
|
|
125
|
+
await admin.webhooks.create({
|
|
126
|
+
"name": "Lifecycle events",
|
|
127
|
+
"url": "https://...",
|
|
128
|
+
"auth": {"type": "signature"},
|
|
129
|
+
"events": [WebhookEventType.USER_CREATED],
|
|
130
|
+
})
|
|
80
131
|
await admin.events.list(limit=50)
|
|
81
132
|
|
|
82
133
|
# Issuer scope
|
|
@@ -87,6 +138,10 @@ await iss.clients.list()
|
|
|
87
138
|
await iss.organizations.list()
|
|
88
139
|
|
|
89
140
|
# User scope (nested under issuer)
|
|
141
|
+
org = admin.issuer("iss_xxx").organization("org_xxx")
|
|
142
|
+
await org.members.list()
|
|
143
|
+
await org.sso.add_domain(domain="acme.com")
|
|
144
|
+
|
|
90
145
|
usr = admin.issuer("iss_xxx").user("usr_xxx")
|
|
91
146
|
await usr.get()
|
|
92
147
|
await usr.sessions.list()
|
|
@@ -128,14 +183,15 @@ Read-only requests (GET, HEAD, OPTIONS) are automatically retried on 429, 502, 5
|
|
|
128
183
|
|
|
129
184
|
```python
|
|
130
185
|
# Default: retries enabled (3 attempts, 1s base delay, exponential backoff)
|
|
131
|
-
admin = AuthPIAdmin(api_key="key_xxx")
|
|
186
|
+
admin = AuthPIAdmin(api_key=("key_xxx", "your_key_secret"), account_id="acc_xxx")
|
|
132
187
|
|
|
133
188
|
# Disable retries
|
|
134
|
-
admin = AuthPIAdmin(api_key="key_xxx", retries=False)
|
|
189
|
+
admin = AuthPIAdmin(api_key=("key_xxx", "your_key_secret"), retries=False)
|
|
135
190
|
|
|
136
191
|
# Custom retry config
|
|
137
192
|
admin = AuthPIAdmin(
|
|
138
|
-
api_key="key_xxx",
|
|
193
|
+
api_key=("key_xxx", "your_key_secret"),
|
|
194
|
+
account_id="acc_xxx",
|
|
139
195
|
retries={"limit": 5, "delay": 0.5, "backoff": "linear"},
|
|
140
196
|
)
|
|
141
197
|
```
|
|
@@ -207,8 +263,9 @@ except AuthenticationError:
|
|
|
207
263
|
from authpi_admin import AuthPIAdmin
|
|
208
264
|
|
|
209
265
|
admin = AuthPIAdmin(
|
|
210
|
-
api_key="key_xxx",
|
|
211
|
-
|
|
266
|
+
api_key=("key_xxx", "your_key_secret"), # or access_token instead
|
|
267
|
+
account_id="acc_xxx", # optional — resolved via GET /v1/me when omitted
|
|
268
|
+
base_url="https://api.authpi.com", # default
|
|
212
269
|
timeout=30.0, # default, in seconds
|
|
213
270
|
default_headers={"X-Custom": "value"}, # optional extra headers
|
|
214
271
|
retries=True, # default (or False, or dict)
|
|
@@ -224,7 +281,7 @@ import httpx
|
|
|
224
281
|
from authpi_admin import AuthPIAdmin
|
|
225
282
|
|
|
226
283
|
async with httpx.AsyncClient(proxies="http://proxy:8080") as http:
|
|
227
|
-
admin = AuthPIAdmin(api_key="key_xxx", http_client=http)
|
|
284
|
+
admin = AuthPIAdmin(api_key=("key_xxx", "your_key_secret"), http_client=http)
|
|
228
285
|
await admin.issuers.list()
|
|
229
286
|
```
|
|
230
287
|
|
|
@@ -233,7 +290,7 @@ async with httpx.AsyncClient(proxies="http://proxy:8080") as http:
|
|
|
233
290
|
The SDK supports async context managers for clean resource cleanup:
|
|
234
291
|
|
|
235
292
|
```python
|
|
236
|
-
async with AuthPIAdmin(api_key="key_xxx") as admin:
|
|
293
|
+
async with AuthPIAdmin(api_key=("key_xxx", "your_key_secret"), account_id="acc_xxx") as admin:
|
|
237
294
|
await admin.issuers.list()
|
|
238
295
|
# httpx client is closed automatically
|
|
239
296
|
```
|
|
@@ -241,7 +298,7 @@ async with AuthPIAdmin(api_key="key_xxx") as admin:
|
|
|
241
298
|
Or close manually:
|
|
242
299
|
|
|
243
300
|
```python
|
|
244
|
-
admin = AuthPIAdmin(api_key="key_xxx")
|
|
301
|
+
admin = AuthPIAdmin(api_key=("key_xxx", "your_key_secret"), account_id="acc_xxx")
|
|
245
302
|
try:
|
|
246
303
|
await admin.issuers.list()
|
|
247
304
|
finally:
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
__version__ = "0.
|
|
5
|
+
__version__ = "0.8.0"
|
|
6
6
|
|
|
7
7
|
from authpi_admin.client import AuthPIAdmin
|
|
8
8
|
from authpi_admin.errors import (
|
|
@@ -23,11 +23,12 @@ from authpi_admin.errors import (
|
|
|
23
23
|
UnexpectedError,
|
|
24
24
|
ValidationError,
|
|
25
25
|
)
|
|
26
|
+
from authpi_admin.generated.scopes.agent_scope import AgentScope
|
|
26
27
|
from authpi_admin.generated.scopes.issuer_scope import IssuerScope
|
|
27
28
|
from authpi_admin.generated.scopes.user_scope import UserScope
|
|
28
|
-
from authpi_admin.generated.scopes.agent_scope import AgentScope
|
|
29
29
|
from authpi_admin.generated.scopes.webhook_scope import WebhookScope
|
|
30
30
|
from authpi_admin.pagination import Page
|
|
31
|
+
from authpi_admin.types import WebhookEventType
|
|
31
32
|
|
|
32
33
|
__all__ = [
|
|
33
34
|
"AgentScope",
|
|
@@ -51,5 +52,6 @@ __all__ = [
|
|
|
51
52
|
"UnexpectedError",
|
|
52
53
|
"UserScope",
|
|
53
54
|
"ValidationError",
|
|
55
|
+
"WebhookEventType",
|
|
54
56
|
"WebhookScope",
|
|
55
57
|
]
|
|
@@ -2,23 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
from collections.abc import Awaitable, Callable
|
|
5
6
|
from functools import cached_property
|
|
6
|
-
from typing import TYPE_CHECKING, Any
|
|
7
|
+
from typing import TYPE_CHECKING, Any
|
|
7
8
|
|
|
8
9
|
import httpx
|
|
9
10
|
|
|
10
11
|
from authpi_admin.errors import ConfigurationError
|
|
11
|
-
from authpi_admin.
|
|
12
|
+
from authpi_admin.generated.resources.account_api_keys import AccountApiKeysResource
|
|
13
|
+
from authpi_admin.generated.resources.account_tokens import AccountTokensResource
|
|
12
14
|
from authpi_admin.generated.resources.accounts import AccountsResource
|
|
13
|
-
from authpi_admin.generated.resources.
|
|
15
|
+
from authpi_admin.generated.resources.credits import CreditsResource
|
|
14
16
|
from authpi_admin.generated.resources.domains import DomainsResource
|
|
15
17
|
from authpi_admin.generated.resources.events import EventsResource
|
|
16
18
|
from authpi_admin.generated.resources.issuers import IssuersResource
|
|
17
19
|
from authpi_admin.generated.resources.notes import NotesResource
|
|
18
|
-
from authpi_admin.generated.resources.tokens import TokensResource
|
|
19
20
|
from authpi_admin.generated.resources.webhooks import WebhooksResource
|
|
20
21
|
from authpi_admin.generated.scopes.issuer_scope import IssuerScope
|
|
21
22
|
from authpi_admin.generated.scopes.webhook_scope import WebhookScope
|
|
23
|
+
from authpi_admin.http_client import UNRESOLVED_ACCOUNT_ID, HttpClient
|
|
22
24
|
|
|
23
25
|
if TYPE_CHECKING:
|
|
24
26
|
from types import TracebackType
|
|
@@ -27,8 +29,11 @@ if TYPE_CHECKING:
|
|
|
27
29
|
class AuthPIAdmin:
|
|
28
30
|
"""Entry-point client for the AuthPI Core API.
|
|
29
31
|
|
|
30
|
-
Usage with API key:
|
|
31
|
-
async with AuthPIAdmin(
|
|
32
|
+
Usage with API key (id + secret pair, sent as HTTP Basic):
|
|
33
|
+
async with AuthPIAdmin(
|
|
34
|
+
api_key=("key_xxx", "your_key_secret"),
|
|
35
|
+
account_id="acc_xxx",
|
|
36
|
+
) as admin:
|
|
32
37
|
users = await admin.issuer("iss_xxx").users.list()
|
|
33
38
|
|
|
34
39
|
Usage with bearer token:
|
|
@@ -42,11 +47,11 @@ class AuthPIAdmin:
|
|
|
42
47
|
def __init__(
|
|
43
48
|
self,
|
|
44
49
|
*,
|
|
45
|
-
api_key: str | None = None,
|
|
50
|
+
api_key: tuple[str, str] | None = None,
|
|
46
51
|
access_token: str | None = None,
|
|
47
52
|
on_token_expired: Callable[[], Awaitable[dict[str, str]]] | None = None,
|
|
48
53
|
account_id: str | None = None,
|
|
49
|
-
base_url: str = "https://api.authpi.
|
|
54
|
+
base_url: str = "https://api.authpi.com",
|
|
50
55
|
http_client: httpx.AsyncClient | None = None,
|
|
51
56
|
timeout: float = 30.0,
|
|
52
57
|
default_headers: dict[str, str] | None = None,
|
|
@@ -56,10 +61,7 @@ class AuthPIAdmin:
|
|
|
56
61
|
raise ConfigurationError("Either api_key or access_token is required")
|
|
57
62
|
if api_key and access_token:
|
|
58
63
|
raise ConfigurationError("Provide either api_key or access_token, not both")
|
|
59
|
-
if access_token and not account_id:
|
|
60
|
-
raise ConfigurationError("account_id is required when using access_token")
|
|
61
64
|
|
|
62
|
-
resolved_account_id = account_id or "me"
|
|
63
65
|
self._http = HttpClient(
|
|
64
66
|
base_url=base_url,
|
|
65
67
|
api_key=api_key,
|
|
@@ -70,13 +72,23 @@ class AuthPIAdmin:
|
|
|
70
72
|
default_headers=default_headers,
|
|
71
73
|
retries=retries,
|
|
72
74
|
)
|
|
73
|
-
|
|
75
|
+
# Without an explicit account_id, paths carry a placeholder that the
|
|
76
|
+
# HttpClient substitutes at request time after resolving GET /v1/me once.
|
|
77
|
+
self._base_path = f"/v1/accounts/{account_id or UNRESOLVED_ACCOUNT_ID}"
|
|
74
78
|
|
|
75
79
|
@property
|
|
76
80
|
def http(self) -> HttpClient:
|
|
77
81
|
"""The underlying HTTP client (for advanced use)."""
|
|
78
82
|
return self._http
|
|
79
83
|
|
|
84
|
+
async def whoami(self) -> dict[str, Any]:
|
|
85
|
+
"""Who does the Core API consider this credential to be?
|
|
86
|
+
|
|
87
|
+
Returns the credential type and the verified accounts it can act on
|
|
88
|
+
(``GET /v1/me``).
|
|
89
|
+
"""
|
|
90
|
+
return await self._http.whoami()
|
|
91
|
+
|
|
80
92
|
# --- Account-level resource accessors ---
|
|
81
93
|
|
|
82
94
|
@cached_property
|
|
@@ -85,9 +97,14 @@ class AuthPIAdmin:
|
|
|
85
97
|
return AccountsResource(self._http, "/v1/accounts")
|
|
86
98
|
|
|
87
99
|
@cached_property
|
|
88
|
-
def api_keys(self) ->
|
|
100
|
+
def api_keys(self) -> AccountApiKeysResource:
|
|
89
101
|
"""Manage account-level API keys."""
|
|
90
|
-
return
|
|
102
|
+
return AccountApiKeysResource(self._http, f"{self._base_path}/api-keys")
|
|
103
|
+
|
|
104
|
+
@cached_property
|
|
105
|
+
def credits(self) -> CreditsResource:
|
|
106
|
+
"""Account credit balance, ledger, and top-ups."""
|
|
107
|
+
return CreditsResource(self._http, f"{self._base_path}/credits")
|
|
91
108
|
|
|
92
109
|
@cached_property
|
|
93
110
|
def domains(self) -> DomainsResource:
|
|
@@ -110,9 +127,9 @@ class AuthPIAdmin:
|
|
|
110
127
|
return NotesResource(self._http, f"{self._base_path}/notes")
|
|
111
128
|
|
|
112
129
|
@cached_property
|
|
113
|
-
def tokens(self) ->
|
|
130
|
+
def tokens(self) -> AccountTokensResource:
|
|
114
131
|
"""Manage account-level tokens."""
|
|
115
|
-
return
|
|
132
|
+
return AccountTokensResource(self._http, f"{self._base_path}/tokens")
|
|
116
133
|
|
|
117
134
|
@cached_property
|
|
118
135
|
def webhooks(self) -> WebhooksResource:
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""Generated exports — DO NOT EDIT."""
|
|
2
|
+
# ruff: noqa: F401,F403
|
|
3
|
+
|
|
4
|
+
from .models import *
|
|
5
|
+
from .resources.account_api_keys import AccountApiKeysResource
|
|
6
|
+
from .resources.account_tokens import AccountTokensResource
|
|
7
|
+
from .resources.accounts import AccountsResource
|
|
8
|
+
from .resources.agent_verifiers import AgentVerifiersResource
|
|
9
|
+
from .resources.agents import AgentsResource
|
|
10
|
+
from .resources.approvals import ApprovalsResource
|
|
11
|
+
from .resources.auth_methods import AuthMethodsResource
|
|
12
|
+
from .resources.clients import ClientsResource
|
|
13
|
+
from .resources.credits import CreditsResource
|
|
14
|
+
from .resources.deliveries import DeliveriesResource
|
|
15
|
+
from .resources.domains import DomainsResource
|
|
16
|
+
from .resources.events import EventsResource
|
|
17
|
+
from .resources.groups import GroupsResource
|
|
18
|
+
from .resources.invitations import InvitationsResource
|
|
19
|
+
from .resources.issuer_sessions import IssuerSessionsResource
|
|
20
|
+
from .resources.issuer_tokens import IssuerTokensResource
|
|
21
|
+
from .resources.issuers import IssuersResource
|
|
22
|
+
from .resources.members import MembersResource
|
|
23
|
+
from .resources.notes import NotesResource
|
|
24
|
+
from .resources.organization_api_keys import OrganizationApiKeysResource
|
|
25
|
+
from .resources.organizations import OrganizationsResource
|
|
26
|
+
from .resources.payment_methods import PaymentMethodsResource
|
|
27
|
+
from .resources.sso import SsoResource
|
|
28
|
+
from .resources.trusted_devices import TrustedDevicesResource
|
|
29
|
+
from .resources.user_sessions import UserSessionsResource
|
|
30
|
+
from .resources.user_tokens import UserTokensResource
|
|
31
|
+
from .resources.user_verifiers import UserVerifiersResource
|
|
32
|
+
from .resources.users import UsersResource
|
|
33
|
+
from .resources.webhooks import WebhooksResource
|
|
34
|
+
from .scopes.agent_scope import AgentScope
|
|
35
|
+
from .scopes.issuer_scope import IssuerScope
|
|
36
|
+
from .scopes.organization_scope import OrganizationScope
|
|
37
|
+
from .scopes.user_scope import UserScope
|
|
38
|
+
from .scopes.webhook_scope import WebhookScope
|