authpi-admin 0.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. authpi_admin/__init__.py +55 -0
  2. authpi_admin/_version.py +5 -0
  3. authpi_admin/client.py +155 -0
  4. authpi_admin/errors.py +218 -0
  5. authpi_admin/generated/__init__.py +28 -0
  6. authpi_admin/generated/models.py +380 -0
  7. authpi_admin/generated/resources/__init__.py +0 -0
  8. authpi_admin/generated/resources/accounts.py +157 -0
  9. authpi_admin/generated/resources/agents.py +177 -0
  10. authpi_admin/generated/resources/api_keys.py +213 -0
  11. authpi_admin/generated/resources/approvals.py +109 -0
  12. authpi_admin/generated/resources/auth_methods.py +148 -0
  13. authpi_admin/generated/resources/clients.py +197 -0
  14. authpi_admin/generated/resources/deliveries.py +83 -0
  15. authpi_admin/generated/resources/domains.py +194 -0
  16. authpi_admin/generated/resources/events.py +111 -0
  17. authpi_admin/generated/resources/groups.py +160 -0
  18. authpi_admin/generated/resources/invitations.py +202 -0
  19. authpi_admin/generated/resources/issuers.py +157 -0
  20. authpi_admin/generated/resources/members.py +154 -0
  21. authpi_admin/generated/resources/notes.py +192 -0
  22. authpi_admin/generated/resources/organizations.py +244 -0
  23. authpi_admin/generated/resources/sessions.py +125 -0
  24. authpi_admin/generated/resources/tokens.py +94 -0
  25. authpi_admin/generated/resources/trusted_devices.py +102 -0
  26. authpi_admin/generated/resources/users.py +224 -0
  27. authpi_admin/generated/resources/verifiers.py +102 -0
  28. authpi_admin/generated/resources/webhooks.py +167 -0
  29. authpi_admin/generated/scopes/__init__.py +0 -0
  30. authpi_admin/generated/scopes/agent_scope.py +84 -0
  31. authpi_admin/generated/scopes/issuer_scope.py +160 -0
  32. authpi_admin/generated/scopes/user_scope.py +102 -0
  33. authpi_admin/generated/scopes/webhook_scope.py +84 -0
  34. authpi_admin/http_client.py +305 -0
  35. authpi_admin/pagination.py +41 -0
  36. authpi_admin/py.typed +0 -0
  37. authpi_admin/user_agent.py +21 -0
  38. authpi_admin-0.3.0.dist-info/METADATA +285 -0
  39. authpi_admin-0.3.0.dist-info/RECORD +40 -0
  40. authpi_admin-0.3.0.dist-info/WHEEL +4 -0
@@ -0,0 +1,213 @@
1
+ """Generated resource class for api_keys — DO NOT EDIT."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any, AsyncIterator
6
+
7
+ from authpi_admin.http_client import HttpClient, validate_path_segment as _validate_path_segment
8
+ from authpi_admin.pagination import Page, auto_paginate
9
+ from authpi_admin.generated.models import (
10
+ UpdateApiKey,
11
+ )
12
+
13
+
14
+ class ApiKeysResource:
15
+ """Operations on api_keys."""
16
+
17
+ def __init__(self, client: HttpClient, base_path: str) -> None:
18
+ self._client = client
19
+ self._base_path = base_path
20
+
21
+ async def get(
22
+ self,
23
+ key_id: str,
24
+ *,
25
+ timeout: float | None = None,
26
+ headers: dict[str, str] | None = None,
27
+ ) -> dict[str, Any]:
28
+ """Get API Key"""
29
+ path = f"{self._base_path}/{key_id}"
30
+ resp = await self._client.request("GET", path, timeout=timeout, headers=headers)
31
+ data = resp.data
32
+ if resp.etag and isinstance(data, dict):
33
+ data["_etag"] = resp.etag
34
+ return data
35
+
36
+ async def update(
37
+ self,
38
+ key_id: str,
39
+ body: dict[str, Any],
40
+ *,
41
+ if_match: str | None = None,
42
+ idempotency_key: str | None = None,
43
+ timeout: float | None = None,
44
+ headers: dict[str, str] | None = None,
45
+ ) -> dict[str, Any]:
46
+ """Update API Key"""
47
+ path = f"{self._base_path}/{key_id}"
48
+ req_headers: dict[str, str] = {}
49
+ if headers:
50
+ req_headers.update(headers)
51
+ if if_match:
52
+ req_headers["if-match"] = if_match
53
+ if idempotency_key:
54
+ req_headers["idempotency-key"] = idempotency_key
55
+ resp = await self._client.request(
56
+ "PATCH",
57
+ path,
58
+ body=body,
59
+ timeout=timeout,
60
+ headers=req_headers or None,
61
+ )
62
+ data = resp.data
63
+ if resp.etag and isinstance(data, dict):
64
+ data["_etag"] = resp.etag
65
+ return data
66
+
67
+ async def delete(
68
+ self,
69
+ key_id: str,
70
+ *,
71
+ if_match: str | None = None,
72
+ timeout: float | None = None,
73
+ headers: dict[str, str] | None = None,
74
+ ) -> None:
75
+ """Delete API Key"""
76
+ path = f"{self._base_path}/{key_id}"
77
+ req_headers: dict[str, str] = {}
78
+ if headers:
79
+ req_headers.update(headers)
80
+ if if_match:
81
+ req_headers["if-match"] = if_match
82
+ await self._client.request(
83
+ "DELETE",
84
+ path,
85
+ timeout=timeout,
86
+ headers=req_headers or None,
87
+ )
88
+
89
+ async def block(
90
+ self,
91
+ key_id: str,
92
+ body: dict[str, Any] | None = None,
93
+ *,
94
+ if_match: str | None = None,
95
+ idempotency_key: str | None = None,
96
+ timeout: float | None = None,
97
+ headers: dict[str, str] | None = None,
98
+ ) -> dict[str, Any]:
99
+ """Block API Key"""
100
+ _validate_path_segment(key_id, "key_id")
101
+ path = f"{self._base_path}/{key_id}/block"
102
+ req_headers: dict[str, str] = {}
103
+ if headers:
104
+ req_headers.update(headers)
105
+ if if_match:
106
+ req_headers["if-match"] = if_match
107
+ if idempotency_key:
108
+ req_headers["idempotency-key"] = idempotency_key
109
+ resp = await self._client.request(
110
+ "POST",
111
+ path,
112
+ body=body,
113
+ timeout=timeout,
114
+ headers=req_headers or None,
115
+ )
116
+ data = resp.data
117
+ if resp.etag and isinstance(data, dict):
118
+ data["_etag"] = resp.etag
119
+ return data
120
+
121
+ async def revoke(
122
+ self,
123
+ key_id: str,
124
+ body: dict[str, Any] | None = None,
125
+ *,
126
+ if_match: str | None = None,
127
+ idempotency_key: str | None = None,
128
+ timeout: float | None = None,
129
+ headers: dict[str, str] | None = None,
130
+ ) -> dict[str, Any]:
131
+ """Revoke API Key"""
132
+ _validate_path_segment(key_id, "key_id")
133
+ path = f"{self._base_path}/{key_id}/revoke"
134
+ req_headers: dict[str, str] = {}
135
+ if headers:
136
+ req_headers.update(headers)
137
+ if if_match:
138
+ req_headers["if-match"] = if_match
139
+ if idempotency_key:
140
+ req_headers["idempotency-key"] = idempotency_key
141
+ resp = await self._client.request(
142
+ "POST",
143
+ path,
144
+ body=body,
145
+ timeout=timeout,
146
+ headers=req_headers or None,
147
+ )
148
+ data = resp.data
149
+ if resp.etag and isinstance(data, dict):
150
+ data["_etag"] = resp.etag
151
+ return data
152
+
153
+ async def rotate(
154
+ self,
155
+ key_id: str,
156
+ body: dict[str, Any] | None = None,
157
+ *,
158
+ if_match: str | None = None,
159
+ idempotency_key: str | None = None,
160
+ timeout: float | None = None,
161
+ headers: dict[str, str] | None = None,
162
+ ) -> dict[str, Any]:
163
+ """Rotate API Key Secret"""
164
+ _validate_path_segment(key_id, "key_id")
165
+ path = f"{self._base_path}/{key_id}/rotate"
166
+ req_headers: dict[str, str] = {}
167
+ if headers:
168
+ req_headers.update(headers)
169
+ if if_match:
170
+ req_headers["if-match"] = if_match
171
+ if idempotency_key:
172
+ req_headers["idempotency-key"] = idempotency_key
173
+ resp = await self._client.request(
174
+ "POST",
175
+ path,
176
+ body=body,
177
+ timeout=timeout,
178
+ headers=req_headers or None,
179
+ )
180
+ data = resp.data
181
+ if resp.etag and isinstance(data, dict):
182
+ data["_etag"] = resp.etag
183
+ return data
184
+
185
+ async def unblock(
186
+ self,
187
+ key_id: str,
188
+ *,
189
+ if_match: str | None = None,
190
+ idempotency_key: str | None = None,
191
+ timeout: float | None = None,
192
+ headers: dict[str, str] | None = None,
193
+ ) -> dict[str, Any]:
194
+ """Unblock API Key"""
195
+ _validate_path_segment(key_id, "key_id")
196
+ path = f"{self._base_path}/{key_id}/unblock"
197
+ req_headers: dict[str, str] = {}
198
+ if headers:
199
+ req_headers.update(headers)
200
+ if if_match:
201
+ req_headers["if-match"] = if_match
202
+ if idempotency_key:
203
+ req_headers["idempotency-key"] = idempotency_key
204
+ resp = await self._client.request(
205
+ "POST",
206
+ path,
207
+ timeout=timeout,
208
+ headers=req_headers or None,
209
+ )
210
+ data = resp.data
211
+ if resp.etag and isinstance(data, dict):
212
+ data["_etag"] = resp.etag
213
+ return data
@@ -0,0 +1,109 @@
1
+ """Generated resource class for approvals — DO NOT EDIT."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any, AsyncIterator
6
+
7
+ from authpi_admin.http_client import HttpClient, validate_path_segment as _validate_path_segment
8
+ from authpi_admin.pagination import Page, auto_paginate
9
+
10
+
11
+ class ApprovalsResource:
12
+ """Operations on approvals."""
13
+
14
+ def __init__(self, client: HttpClient, base_path: str) -> None:
15
+ self._client = client
16
+ self._base_path = base_path
17
+
18
+ async def list(
19
+ self,
20
+ limit: int | None = None,
21
+ cursor: str | None = None,
22
+ *,
23
+ timeout: float | None = None,
24
+ headers: dict[str, str] | None = None,
25
+ ) -> Page[dict[str, Any]]:
26
+ """List Pending Approvals"""
27
+ query: dict[str, Any] = {}
28
+ if limit is not None:
29
+ query["limit"] = limit
30
+ if cursor is not None:
31
+ query["cursor"] = cursor
32
+ resp = await self._client.request(
33
+ "GET",
34
+ self._base_path,
35
+ query=query or None,
36
+ timeout=timeout,
37
+ headers=headers,
38
+ )
39
+ data = resp.data
40
+ items = data.get("data", []) if data else []
41
+ return Page(
42
+ data=items,
43
+ has_more=data.get("has_more", False) if data else False,
44
+ next_cursor=data.get("next_cursor") if data else None,
45
+ )
46
+
47
+ async def list_all(
48
+ self,
49
+ limit: int | None = None,
50
+ *,
51
+ timeout: float | None = None,
52
+ headers: dict[str, str] | None = None,
53
+ ) -> AsyncIterator[dict[str, Any]]:
54
+ """Auto-paginating iterator over all approvals."""
55
+ async def _fetch(cursor: str | None = None) -> Page[dict[str, Any]]:
56
+ return await self.list(
57
+ limit=limit,
58
+ cursor=cursor,
59
+ timeout=timeout,
60
+ headers=headers,
61
+ )
62
+ async for item in auto_paginate(_fetch):
63
+ yield item
64
+
65
+ async def get(
66
+ self,
67
+ resource_id: str,
68
+ *,
69
+ timeout: float | None = None,
70
+ headers: dict[str, str] | None = None,
71
+ ) -> dict[str, Any]:
72
+ """Get Approval Details"""
73
+ path = f"{self._base_path}/{resource_id}"
74
+ resp = await self._client.request("GET", path, timeout=timeout, headers=headers)
75
+ data = resp.data
76
+ if resp.etag and isinstance(data, dict):
77
+ data["_etag"] = resp.etag
78
+ return data
79
+
80
+ async def update(
81
+ self,
82
+ resource_id: str,
83
+ body: dict[str, Any],
84
+ *,
85
+ if_match: str | None = None,
86
+ idempotency_key: str | None = None,
87
+ timeout: float | None = None,
88
+ headers: dict[str, str] | None = None,
89
+ ) -> dict[str, Any]:
90
+ """Update Approval"""
91
+ path = f"{self._base_path}/{resource_id}"
92
+ req_headers: dict[str, str] = {}
93
+ if headers:
94
+ req_headers.update(headers)
95
+ if if_match:
96
+ req_headers["if-match"] = if_match
97
+ if idempotency_key:
98
+ req_headers["idempotency-key"] = idempotency_key
99
+ resp = await self._client.request(
100
+ "PATCH",
101
+ path,
102
+ body=body,
103
+ timeout=timeout,
104
+ headers=req_headers or None,
105
+ )
106
+ data = resp.data
107
+ if resp.etag and isinstance(data, dict):
108
+ data["_etag"] = resp.etag
109
+ return data
@@ -0,0 +1,148 @@
1
+ """Generated resource class for auth_methods — DO NOT EDIT."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any, AsyncIterator
6
+
7
+ from authpi_admin.http_client import HttpClient, validate_path_segment as _validate_path_segment
8
+ from authpi_admin.pagination import Page, auto_paginate
9
+
10
+
11
+ class AuthMethodsResource:
12
+ """Operations on auth_methods."""
13
+
14
+ def __init__(self, client: HttpClient, base_path: str) -> None:
15
+ self._client = client
16
+ self._base_path = base_path
17
+
18
+ async def list(
19
+ self,
20
+ *,
21
+ timeout: float | None = None,
22
+ headers: dict[str, str] | None = None,
23
+ ) -> Page[dict[str, Any]]:
24
+ """List Auth Methods"""
25
+ query: dict[str, Any] = {}
26
+ resp = await self._client.request(
27
+ "GET",
28
+ self._base_path,
29
+ query=query or None,
30
+ timeout=timeout,
31
+ headers=headers,
32
+ )
33
+ data = resp.data
34
+ items = data.get("data", []) if data else []
35
+ return Page(
36
+ data=items,
37
+ has_more=data.get("has_more", False) if data else False,
38
+ next_cursor=data.get("next_cursor") if data else None,
39
+ )
40
+
41
+ async def list_all(
42
+ self,
43
+ *,
44
+ timeout: float | None = None,
45
+ headers: dict[str, str] | None = None,
46
+ ) -> AsyncIterator[dict[str, Any]]:
47
+ """Auto-paginating iterator over all auth_methods."""
48
+ async def _fetch(cursor: str | None = None) -> Page[dict[str, Any]]:
49
+ return await self.list(
50
+ timeout=timeout,
51
+ headers=headers,
52
+ )
53
+ async for item in auto_paginate(_fetch):
54
+ yield item
55
+
56
+ async def create(
57
+ self,
58
+ body: dict[str, Any],
59
+ *,
60
+ idempotency_key: str | None = None,
61
+ timeout: float | None = None,
62
+ headers: dict[str, str] | None = None,
63
+ ) -> dict[str, Any]:
64
+ """Create Auth Method"""
65
+ req_headers: dict[str, str] = {}
66
+ if headers:
67
+ req_headers.update(headers)
68
+ if idempotency_key:
69
+ req_headers["idempotency-key"] = idempotency_key
70
+ resp = await self._client.request(
71
+ "POST",
72
+ self._base_path,
73
+ body=body,
74
+ timeout=timeout,
75
+ headers=req_headers or None,
76
+ )
77
+ data = resp.data
78
+ if resp.etag and isinstance(data, dict):
79
+ data["_etag"] = resp.etag
80
+ return data
81
+
82
+ async def get(
83
+ self,
84
+ method_id: str,
85
+ *,
86
+ timeout: float | None = None,
87
+ headers: dict[str, str] | None = None,
88
+ ) -> dict[str, Any]:
89
+ """Get Auth Method"""
90
+ path = f"{self._base_path}/{method_id}"
91
+ resp = await self._client.request("GET", path, timeout=timeout, headers=headers)
92
+ data = resp.data
93
+ if resp.etag and isinstance(data, dict):
94
+ data["_etag"] = resp.etag
95
+ return data
96
+
97
+ async def update(
98
+ self,
99
+ method_id: str,
100
+ body: dict[str, Any],
101
+ *,
102
+ if_match: str | None = None,
103
+ idempotency_key: str | None = None,
104
+ timeout: float | None = None,
105
+ headers: dict[str, str] | None = None,
106
+ ) -> dict[str, Any]:
107
+ """Update Auth Method"""
108
+ path = f"{self._base_path}/{method_id}"
109
+ req_headers: dict[str, str] = {}
110
+ if headers:
111
+ req_headers.update(headers)
112
+ if if_match:
113
+ req_headers["if-match"] = if_match
114
+ if idempotency_key:
115
+ req_headers["idempotency-key"] = idempotency_key
116
+ resp = await self._client.request(
117
+ "PATCH",
118
+ path,
119
+ body=body,
120
+ timeout=timeout,
121
+ headers=req_headers or None,
122
+ )
123
+ data = resp.data
124
+ if resp.etag and isinstance(data, dict):
125
+ data["_etag"] = resp.etag
126
+ return data
127
+
128
+ async def delete(
129
+ self,
130
+ method_id: str,
131
+ *,
132
+ if_match: str | None = None,
133
+ timeout: float | None = None,
134
+ headers: dict[str, str] | None = None,
135
+ ) -> None:
136
+ """Delete Auth Method"""
137
+ path = f"{self._base_path}/{method_id}"
138
+ req_headers: dict[str, str] = {}
139
+ if headers:
140
+ req_headers.update(headers)
141
+ if if_match:
142
+ req_headers["if-match"] = if_match
143
+ await self._client.request(
144
+ "DELETE",
145
+ path,
146
+ timeout=timeout,
147
+ headers=req_headers or None,
148
+ )
@@ -0,0 +1,197 @@
1
+ """Generated resource class for clients — DO NOT EDIT."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any, AsyncIterator
6
+
7
+ from authpi_admin.http_client import HttpClient, validate_path_segment as _validate_path_segment
8
+ from authpi_admin.pagination import Page, auto_paginate
9
+
10
+
11
+ class ClientsResource:
12
+ """Operations on clients."""
13
+
14
+ def __init__(self, client: HttpClient, base_path: str) -> None:
15
+ self._client = client
16
+ self._base_path = base_path
17
+
18
+ async def list(
19
+ self,
20
+ limit: int | None = None,
21
+ cursor: str | None = None,
22
+ status: str | None = None,
23
+ name: str | None = None,
24
+ *,
25
+ timeout: float | None = None,
26
+ headers: dict[str, str] | None = None,
27
+ ) -> Page[dict[str, Any]]:
28
+ """List Clients"""
29
+ query: dict[str, Any] = {}
30
+ if limit is not None:
31
+ query["limit"] = limit
32
+ if cursor is not None:
33
+ query["cursor"] = cursor
34
+ if status is not None:
35
+ query["status"] = status
36
+ if name is not None:
37
+ query["name"] = name
38
+ resp = await self._client.request(
39
+ "GET",
40
+ self._base_path,
41
+ query=query or None,
42
+ timeout=timeout,
43
+ headers=headers,
44
+ )
45
+ data = resp.data
46
+ items = data.get("data", []) if data else []
47
+ return Page(
48
+ data=items,
49
+ has_more=data.get("has_more", False) if data else False,
50
+ next_cursor=data.get("next_cursor") if data else None,
51
+ )
52
+
53
+ async def list_all(
54
+ self,
55
+ limit: int | None = None,
56
+ status: str | None = None,
57
+ name: str | None = None,
58
+ *,
59
+ timeout: float | None = None,
60
+ headers: dict[str, str] | None = None,
61
+ ) -> AsyncIterator[dict[str, Any]]:
62
+ """Auto-paginating iterator over all clients."""
63
+ async def _fetch(cursor: str | None = None) -> Page[dict[str, Any]]:
64
+ return await self.list(
65
+ limit=limit,
66
+ status=status,
67
+ name=name,
68
+ cursor=cursor,
69
+ timeout=timeout,
70
+ headers=headers,
71
+ )
72
+ async for item in auto_paginate(_fetch):
73
+ yield item
74
+
75
+ async def create(
76
+ self,
77
+ body: dict[str, Any],
78
+ *,
79
+ idempotency_key: str | None = None,
80
+ timeout: float | None = None,
81
+ headers: dict[str, str] | None = None,
82
+ ) -> dict[str, Any]:
83
+ """Create Client"""
84
+ req_headers: dict[str, str] = {}
85
+ if headers:
86
+ req_headers.update(headers)
87
+ if idempotency_key:
88
+ req_headers["idempotency-key"] = idempotency_key
89
+ resp = await self._client.request(
90
+ "POST",
91
+ self._base_path,
92
+ body=body,
93
+ timeout=timeout,
94
+ headers=req_headers or None,
95
+ )
96
+ data = resp.data
97
+ if resp.etag and isinstance(data, dict):
98
+ data["_etag"] = resp.etag
99
+ return data
100
+
101
+ async def get(
102
+ self,
103
+ client_id: str,
104
+ *,
105
+ timeout: float | None = None,
106
+ headers: dict[str, str] | None = None,
107
+ ) -> dict[str, Any]:
108
+ """Get Client"""
109
+ path = f"{self._base_path}/{client_id}"
110
+ resp = await self._client.request("GET", path, timeout=timeout, headers=headers)
111
+ data = resp.data
112
+ if resp.etag and isinstance(data, dict):
113
+ data["_etag"] = resp.etag
114
+ return data
115
+
116
+ async def update(
117
+ self,
118
+ client_id: str,
119
+ body: dict[str, Any],
120
+ *,
121
+ if_match: str | None = None,
122
+ idempotency_key: str | None = None,
123
+ timeout: float | None = None,
124
+ headers: dict[str, str] | None = None,
125
+ ) -> dict[str, Any]:
126
+ """Update Client"""
127
+ path = f"{self._base_path}/{client_id}"
128
+ req_headers: dict[str, str] = {}
129
+ if headers:
130
+ req_headers.update(headers)
131
+ if if_match:
132
+ req_headers["if-match"] = if_match
133
+ if idempotency_key:
134
+ req_headers["idempotency-key"] = idempotency_key
135
+ resp = await self._client.request(
136
+ "PATCH",
137
+ path,
138
+ body=body,
139
+ timeout=timeout,
140
+ headers=req_headers or None,
141
+ )
142
+ data = resp.data
143
+ if resp.etag and isinstance(data, dict):
144
+ data["_etag"] = resp.etag
145
+ return data
146
+
147
+ async def delete(
148
+ self,
149
+ client_id: str,
150
+ *,
151
+ if_match: str | None = None,
152
+ timeout: float | None = None,
153
+ headers: dict[str, str] | None = None,
154
+ ) -> None:
155
+ """Delete Client"""
156
+ path = f"{self._base_path}/{client_id}"
157
+ req_headers: dict[str, str] = {}
158
+ if headers:
159
+ req_headers.update(headers)
160
+ if if_match:
161
+ req_headers["if-match"] = if_match
162
+ await self._client.request(
163
+ "DELETE",
164
+ path,
165
+ timeout=timeout,
166
+ headers=req_headers or None,
167
+ )
168
+
169
+ async def rotate_secret(
170
+ self,
171
+ client_id: str,
172
+ *,
173
+ if_match: str | None = None,
174
+ idempotency_key: str | None = None,
175
+ timeout: float | None = None,
176
+ headers: dict[str, str] | None = None,
177
+ ) -> dict[str, Any]:
178
+ """Rotate Client Secret"""
179
+ _validate_path_segment(client_id, "client_id")
180
+ path = f"{self._base_path}/{client_id}/secret/rotate"
181
+ req_headers: dict[str, str] = {}
182
+ if headers:
183
+ req_headers.update(headers)
184
+ if if_match:
185
+ req_headers["if-match"] = if_match
186
+ if idempotency_key:
187
+ req_headers["idempotency-key"] = idempotency_key
188
+ resp = await self._client.request(
189
+ "POST",
190
+ path,
191
+ timeout=timeout,
192
+ headers=req_headers or None,
193
+ )
194
+ data = resp.data
195
+ if resp.etag and isinstance(data, dict):
196
+ data["_etag"] = resp.etag
197
+ return data