domani 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- domani-0.1.0/.gitignore +71 -0
- domani-0.1.0/PKG-INFO +66 -0
- domani-0.1.0/README.md +51 -0
- domani-0.1.0/domani/__init__.py +763 -0
- domani-0.1.0/e2e_check.py +72 -0
- domani-0.1.0/pyproject.toml +25 -0
domani-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
|
2
|
+
|
|
3
|
+
# dependencies
|
|
4
|
+
/node_modules
|
|
5
|
+
/.pnp
|
|
6
|
+
.pnp.*
|
|
7
|
+
.yarn/*
|
|
8
|
+
!.yarn/patches
|
|
9
|
+
!.yarn/plugins
|
|
10
|
+
!.yarn/releases
|
|
11
|
+
!.yarn/versions
|
|
12
|
+
|
|
13
|
+
# testing
|
|
14
|
+
/coverage
|
|
15
|
+
|
|
16
|
+
# next.js
|
|
17
|
+
/.next/
|
|
18
|
+
/out/
|
|
19
|
+
|
|
20
|
+
# production
|
|
21
|
+
/build
|
|
22
|
+
/dist
|
|
23
|
+
|
|
24
|
+
# misc
|
|
25
|
+
.DS_Store
|
|
26
|
+
*.pem
|
|
27
|
+
|
|
28
|
+
# debug
|
|
29
|
+
npm-debug.log*
|
|
30
|
+
yarn-debug.log*
|
|
31
|
+
yarn-error.log*
|
|
32
|
+
.pnpm-debug.log*
|
|
33
|
+
|
|
34
|
+
# env files
|
|
35
|
+
.env
|
|
36
|
+
.env.local
|
|
37
|
+
.env.*.local
|
|
38
|
+
.env.production
|
|
39
|
+
.env.preview
|
|
40
|
+
*.bak
|
|
41
|
+
|
|
42
|
+
# cli build
|
|
43
|
+
cli/dist
|
|
44
|
+
cli/node_modules
|
|
45
|
+
cli/.next
|
|
46
|
+
public/cli/domainer.js
|
|
47
|
+
public/cli/domainer.js.sha256
|
|
48
|
+
|
|
49
|
+
# sdk build artifacts (generated SOURCES are committed; these are not)
|
|
50
|
+
sdk/openapi.json
|
|
51
|
+
sdk/ts/dist
|
|
52
|
+
sdk/ts/node_modules
|
|
53
|
+
sdk/ts/package-lock.json
|
|
54
|
+
sdk/python/dist
|
|
55
|
+
sdk/python/build
|
|
56
|
+
sdk/**/__pycache__
|
|
57
|
+
sdk/**/*.egg-info
|
|
58
|
+
|
|
59
|
+
# generated skill
|
|
60
|
+
public/skill.md
|
|
61
|
+
public/references/
|
|
62
|
+
|
|
63
|
+
# vercel
|
|
64
|
+
.vercel
|
|
65
|
+
|
|
66
|
+
# typescript
|
|
67
|
+
*.tsbuildinfo
|
|
68
|
+
next-env.d.ts
|
|
69
|
+
|
|
70
|
+
/src/generated/prisma
|
|
71
|
+
.env*.local
|
domani-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: domani
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Official Python SDK for the domani API - domains, email, and identity for AI agents.
|
|
5
|
+
Project-URL: Homepage, https://domani.run
|
|
6
|
+
Project-URL: Repository, https://github.com/gwendall/domani
|
|
7
|
+
Author: domani
|
|
8
|
+
License: MIT
|
|
9
|
+
Keywords: agents,api,domains,domani,email,mcp,sdk
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Requires-Python: >=3.8
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
|
|
16
|
+
# domani
|
|
17
|
+
|
|
18
|
+
Official Python SDK for the [domani](https://domani.run) API - domains, email, and identity for AI agents. Typed, generated from the OpenAPI spec, standard-library only (no dependencies).
|
|
19
|
+
|
|
20
|
+
## Install
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pip install domani
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Requires Python 3.8+.
|
|
27
|
+
|
|
28
|
+
## Quickstart
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
from domani import Domani, DomaniError
|
|
32
|
+
|
|
33
|
+
domani = Domani(api_key="domani_sk_...")
|
|
34
|
+
|
|
35
|
+
# Search availability + pricing
|
|
36
|
+
results = domani.search(query={"q": "myagent.com"})
|
|
37
|
+
|
|
38
|
+
# Give an agent an inbox
|
|
39
|
+
domani.create_mailbox_by_address(body={"address": "hi@myagent.com"})
|
|
40
|
+
|
|
41
|
+
# Send mail
|
|
42
|
+
domani.send_email_by_address(
|
|
43
|
+
"hi@myagent.com",
|
|
44
|
+
body={"to": "user@example.com", "subject": "Hello", "text": "Sent from my domani mailbox."},
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
# Errors carry the API's structured code + hint
|
|
48
|
+
try:
|
|
49
|
+
domani.send_email_by_address("hi@myagent.com", body={"to": "bounced@example.com", "text": "..."})
|
|
50
|
+
except DomaniError as e:
|
|
51
|
+
print(e.status, e.code, e.hint) # e.g. 400 RECIPIENTS_SUPPRESSED
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## API
|
|
55
|
+
|
|
56
|
+
`Domani(api_key, base_url=..., timeout=...)` exposes one method per API operation (named after its operationId in snake_case). Path parameters are positional; request bodies (`body=`) and query objects (`query=`) are keyword arguments. Every method returns the parsed JSON response and raises `DomaniError` (`status`, `code`, `hint`, `documentation_url`) on non-2xx.
|
|
57
|
+
|
|
58
|
+
Full endpoint reference: <https://domani.run/docs> · OpenAPI spec: <https://domani.run/.well-known/openapi.json>
|
|
59
|
+
|
|
60
|
+
## Generated
|
|
61
|
+
|
|
62
|
+
`domani/__init__.py` is generated from the domani OpenAPI spec - do not edit it by hand. It is regenerated whenever the API surface changes.
|
|
63
|
+
|
|
64
|
+
## License
|
|
65
|
+
|
|
66
|
+
MIT
|
domani-0.1.0/README.md
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# domani
|
|
2
|
+
|
|
3
|
+
Official Python SDK for the [domani](https://domani.run) API - domains, email, and identity for AI agents. Typed, generated from the OpenAPI spec, standard-library only (no dependencies).
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install domani
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Requires Python 3.8+.
|
|
12
|
+
|
|
13
|
+
## Quickstart
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
from domani import Domani, DomaniError
|
|
17
|
+
|
|
18
|
+
domani = Domani(api_key="domani_sk_...")
|
|
19
|
+
|
|
20
|
+
# Search availability + pricing
|
|
21
|
+
results = domani.search(query={"q": "myagent.com"})
|
|
22
|
+
|
|
23
|
+
# Give an agent an inbox
|
|
24
|
+
domani.create_mailbox_by_address(body={"address": "hi@myagent.com"})
|
|
25
|
+
|
|
26
|
+
# Send mail
|
|
27
|
+
domani.send_email_by_address(
|
|
28
|
+
"hi@myagent.com",
|
|
29
|
+
body={"to": "user@example.com", "subject": "Hello", "text": "Sent from my domani mailbox."},
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
# Errors carry the API's structured code + hint
|
|
33
|
+
try:
|
|
34
|
+
domani.send_email_by_address("hi@myagent.com", body={"to": "bounced@example.com", "text": "..."})
|
|
35
|
+
except DomaniError as e:
|
|
36
|
+
print(e.status, e.code, e.hint) # e.g. 400 RECIPIENTS_SUPPRESSED
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## API
|
|
40
|
+
|
|
41
|
+
`Domani(api_key, base_url=..., timeout=...)` exposes one method per API operation (named after its operationId in snake_case). Path parameters are positional; request bodies (`body=`) and query objects (`query=`) are keyword arguments. Every method returns the parsed JSON response and raises `DomaniError` (`status`, `code`, `hint`, `documentation_url`) on non-2xx.
|
|
42
|
+
|
|
43
|
+
Full endpoint reference: <https://domani.run/docs> · OpenAPI spec: <https://domani.run/.well-known/openapi.json>
|
|
44
|
+
|
|
45
|
+
## Generated
|
|
46
|
+
|
|
47
|
+
`domani/__init__.py` is generated from the domani OpenAPI spec - do not edit it by hand. It is regenerated whenever the API surface changes.
|
|
48
|
+
|
|
49
|
+
## License
|
|
50
|
+
|
|
51
|
+
MIT
|
|
@@ -0,0 +1,763 @@
|
|
|
1
|
+
# AUTO-GENERATED from the domani OpenAPI spec by scripts/generate-sdk.ts.
|
|
2
|
+
# Do not edit by hand - re-run `npx tsx scripts/generate-sdk.ts`.
|
|
3
|
+
"""Typed Python client for the domani API."""
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import json as _json
|
|
7
|
+
import urllib.parse
|
|
8
|
+
import urllib.request
|
|
9
|
+
import urllib.error
|
|
10
|
+
from typing import Any, Dict, List, Literal, Optional, Union
|
|
11
|
+
from urllib.parse import quote
|
|
12
|
+
|
|
13
|
+
try: # Python 3.8+
|
|
14
|
+
from typing import TypedDict
|
|
15
|
+
except ImportError: # pragma: no cover
|
|
16
|
+
from typing_extensions import TypedDict # type: ignore
|
|
17
|
+
|
|
18
|
+
DEFAULT_BASE_URL = "https://domani.run"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class DomaniError(Exception):
|
|
22
|
+
"""Structured API error - mirrors the apiError() shape returned by the API."""
|
|
23
|
+
|
|
24
|
+
def __init__(self, status: int, body: Any):
|
|
25
|
+
self.status = status
|
|
26
|
+
if isinstance(body, dict):
|
|
27
|
+
self.code = body.get("code", "ERROR")
|
|
28
|
+
self.hint = body.get("hint")
|
|
29
|
+
self.documentation_url = body.get("documentation_url")
|
|
30
|
+
message = body.get("error") or body.get("message") or f"Request failed with status {status}"
|
|
31
|
+
else:
|
|
32
|
+
self.code = "ERROR"
|
|
33
|
+
self.hint = None
|
|
34
|
+
self.documentation_url = None
|
|
35
|
+
message = f"Request failed with status {status}"
|
|
36
|
+
super().__init__(message)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
# ─── Types ──────────────────────────────────────────
|
|
40
|
+
|
|
41
|
+
class Error(TypedDict, total=False):
|
|
42
|
+
error: str
|
|
43
|
+
code: Literal["MISSING_API_KEY", "INVALID_API_KEY", "EXPIRED_API_KEY", "MISSING_PARAMETER", "INVALID_DOMAIN", "PAYMENT_REQUIRED", "DOMAIN_UNAVAILABLE", "RATE_LIMIT_EXCEEDED", "USER_EXISTS", "NOT_FOUND", "UNKNOWN_PROVIDER", "UNKNOWN_METHOD", "TARGET_REQUIRED", "INVALID_PARAMETER", "CONTACT_REQUIRED", "INVALID_CONTACT", "INSUFFICIENT_SCOPE", "SCOPE_ESCALATION", "DOMAIN_EXISTS", "DOMAIN_NOT_ACTIVE", "UNSUPPORTED_TLD", "NOT_AVAILABLE", "ALREADY_REGISTERED", "TRANSFER_NOT_ELIGIBLE", "ALREADY_LISTED", "LISTING_SOLD", "SELF_PURCHASE", "DESCRIPTION_TOO_LONG", "DEAL_NOT_FOUND", "INVALID_DEAL_STATE", "EPP_ATTEMPTS_EXCEEDED", "ACTIVE_DEAL_EXISTS"]
|
|
44
|
+
hint: str
|
|
45
|
+
fix_command: str
|
|
46
|
+
documentation_url: str
|
|
47
|
+
setup_url: str
|
|
48
|
+
fix_url: str
|
|
49
|
+
retry_after: int
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class SearchResult(TypedDict, total=False):
|
|
53
|
+
available: bool
|
|
54
|
+
domain: str
|
|
55
|
+
price: float
|
|
56
|
+
currency: str
|
|
57
|
+
error: bool
|
|
58
|
+
for_sale: Dict[str, Any]
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class TldInfo(TypedDict, total=False):
|
|
62
|
+
tld: str
|
|
63
|
+
registration: float
|
|
64
|
+
renewal: float
|
|
65
|
+
transfer: float
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class DnsRecord(TypedDict, total=False):
|
|
69
|
+
type: Literal["A", "AAAA", "CNAME", "MX", "TXT", "NS", "SRV", "CAA"]
|
|
70
|
+
name: str
|
|
71
|
+
value: str
|
|
72
|
+
ttl: int
|
|
73
|
+
priority: int
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class DomainRecord(TypedDict, total=False):
|
|
77
|
+
domain: str
|
|
78
|
+
status: Literal["active", "expired", "pending"]
|
|
79
|
+
auto_renew: bool
|
|
80
|
+
purchased_at: str
|
|
81
|
+
expires_at: str
|
|
82
|
+
email_enabled: bool
|
|
83
|
+
email_verified: bool
|
|
84
|
+
email_verified_at: Optional[str]
|
|
85
|
+
mailbox_count: int
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class Backorder(TypedDict, total=False):
|
|
89
|
+
id: str
|
|
90
|
+
domain: str
|
|
91
|
+
status: Literal["watching", "caught", "failed", "cancelled", "expired"]
|
|
92
|
+
payment_method: Literal["card", "balance"]
|
|
93
|
+
max_price: float
|
|
94
|
+
currency: str
|
|
95
|
+
domain_expires: Optional[str]
|
|
96
|
+
last_checked: Optional[str]
|
|
97
|
+
attempts: int
|
|
98
|
+
caught_at: Optional[str]
|
|
99
|
+
registered_domain_id: Optional[str]
|
|
100
|
+
failure_reason: Optional[str]
|
|
101
|
+
expires_at: str
|
|
102
|
+
created_at: str
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class DealInvoice(TypedDict, total=False):
|
|
106
|
+
deal_id: str
|
|
107
|
+
domain: str
|
|
108
|
+
date: str
|
|
109
|
+
status: str
|
|
110
|
+
your_role: Literal["buyer", "seller"]
|
|
111
|
+
currency: str
|
|
112
|
+
line_items: List[Dict[str, Any]]
|
|
113
|
+
total: float
|
|
114
|
+
commission_rate: float
|
|
115
|
+
commission_amount: float
|
|
116
|
+
payment_method: str
|
|
117
|
+
payout_method: Literal["bank", "balance"]
|
|
118
|
+
payout_status: Literal["paid", "credited", "pending"]
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class BrokerRequest(TypedDict, total=False):
|
|
122
|
+
id: str
|
|
123
|
+
domain: str
|
|
124
|
+
status: Literal["sourcing", "contacted", "negotiating", "agreed", "completed", "no_contact", "declined", "expired", "cancelled"]
|
|
125
|
+
max_budget: Optional[float]
|
|
126
|
+
contacted: bool
|
|
127
|
+
negotiation_id: Optional[str]
|
|
128
|
+
failure_reason: Optional[str]
|
|
129
|
+
expires_at: str
|
|
130
|
+
created_at: str
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
class Negotiation(TypedDict, total=False):
|
|
134
|
+
id: str
|
|
135
|
+
domain: str
|
|
136
|
+
status: Literal["open", "agreed", "completed", "declined", "expired", "cancelled"]
|
|
137
|
+
your_role: Literal["buyer", "seller"]
|
|
138
|
+
your_turn: bool
|
|
139
|
+
current_offer: float
|
|
140
|
+
agreed_price: Optional[float]
|
|
141
|
+
list_price: float
|
|
142
|
+
expires_at: str
|
|
143
|
+
deal_id: Optional[str]
|
|
144
|
+
created_at: str
|
|
145
|
+
offers: List[Dict[str, Any]]
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
class DomainDetail(TypedDict, total=False):
|
|
149
|
+
domain: str
|
|
150
|
+
status: Literal["active", "expired", "pending"]
|
|
151
|
+
auto_renew: bool
|
|
152
|
+
purchased_at: str
|
|
153
|
+
expires_at: str
|
|
154
|
+
days_until_expiry: int
|
|
155
|
+
payment_method: Literal["card", "x402", "crypto"]
|
|
156
|
+
payment_tx: Optional[str]
|
|
157
|
+
registrar: Optional[Dict[str, Any]]
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
class TokenInfo(TypedDict, total=False):
|
|
161
|
+
id: str
|
|
162
|
+
name: str
|
|
163
|
+
key: str
|
|
164
|
+
last_used: Optional[str]
|
|
165
|
+
expires_at: Optional[str]
|
|
166
|
+
expired: bool
|
|
167
|
+
scopes: List[str]
|
|
168
|
+
created_at: str
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
class Contact(TypedDict, total=False):
|
|
172
|
+
name: Optional[str]
|
|
173
|
+
organization: Optional[str]
|
|
174
|
+
email: Optional[str]
|
|
175
|
+
phone: Optional[str]
|
|
176
|
+
fax: Optional[str]
|
|
177
|
+
street: Optional[str]
|
|
178
|
+
city: Optional[str]
|
|
179
|
+
state: Optional[str]
|
|
180
|
+
postal_code: Optional[str]
|
|
181
|
+
country: Optional[str]
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
class RegistrantContact(TypedDict, total=False):
|
|
185
|
+
first_name: str
|
|
186
|
+
last_name: str
|
|
187
|
+
org_name: Optional[str]
|
|
188
|
+
address1: str
|
|
189
|
+
address2: Optional[str]
|
|
190
|
+
city: str
|
|
191
|
+
state: str
|
|
192
|
+
postal_code: str
|
|
193
|
+
country: str
|
|
194
|
+
phone: str
|
|
195
|
+
email: str
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
class AccountInfo(TypedDict, total=False):
|
|
199
|
+
id: str
|
|
200
|
+
email: str
|
|
201
|
+
has_payment_method: bool
|
|
202
|
+
has_contact: bool
|
|
203
|
+
contact: "RegistrantContact"
|
|
204
|
+
referral_code: str
|
|
205
|
+
referral_rate: int
|
|
206
|
+
balance: float
|
|
207
|
+
domain_count: int
|
|
208
|
+
token_count: int
|
|
209
|
+
created_at: str
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
# ─── Client ─────────────────────────────────────────
|
|
213
|
+
|
|
214
|
+
class Domani:
|
|
215
|
+
"""domani API client. Get an API key at https://domani.run."""
|
|
216
|
+
|
|
217
|
+
def __init__(self, api_key: str, base_url: str = DEFAULT_BASE_URL, timeout: float = 30.0):
|
|
218
|
+
if not api_key:
|
|
219
|
+
raise ValueError("Domani: api_key is required")
|
|
220
|
+
self.api_key = api_key
|
|
221
|
+
self.base_url = base_url.rstrip("/")
|
|
222
|
+
self.timeout = timeout
|
|
223
|
+
|
|
224
|
+
def _request(self, method: str, path: str, json: Any = None, query: Optional[Dict[str, Any]] = None) -> Any:
|
|
225
|
+
url = self.base_url + path
|
|
226
|
+
if query:
|
|
227
|
+
pairs = []
|
|
228
|
+
for k, v in query.items():
|
|
229
|
+
if v is None:
|
|
230
|
+
continue
|
|
231
|
+
if isinstance(v, (list, tuple)):
|
|
232
|
+
pairs.extend((k, str(item)) for item in v)
|
|
233
|
+
else:
|
|
234
|
+
pairs.append((k, str(v)))
|
|
235
|
+
if pairs:
|
|
236
|
+
url += ("&" if "?" in url else "?") + urllib.parse.urlencode(pairs)
|
|
237
|
+
headers = {"Authorization": f"Bearer {self.api_key}"}
|
|
238
|
+
data = None
|
|
239
|
+
if json is not None:
|
|
240
|
+
headers["Content-Type"] = "application/json"
|
|
241
|
+
data = _json.dumps(json).encode("utf-8")
|
|
242
|
+
req = urllib.request.Request(url, data=data, headers=headers, method=method)
|
|
243
|
+
try:
|
|
244
|
+
with urllib.request.urlopen(req, timeout=self.timeout) as resp:
|
|
245
|
+
text = resp.read().decode("utf-8")
|
|
246
|
+
return _json.loads(text) if text else None
|
|
247
|
+
except urllib.error.HTTPError as e:
|
|
248
|
+
text = e.read().decode("utf-8")
|
|
249
|
+
body: Any = None
|
|
250
|
+
if text:
|
|
251
|
+
try:
|
|
252
|
+
body = _json.loads(text)
|
|
253
|
+
except ValueError:
|
|
254
|
+
body = text
|
|
255
|
+
raise DomaniError(e.code, body) from None
|
|
256
|
+
|
|
257
|
+
def add_mailbox_alias(self, address: str, body: Dict[str, Any]) -> Any:
|
|
258
|
+
"""Add a mailbox alias (POST /api/emails/{address}/aliases)"""
|
|
259
|
+
return self._request("POST", f"/api/emails/{quote(str(address))}/aliases", json=body)
|
|
260
|
+
|
|
261
|
+
def add_suppression(self, body: Dict[str, Any]) -> Any:
|
|
262
|
+
"""Add a suppression (POST /api/suppressions)"""
|
|
263
|
+
return self._request("POST", f"/api/suppressions", json=body)
|
|
264
|
+
|
|
265
|
+
def buy_domain(self, body: Dict[str, Any]) -> Any:
|
|
266
|
+
"""Purchase one or more domains (POST /api/domains/buy)"""
|
|
267
|
+
return self._request("POST", f"/api/domains/buy", json=body)
|
|
268
|
+
|
|
269
|
+
def cancel_backorder(self, id: str) -> Any:
|
|
270
|
+
"""Cancel a backorder (DELETE /api/backorders/{id})"""
|
|
271
|
+
return self._request("DELETE", f"/api/backorders/{quote(str(id))}")
|
|
272
|
+
|
|
273
|
+
def cancel_broker_request(self, id: str) -> Any:
|
|
274
|
+
"""Cancel a broker request (DELETE /api/broker/{id})"""
|
|
275
|
+
return self._request("DELETE", f"/api/broker/{quote(str(id))}")
|
|
276
|
+
|
|
277
|
+
def cancel_listing(self, domain: str) -> Any:
|
|
278
|
+
"""Cancel a domain listing (DELETE /api/domains/{domain}/for-sale)"""
|
|
279
|
+
return self._request("DELETE", f"/api/domains/{quote(str(domain))}/for-sale")
|
|
280
|
+
|
|
281
|
+
def cancel_negotiation(self, id: str) -> Any:
|
|
282
|
+
"""Withdraw a negotiation (DELETE /api/negotiations/{id})"""
|
|
283
|
+
return self._request("DELETE", f"/api/negotiations/{quote(str(id))}")
|
|
284
|
+
|
|
285
|
+
def check_email(self, domain: str) -> Any:
|
|
286
|
+
"""Check email DNS health (MX, SPF, DMARC, DKIM) (GET /api/domains/{domain}/email/check)"""
|
|
287
|
+
return self._request("GET", f"/api/domains/{quote(str(domain))}/email/check")
|
|
288
|
+
|
|
289
|
+
def check_transfer_eligibility(self, query: Optional[Dict[str, Any]] = None) -> Any:
|
|
290
|
+
"""Pre-check transfer eligibility (GET /api/domains/transfer-check)"""
|
|
291
|
+
return self._request("GET", f"/api/domains/transfer-check", query=query)
|
|
292
|
+
|
|
293
|
+
def check_transfer_status(self, domain: str) -> Any:
|
|
294
|
+
"""Check inbound transfer status (GET /api/domains/{domain}/transfer-status)"""
|
|
295
|
+
return self._request("GET", f"/api/domains/{quote(str(domain))}/transfer-status")
|
|
296
|
+
|
|
297
|
+
def clear_catch_all(self, domain: str) -> Any:
|
|
298
|
+
"""Clear domain catch-all (DELETE /api/domains/{domain}/email/catch-all)"""
|
|
299
|
+
return self._request("DELETE", f"/api/domains/{quote(str(domain))}/email/catch-all")
|
|
300
|
+
|
|
301
|
+
def clear_domain_redirect(self, domain: str) -> Any:
|
|
302
|
+
"""Stop forwarding a domain (DELETE /api/domains/{domain}/redirect)"""
|
|
303
|
+
return self._request("DELETE", f"/api/domains/{quote(str(domain))}/redirect")
|
|
304
|
+
|
|
305
|
+
def connect_domain(self, domain: str, body: Dict[str, Any]) -> Any:
|
|
306
|
+
"""Connect a domain to a hosting or email provider (POST /api/domains/{domain}/connect)"""
|
|
307
|
+
return self._request("POST", f"/api/domains/{quote(str(domain))}/connect", json=body)
|
|
308
|
+
|
|
309
|
+
def create_backorder(self, body: Dict[str, Any]) -> Any:
|
|
310
|
+
"""Place a backorder on a taken domain (POST /api/backorders)"""
|
|
311
|
+
return self._request("POST", f"/api/backorders", json=body)
|
|
312
|
+
|
|
313
|
+
def create_broker_request(self, body: Dict[str, Any]) -> Any:
|
|
314
|
+
"""Request acquisition of a taken domain (POST /api/broker)"""
|
|
315
|
+
return self._request("POST", f"/api/broker", json=body)
|
|
316
|
+
|
|
317
|
+
def create_deal(self, body: Dict[str, Any]) -> Any:
|
|
318
|
+
"""Purchase an external domain (create deal) (POST /api/deals)"""
|
|
319
|
+
return self._request("POST", f"/api/deals", json=body)
|
|
320
|
+
|
|
321
|
+
def create_listing(self, domain: str, body: Dict[str, Any]) -> Any:
|
|
322
|
+
"""List a domain for sale (PUT /api/domains/{domain}/for-sale)"""
|
|
323
|
+
return self._request("PUT", f"/api/domains/{quote(str(domain))}/for-sale", json=body)
|
|
324
|
+
|
|
325
|
+
def create_mailbox(self, body: Dict[str, Any]) -> Any:
|
|
326
|
+
"""Create a mailbox on domani.run (POST /api/email)"""
|
|
327
|
+
return self._request("POST", f"/api/email", json=body)
|
|
328
|
+
|
|
329
|
+
def create_mailbox_by_address(self, body: Dict[str, Any] = None) -> Any:
|
|
330
|
+
"""Create a mailbox (POST /api/emails)"""
|
|
331
|
+
return self._request("POST", f"/api/emails", json=body)
|
|
332
|
+
|
|
333
|
+
def create_token(self, body: Dict[str, Any]) -> Any:
|
|
334
|
+
"""Create a new API token (POST /api/tokens)"""
|
|
335
|
+
return self._request("POST", f"/api/tokens", json=body)
|
|
336
|
+
|
|
337
|
+
def create_webhook(self, body: Dict[str, Any]) -> Any:
|
|
338
|
+
"""Create webhook (POST /api/webhooks)"""
|
|
339
|
+
return self._request("POST", f"/api/webhooks", json=body)
|
|
340
|
+
|
|
341
|
+
def delete_account(self) -> Any:
|
|
342
|
+
"""Delete account and all associated data (DELETE /api/me)"""
|
|
343
|
+
return self._request("DELETE", f"/api/me")
|
|
344
|
+
|
|
345
|
+
def delete_mailbox_by_address(self, address: str, body: Dict[str, Any] = None) -> Any:
|
|
346
|
+
"""Delete a mailbox (DELETE /api/emails/{address})"""
|
|
347
|
+
return self._request("DELETE", f"/api/emails/{quote(str(address))}", json=body)
|
|
348
|
+
|
|
349
|
+
def delete_message_by_address(self, address: str, id: str) -> Any:
|
|
350
|
+
"""Delete a message (DELETE /api/emails/{address}/messages/{id})"""
|
|
351
|
+
return self._request("DELETE", f"/api/emails/{quote(str(address))}/messages/{quote(str(id))}")
|
|
352
|
+
|
|
353
|
+
def delete_messages_by_address(self, address: str, body: Dict[str, Any] = None) -> Any:
|
|
354
|
+
"""Bulk delete messages (POST /api/emails/{address}/messages/delete)"""
|
|
355
|
+
return self._request("POST", f"/api/emails/{quote(str(address))}/messages/delete", json=body)
|
|
356
|
+
|
|
357
|
+
def delete_webhook(self, id: str) -> Any:
|
|
358
|
+
"""Delete webhook (DELETE /api/webhooks/{id})"""
|
|
359
|
+
return self._request("DELETE", f"/api/webhooks/{quote(str(id))}")
|
|
360
|
+
|
|
361
|
+
def dns_check(self, query: Optional[Dict[str, Any]] = None) -> Any:
|
|
362
|
+
"""Fast DNS-based domain existence check (GET /api/domains/dns-check)"""
|
|
363
|
+
return self._request("GET", f"/api/domains/dns-check", query=query)
|
|
364
|
+
|
|
365
|
+
def finalize_negotiation(self, id: str, body: Dict[str, Any] = None) -> Any:
|
|
366
|
+
"""Finalize an agreed negotiation (pay) (POST /api/negotiations/{id}/finalize)"""
|
|
367
|
+
return self._request("POST", f"/api/negotiations/{quote(str(id))}/finalize", json=body)
|
|
368
|
+
|
|
369
|
+
def forward_message_by_address(self, address: str, id: str, body: Dict[str, Any] = None) -> Any:
|
|
370
|
+
"""Forward a message (POST /api/emails/{address}/messages/{id}/forward)"""
|
|
371
|
+
return self._request("POST", f"/api/emails/{quote(str(address))}/messages/{quote(str(id))}/forward", json=body)
|
|
372
|
+
|
|
373
|
+
def get_account(self) -> Any:
|
|
374
|
+
"""Get current account details (GET /api/me)"""
|
|
375
|
+
return self._request("GET", f"/api/me")
|
|
376
|
+
|
|
377
|
+
def get_auth_code(self, domain: str) -> Any:
|
|
378
|
+
"""Get EPP auth code (GET /api/domains/{domain}/auth-code)"""
|
|
379
|
+
return self._request("GET", f"/api/domains/{quote(str(domain))}/auth-code")
|
|
380
|
+
|
|
381
|
+
def get_backorder(self, id: str) -> Any:
|
|
382
|
+
"""Get a backorder (GET /api/backorders/{id})"""
|
|
383
|
+
return self._request("GET", f"/api/backorders/{quote(str(id))}")
|
|
384
|
+
|
|
385
|
+
def get_broker_inquiry_state(self, query: Optional[Dict[str, Any]] = None) -> Any:
|
|
386
|
+
"""Read a broker inquiry's state (owner side) (GET /api/broker/respond)"""
|
|
387
|
+
return self._request("GET", f"/api/broker/respond", query=query)
|
|
388
|
+
|
|
389
|
+
def get_broker_request(self, id: str) -> Any:
|
|
390
|
+
"""Get a broker request (GET /api/broker/{id})"""
|
|
391
|
+
return self._request("GET", f"/api/broker/{quote(str(id))}")
|
|
392
|
+
|
|
393
|
+
def get_contact(self) -> Any:
|
|
394
|
+
"""Get registrant contact info (GET /api/me/contact)"""
|
|
395
|
+
return self._request("GET", f"/api/me/contact")
|
|
396
|
+
|
|
397
|
+
def get_deal(self, id: str) -> Any:
|
|
398
|
+
"""Get deal detail with timeline (GET /api/deals/{id})"""
|
|
399
|
+
return self._request("GET", f"/api/deals/{quote(str(id))}")
|
|
400
|
+
|
|
401
|
+
def get_deal_invoice(self, id: str) -> Any:
|
|
402
|
+
"""Get a deal receipt / statement (GET /api/deals/{id}/invoice)"""
|
|
403
|
+
return self._request("GET", f"/api/deals/{quote(str(id))}/invoice")
|
|
404
|
+
|
|
405
|
+
def get_dns_records(self, domain: str) -> Any:
|
|
406
|
+
"""Get DNS records for a domain you own (GET /api/domains/{domain}/dns)"""
|
|
407
|
+
return self._request("GET", f"/api/domains/{quote(str(domain))}/dns")
|
|
408
|
+
|
|
409
|
+
def get_domain_email_status(self, domain: str) -> Any:
|
|
410
|
+
"""Check email DNS status (GET /api/domains/{domain}/email/status)"""
|
|
411
|
+
return self._request("GET", f"/api/domains/{quote(str(domain))}/email/status")
|
|
412
|
+
|
|
413
|
+
def get_domain_info(self, domain: str) -> Any:
|
|
414
|
+
"""Get detailed information about a domain you own (GET /api/domains/{domain})"""
|
|
415
|
+
return self._request("GET", f"/api/domains/{quote(str(domain))}")
|
|
416
|
+
|
|
417
|
+
def get_domain_og(self, domain: str) -> Any:
|
|
418
|
+
"""Get website preview metadata for a domain (GET /api/domains/{domain}/og)"""
|
|
419
|
+
return self._request("GET", f"/api/domains/{quote(str(domain))}/og")
|
|
420
|
+
|
|
421
|
+
def get_domain_status(self, domain: str) -> Any:
|
|
422
|
+
"""Check domain health: DNS, SSL, email, expiry (GET /api/domains/{domain}/status)"""
|
|
423
|
+
return self._request("GET", f"/api/domains/{quote(str(domain))}/status")
|
|
424
|
+
|
|
425
|
+
def get_mailbox_avatar_by_address(self, address: str) -> Any:
|
|
426
|
+
"""Get mailbox avatar info (GET /api/emails/{address}/avatar)"""
|
|
427
|
+
return self._request("GET", f"/api/emails/{quote(str(address))}/avatar")
|
|
428
|
+
|
|
429
|
+
def get_mailbox_by_address(self, address: str) -> Any:
|
|
430
|
+
"""Get mailbox details (GET /api/emails/{address})"""
|
|
431
|
+
return self._request("GET", f"/api/emails/{quote(str(address))}")
|
|
432
|
+
|
|
433
|
+
def get_mailbox_webhook(self, address: str) -> Any:
|
|
434
|
+
"""Get mailbox webhook config (GET /api/emails/{address}/webhook)"""
|
|
435
|
+
return self._request("GET", f"/api/emails/{quote(str(address))}/webhook")
|
|
436
|
+
|
|
437
|
+
def get_message_by_address(self, address: str, id: str) -> Any:
|
|
438
|
+
"""Get a message (GET /api/emails/{address}/messages/{id})"""
|
|
439
|
+
return self._request("GET", f"/api/emails/{quote(str(address))}/messages/{quote(str(id))}")
|
|
440
|
+
|
|
441
|
+
def get_nameservers(self, domain: str) -> Any:
|
|
442
|
+
"""Get nameservers for a domain (GET /api/domains/{domain}/nameservers)"""
|
|
443
|
+
return self._request("GET", f"/api/domains/{quote(str(domain))}/nameservers")
|
|
444
|
+
|
|
445
|
+
def get_negotiation(self, id: str) -> Any:
|
|
446
|
+
"""Get a negotiation thread (GET /api/negotiations/{id})"""
|
|
447
|
+
return self._request("GET", f"/api/negotiations/{quote(str(id))}")
|
|
448
|
+
|
|
449
|
+
def get_notification_preferences(self) -> Any:
|
|
450
|
+
"""Get notification preferences (GET /api/notifications/preferences)"""
|
|
451
|
+
return self._request("GET", f"/api/notifications/preferences")
|
|
452
|
+
|
|
453
|
+
def get_parking_analytics(self, domain: str) -> Any:
|
|
454
|
+
"""Get parking analytics (GET /api/domains/{domain}/analytics)"""
|
|
455
|
+
return self._request("GET", f"/api/domains/{quote(str(domain))}/analytics")
|
|
456
|
+
|
|
457
|
+
def get_payout_status(self) -> Any:
|
|
458
|
+
"""Get marketplace payout status (GET /api/billing/connect)"""
|
|
459
|
+
return self._request("GET", f"/api/billing/connect")
|
|
460
|
+
|
|
461
|
+
def get_referrals(self) -> Any:
|
|
462
|
+
"""Get referral earnings and history (GET /api/referrals)"""
|
|
463
|
+
return self._request("GET", f"/api/referrals")
|
|
464
|
+
|
|
465
|
+
def get_sell_status(self, query: Optional[Dict[str, Any]] = None) -> Any:
|
|
466
|
+
"""Check listing and deal status (GET /api/domains/sell/status)"""
|
|
467
|
+
return self._request("GET", f"/api/domains/sell/status", query=query)
|
|
468
|
+
|
|
469
|
+
def get_transfer_away(self, domain: str) -> Any:
|
|
470
|
+
"""Get outbound transfer status (GET /api/domains/{domain}/transfer-away)"""
|
|
471
|
+
return self._request("GET", f"/api/domains/{quote(str(domain))}/transfer-away")
|
|
472
|
+
|
|
473
|
+
def get_unread_count(self) -> Any:
|
|
474
|
+
"""Get unread notification count (GET /api/notifications/count)"""
|
|
475
|
+
return self._request("GET", f"/api/notifications/count")
|
|
476
|
+
|
|
477
|
+
def get_webhook_deliveries(self, id: str, query: Optional[Dict[str, Any]] = None) -> Any:
|
|
478
|
+
"""List webhook deliveries (GET /api/webhooks/{id}/deliveries)"""
|
|
479
|
+
return self._request("GET", f"/api/webhooks/{quote(str(id))}/deliveries", query=query)
|
|
480
|
+
|
|
481
|
+
def import_domain(self, body: Dict[str, Any]) -> Any:
|
|
482
|
+
"""Import an external domain (POST /api/domains/import)"""
|
|
483
|
+
return self._request("POST", f"/api/domains/import", json=body)
|
|
484
|
+
|
|
485
|
+
def list_backorders(self, query: Optional[Dict[str, Any]] = None) -> Any:
|
|
486
|
+
"""List your backorders (GET /api/backorders)"""
|
|
487
|
+
return self._request("GET", f"/api/backorders", query=query)
|
|
488
|
+
|
|
489
|
+
def list_broker_requests(self, query: Optional[Dict[str, Any]] = None) -> Any:
|
|
490
|
+
"""List your broker requests (GET /api/broker)"""
|
|
491
|
+
return self._request("GET", f"/api/broker", query=query)
|
|
492
|
+
|
|
493
|
+
def list_connect_providers(self, domain: str) -> Any:
|
|
494
|
+
"""List available providers for connecting a domain (GET /api/domains/{domain}/connect)"""
|
|
495
|
+
return self._request("GET", f"/api/domains/{quote(str(domain))}/connect")
|
|
496
|
+
|
|
497
|
+
def list_deals(self, query: Optional[Dict[str, Any]] = None) -> Any:
|
|
498
|
+
"""List your deals (GET /api/deals)"""
|
|
499
|
+
return self._request("GET", f"/api/deals", query=query)
|
|
500
|
+
|
|
501
|
+
def list_domains(self) -> Any:
|
|
502
|
+
"""List all your registered domains (GET /api/domains)"""
|
|
503
|
+
return self._request("GET", f"/api/domains")
|
|
504
|
+
|
|
505
|
+
def list_email_messages_by_address(self, address: str, query: Optional[Dict[str, Any]] = None) -> Any:
|
|
506
|
+
"""List emails (GET /api/emails/{address}/messages)"""
|
|
507
|
+
return self._request("GET", f"/api/emails/{quote(str(address))}/messages", query=query)
|
|
508
|
+
|
|
509
|
+
def list_invoices(self, query: Optional[Dict[str, Any]] = None) -> Any:
|
|
510
|
+
"""List payment invoices (GET /api/billing/invoices)"""
|
|
511
|
+
return self._request("GET", f"/api/billing/invoices", query=query)
|
|
512
|
+
|
|
513
|
+
def list_mailbox_aliases(self, address: str) -> Any:
|
|
514
|
+
"""List mailbox aliases (GET /api/emails/{address}/aliases)"""
|
|
515
|
+
return self._request("GET", f"/api/emails/{quote(str(address))}/aliases")
|
|
516
|
+
|
|
517
|
+
def list_mailboxes(self, query: Optional[Dict[str, Any]] = None) -> Any:
|
|
518
|
+
"""List all mailboxes (GET /api/email)"""
|
|
519
|
+
return self._request("GET", f"/api/email", query=query)
|
|
520
|
+
|
|
521
|
+
def list_mailboxes_by_address(self, query: Optional[Dict[str, Any]] = None) -> Any:
|
|
522
|
+
"""List all mailboxes (GET /api/emails)"""
|
|
523
|
+
return self._request("GET", f"/api/emails", query=query)
|
|
524
|
+
|
|
525
|
+
def list_negotiations(self, query: Optional[Dict[str, Any]] = None) -> Any:
|
|
526
|
+
"""List your negotiations (GET /api/negotiations)"""
|
|
527
|
+
return self._request("GET", f"/api/negotiations", query=query)
|
|
528
|
+
|
|
529
|
+
def list_notifications(self, query: Optional[Dict[str, Any]] = None) -> Any:
|
|
530
|
+
"""List notifications (GET /api/notifications)"""
|
|
531
|
+
return self._request("GET", f"/api/notifications", query=query)
|
|
532
|
+
|
|
533
|
+
def list_suppressions(self, query: Optional[Dict[str, Any]] = None) -> Any:
|
|
534
|
+
"""List suppressed addresses (GET /api/suppressions)"""
|
|
535
|
+
return self._request("GET", f"/api/suppressions", query=query)
|
|
536
|
+
|
|
537
|
+
def list_tlds(self, query: Optional[Dict[str, Any]] = None) -> Any:
|
|
538
|
+
"""List all available TLDs with pricing (GET /api/tlds)"""
|
|
539
|
+
return self._request("GET", f"/api/tlds", query=query)
|
|
540
|
+
|
|
541
|
+
def list_tokens(self) -> Any:
|
|
542
|
+
"""List your API tokens (GET /api/tokens)"""
|
|
543
|
+
return self._request("GET", f"/api/tokens")
|
|
544
|
+
|
|
545
|
+
def list_verify_services(self, domain: str) -> Any:
|
|
546
|
+
"""List supported services for domain verification (GET /api/domains/{domain}/verify-service)"""
|
|
547
|
+
return self._request("GET", f"/api/domains/{quote(str(domain))}/verify-service")
|
|
548
|
+
|
|
549
|
+
def list_webhook_events(self) -> Any:
|
|
550
|
+
"""List webhook event types (GET /api/webhooks/events)"""
|
|
551
|
+
return self._request("GET", f"/api/webhooks/events")
|
|
552
|
+
|
|
553
|
+
def list_webhooks(self) -> Any:
|
|
554
|
+
"""List webhooks (GET /api/webhooks)"""
|
|
555
|
+
return self._request("GET", f"/api/webhooks")
|
|
556
|
+
|
|
557
|
+
def login_user(self, body: Dict[str, Any]) -> Any:
|
|
558
|
+
"""Send a magic link sign-in email (POST /api/auth/login)"""
|
|
559
|
+
return self._request("POST", f"/api/auth/login", json=body)
|
|
560
|
+
|
|
561
|
+
def make_offer(self, body: Dict[str, Any]) -> Any:
|
|
562
|
+
"""Make an offer on a listing (POST /api/negotiations)"""
|
|
563
|
+
return self._request("POST", f"/api/negotiations", json=body)
|
|
564
|
+
|
|
565
|
+
def mark_all_notifications_read(self) -> Any:
|
|
566
|
+
"""Mark all notifications as read (POST /api/notifications/read-all)"""
|
|
567
|
+
return self._request("POST", f"/api/notifications/read-all")
|
|
568
|
+
|
|
569
|
+
def mark_messages_read_by_address(self, address: str, body: Dict[str, Any] = None) -> Any:
|
|
570
|
+
"""Mark messages as read or unread (PATCH /api/emails/{address}/messages/read)"""
|
|
571
|
+
return self._request("PATCH", f"/api/emails/{quote(str(address))}/messages/read", json=body)
|
|
572
|
+
|
|
573
|
+
def mark_notification_read(self, id: str, body: Dict[str, Any]) -> Any:
|
|
574
|
+
"""Mark notification as read (PATCH /api/notifications/{id})"""
|
|
575
|
+
return self._request("PATCH", f"/api/notifications/{quote(str(id))}", json=body)
|
|
576
|
+
|
|
577
|
+
def provide_epp_code(self, id: str, body: Dict[str, Any]) -> Any:
|
|
578
|
+
"""Provide EPP/auth code (seller) (PATCH /api/deals/{id})"""
|
|
579
|
+
return self._request("PATCH", f"/api/deals/{quote(str(id))}", json=body)
|
|
580
|
+
|
|
581
|
+
def provision_agent(self, body: Dict[str, Any]) -> Any:
|
|
582
|
+
"""Provision a complete agent identity in one call (POST /api/agents/provision)"""
|
|
583
|
+
return self._request("POST", f"/api/agents/provision", json=body)
|
|
584
|
+
|
|
585
|
+
def register_user(self, body: Dict[str, Any]) -> Any:
|
|
586
|
+
"""Create a new account (POST /api/auth/register)"""
|
|
587
|
+
return self._request("POST", f"/api/auth/register", json=body)
|
|
588
|
+
|
|
589
|
+
def remove_card(self) -> Any:
|
|
590
|
+
"""Remove saved payment method (DELETE /api/billing/card)"""
|
|
591
|
+
return self._request("DELETE", f"/api/billing/card")
|
|
592
|
+
|
|
593
|
+
def remove_domain_email(self, domain: str) -> Any:
|
|
594
|
+
"""Remove email from a domain (DELETE /api/domains/{domain}/email/setup)"""
|
|
595
|
+
return self._request("DELETE", f"/api/domains/{quote(str(domain))}/email/setup")
|
|
596
|
+
|
|
597
|
+
def remove_mailbox_alias(self, address: str, alias: str) -> Any:
|
|
598
|
+
"""Remove a mailbox alias (DELETE /api/emails/{address}/aliases/{alias})"""
|
|
599
|
+
return self._request("DELETE", f"/api/emails/{quote(str(address))}/aliases/{quote(str(alias))}")
|
|
600
|
+
|
|
601
|
+
def remove_mailbox_webhook(self, address: str) -> Any:
|
|
602
|
+
"""Remove mailbox webhook (DELETE /api/emails/{address}/webhook)"""
|
|
603
|
+
return self._request("DELETE", f"/api/emails/{quote(str(address))}/webhook")
|
|
604
|
+
|
|
605
|
+
def remove_suppression(self, address: str) -> Any:
|
|
606
|
+
"""Remove a suppression (DELETE /api/suppressions/{address})"""
|
|
607
|
+
return self._request("DELETE", f"/api/suppressions/{quote(str(address))}")
|
|
608
|
+
|
|
609
|
+
def renew_domain(self, domain: str, body: Dict[str, Any] = None) -> Any:
|
|
610
|
+
"""Renew a domain (POST /api/domains/{domain}/renew)"""
|
|
611
|
+
return self._request("POST", f"/api/domains/{quote(str(domain))}/renew", json=body)
|
|
612
|
+
|
|
613
|
+
def reply_to_message_by_address(self, address: str, id: str, body: Dict[str, Any] = None) -> Any:
|
|
614
|
+
"""Reply to a message (POST /api/emails/{address}/messages/{id}/reply)"""
|
|
615
|
+
return self._request("POST", f"/api/emails/{quote(str(address))}/messages/{quote(str(id))}/reply", json=body)
|
|
616
|
+
|
|
617
|
+
def resend_email_verification(self, body: Dict[str, Any] = None) -> Any:
|
|
618
|
+
"""Resend contact email verification (POST /api/me/resend-verification)"""
|
|
619
|
+
return self._request("POST", f"/api/me/resend-verification", json=body)
|
|
620
|
+
|
|
621
|
+
def respond_to_broker_inquiry(self, body: Dict[str, Any]) -> Any:
|
|
622
|
+
"""Respond to a broker inquiry (owner side) (POST /api/broker/respond)"""
|
|
623
|
+
return self._request("POST", f"/api/broker/respond", json=body)
|
|
624
|
+
|
|
625
|
+
def respond_to_negotiation(self, id: str, body: Dict[str, Any]) -> Any:
|
|
626
|
+
"""Counter, accept, or decline (POST /api/negotiations/{id})"""
|
|
627
|
+
return self._request("POST", f"/api/negotiations/{quote(str(id))}", json=body)
|
|
628
|
+
|
|
629
|
+
def restore_dns(self, domain: str, body: Dict[str, Any] = None) -> Any:
|
|
630
|
+
"""Restore DNS from a snapshot (POST /api/domains/{domain}/dns/restore)"""
|
|
631
|
+
return self._request("POST", f"/api/domains/{quote(str(domain))}/dns/restore", json=body)
|
|
632
|
+
|
|
633
|
+
def revoke_token(self, id: str) -> Any:
|
|
634
|
+
"""Revoke an API token (DELETE /api/tokens/{id})"""
|
|
635
|
+
return self._request("DELETE", f"/api/tokens/{quote(str(id))}")
|
|
636
|
+
|
|
637
|
+
def rotate_mailbox_webhook_secret(self, address: str) -> Any:
|
|
638
|
+
"""Rotate webhook signing secret (POST /api/emails/{address}/webhook/rotate)"""
|
|
639
|
+
return self._request("POST", f"/api/emails/{quote(str(address))}/webhook/rotate")
|
|
640
|
+
|
|
641
|
+
def search(self, query: Optional[Dict[str, Any]] = None) -> Any:
|
|
642
|
+
"""Check availability and pricing for domains (GET /api/domains/search)"""
|
|
643
|
+
return self._request("GET", f"/api/domains/search", query=query)
|
|
644
|
+
|
|
645
|
+
def sell_domain(self, body: Dict[str, Any]) -> Any:
|
|
646
|
+
"""List a domain for sale (open marketplace) (POST /api/domains/sell)"""
|
|
647
|
+
return self._request("POST", f"/api/domains/sell", json=body)
|
|
648
|
+
|
|
649
|
+
def send_email_by_address(self, address: str, body: Dict[str, Any] = None) -> Any:
|
|
650
|
+
"""Send an email (POST /api/emails/{address}/send)"""
|
|
651
|
+
return self._request("POST", f"/api/emails/{quote(str(address))}/send", json=body)
|
|
652
|
+
|
|
653
|
+
def set_catch_all(self, domain: str, body: Dict[str, Any]) -> Any:
|
|
654
|
+
"""Set domain catch-all (PUT /api/domains/{domain}/email/catch-all)"""
|
|
655
|
+
return self._request("PUT", f"/api/domains/{quote(str(domain))}/email/catch-all", json=body)
|
|
656
|
+
|
|
657
|
+
def set_contact(self, body: "RegistrantContact") -> Any:
|
|
658
|
+
"""Set registrant contact info (PUT /api/me/contact)"""
|
|
659
|
+
return self._request("PUT", f"/api/me/contact", json=body)
|
|
660
|
+
|
|
661
|
+
def set_dns_records(self, domain: str, body: Dict[str, Any]) -> Any:
|
|
662
|
+
"""Set DNS records for a domain you own (PUT /api/domains/{domain}/dns)"""
|
|
663
|
+
return self._request("PUT", f"/api/domains/{quote(str(domain))}/dns", json=body)
|
|
664
|
+
|
|
665
|
+
def set_domain_redirect(self, domain: str, body: Dict[str, Any]) -> Any:
|
|
666
|
+
"""Forward a domain to a URL (PUT /api/domains/{domain}/redirect)"""
|
|
667
|
+
return self._request("PUT", f"/api/domains/{quote(str(domain))}/redirect", json=body)
|
|
668
|
+
|
|
669
|
+
def set_mailbox_webhook(self, address: str, body: Dict[str, Any]) -> Any:
|
|
670
|
+
"""Set mailbox webhook (PUT /api/emails/{address}/webhook)"""
|
|
671
|
+
return self._request("PUT", f"/api/emails/{quote(str(address))}/webhook", json=body)
|
|
672
|
+
|
|
673
|
+
def set_nameservers(self, domain: str, body: Dict[str, Any]) -> Any:
|
|
674
|
+
"""Set nameservers for a domain (PUT /api/domains/{domain}/nameservers)"""
|
|
675
|
+
return self._request("PUT", f"/api/domains/{quote(str(domain))}/nameservers", json=body)
|
|
676
|
+
|
|
677
|
+
def setup_billing(self, body: Dict[str, Any] = None) -> Any:
|
|
678
|
+
"""Get checkout URL for adding a payment method (POST /api/billing/setup)"""
|
|
679
|
+
return self._request("POST", f"/api/billing/setup", json=body)
|
|
680
|
+
|
|
681
|
+
def setup_domain_email(self, domain: str, body: Dict[str, Any] = None) -> Any:
|
|
682
|
+
"""Set up email on a domain (POST /api/domains/{domain}/email/setup)"""
|
|
683
|
+
return self._request("POST", f"/api/domains/{quote(str(domain))}/email/setup", json=body)
|
|
684
|
+
|
|
685
|
+
def snapshot_dns(self, domain: str, body: Dict[str, Any] = None) -> Any:
|
|
686
|
+
"""Capture a DNS snapshot (POST /api/domains/{domain}/dns/snapshot)"""
|
|
687
|
+
return self._request("POST", f"/api/domains/{quote(str(domain))}/dns/snapshot", json=body)
|
|
688
|
+
|
|
689
|
+
def start_payout_onboarding(self) -> Any:
|
|
690
|
+
"""Start bank payout onboarding (POST /api/billing/connect)"""
|
|
691
|
+
return self._request("POST", f"/api/billing/connect")
|
|
692
|
+
|
|
693
|
+
def suggest_domains(self, query: Optional[Dict[str, Any]] = None) -> Any:
|
|
694
|
+
"""AI-powered domain suggestions with availability (GET /api/domains/suggest)"""
|
|
695
|
+
return self._request("GET", f"/api/domains/suggest", query=query)
|
|
696
|
+
|
|
697
|
+
def test_mailbox_webhook(self, address: str) -> Any:
|
|
698
|
+
"""Test mailbox webhook (POST /api/emails/{address}/webhook/test)"""
|
|
699
|
+
return self._request("POST", f"/api/emails/{quote(str(address))}/webhook/test")
|
|
700
|
+
|
|
701
|
+
def transfer_domain(self, body: Dict[str, Any]) -> Any:
|
|
702
|
+
"""Transfer domain from another provider (POST /api/domains/transfer)"""
|
|
703
|
+
return self._request("POST", f"/api/domains/transfer", json=body)
|
|
704
|
+
|
|
705
|
+
def update_account(self, body: Dict[str, Any]) -> Any:
|
|
706
|
+
"""Update account preferences (PUT /api/me)"""
|
|
707
|
+
return self._request("PUT", f"/api/me", json=body)
|
|
708
|
+
|
|
709
|
+
def update_domain_settings(self, domain: str, body: Dict[str, Any]) -> Any:
|
|
710
|
+
"""Update domain settings (PUT /api/domains/{domain}/settings)"""
|
|
711
|
+
return self._request("PUT", f"/api/domains/{quote(str(domain))}/settings", json=body)
|
|
712
|
+
|
|
713
|
+
def update_listing(self, domain: str, body: Dict[str, Any]) -> Any:
|
|
714
|
+
"""Update a domain listing (PATCH /api/domains/{domain}/for-sale)"""
|
|
715
|
+
return self._request("PATCH", f"/api/domains/{quote(str(domain))}/for-sale", json=body)
|
|
716
|
+
|
|
717
|
+
def update_mailbox_by_address(self, address: str, body: Dict[str, Any] = None) -> Any:
|
|
718
|
+
"""Update mailbox (PATCH /api/emails/{address})"""
|
|
719
|
+
return self._request("PATCH", f"/api/emails/{quote(str(address))}", json=body)
|
|
720
|
+
|
|
721
|
+
def update_notification_preferences(self, body: Dict[str, Any]) -> Any:
|
|
722
|
+
"""Update notification preferences (PUT /api/notifications/preferences)"""
|
|
723
|
+
return self._request("PUT", f"/api/notifications/preferences", json=body)
|
|
724
|
+
|
|
725
|
+
def update_parking(self, domain: str, body: Dict[str, Any]) -> Any:
|
|
726
|
+
"""Update parking settings (PUT /api/domains/{domain}/parking)"""
|
|
727
|
+
return self._request("PUT", f"/api/domains/{quote(str(domain))}/parking", json=body)
|
|
728
|
+
|
|
729
|
+
def update_webhook(self, id: str, body: Dict[str, Any]) -> Any:
|
|
730
|
+
"""Update webhook (PATCH /api/webhooks/{id})"""
|
|
731
|
+
return self._request("PATCH", f"/api/webhooks/{quote(str(id))}", json=body)
|
|
732
|
+
|
|
733
|
+
def verify_connection(self, domain: str, body: Dict[str, Any]) -> Any:
|
|
734
|
+
"""Verify that a provider connection is working (POST /api/domains/{domain}/verify)"""
|
|
735
|
+
return self._request("POST", f"/api/domains/{quote(str(domain))}/verify", json=body)
|
|
736
|
+
|
|
737
|
+
def verify_import(self, body: Dict[str, Any]) -> Any:
|
|
738
|
+
"""Verify and complete domain import (POST /api/domains/import/verify)"""
|
|
739
|
+
return self._request("POST", f"/api/domains/import/verify", json=body)
|
|
740
|
+
|
|
741
|
+
def verify_magic_link(self, query: Optional[Dict[str, Any]] = None) -> Any:
|
|
742
|
+
"""Verify a magic link token (GET /api/auth/verify)"""
|
|
743
|
+
return self._request("GET", f"/api/auth/verify", query=query)
|
|
744
|
+
|
|
745
|
+
def verify_sell_listing(self, body: Dict[str, Any]) -> Any:
|
|
746
|
+
"""Verify TXT ownership for external listing (POST /api/domains/sell/verify)"""
|
|
747
|
+
return self._request("POST", f"/api/domains/sell/verify", json=body)
|
|
748
|
+
|
|
749
|
+
def verify_service(self, domain: str, body: Dict[str, Any]) -> Any:
|
|
750
|
+
"""Add DNS records to verify domain ownership for a third-party service (POST /api/domains/{domain}/verify-service)"""
|
|
751
|
+
return self._request("POST", f"/api/domains/{quote(str(domain))}/verify-service", json=body)
|
|
752
|
+
|
|
753
|
+
def watch_transfer(self, body: Dict[str, Any]) -> Any:
|
|
754
|
+
"""Watch a domain for transfer eligibility (POST /api/domains/transfer-watch)"""
|
|
755
|
+
return self._request("POST", f"/api/domains/transfer-watch", json=body)
|
|
756
|
+
|
|
757
|
+
def whois_lookup(self, query: Optional[Dict[str, Any]] = None) -> Any:
|
|
758
|
+
"""Look up domain registration data via RDAP (GET /api/domains/whois)"""
|
|
759
|
+
return self._request("GET", f"/api/domains/whois", query=query)
|
|
760
|
+
|
|
761
|
+
def withdraw_balance(self) -> Any:
|
|
762
|
+
"""Withdraw marketplace balance to bank (POST /api/billing/connect/payout)"""
|
|
763
|
+
return self._request("POST", f"/api/billing/connect/payout")
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""End-to-end check for the domani Python SDK against a running server.
|
|
3
|
+
|
|
4
|
+
Reads DOMANI_API_KEY, DOMANI_BASE_URL, DOMANI_DOMAIN from the environment,
|
|
5
|
+
runs the README quickstart flow, and exits 0 on success / 1 on any failure.
|
|
6
|
+
Invoked by the `sdk.python_e2e` eval scenario; also runnable by hand against a
|
|
7
|
+
booted mock-mode server.
|
|
8
|
+
"""
|
|
9
|
+
import os
|
|
10
|
+
import sys
|
|
11
|
+
|
|
12
|
+
# Import the sibling package without installing it.
|
|
13
|
+
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
14
|
+
from domani import Domani, DomaniError # noqa: E402
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def fail(msg):
|
|
18
|
+
print(f"[sdk-py-e2e] FAIL: {msg}", file=sys.stderr)
|
|
19
|
+
sys.exit(1)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def main():
|
|
23
|
+
api_key = os.environ.get("DOMANI_API_KEY")
|
|
24
|
+
base_url = os.environ.get("DOMANI_BASE_URL")
|
|
25
|
+
domain = os.environ.get("DOMANI_DOMAIN")
|
|
26
|
+
if not (api_key and base_url and domain):
|
|
27
|
+
fail("DOMANI_API_KEY, DOMANI_BASE_URL, DOMANI_DOMAIN must be set")
|
|
28
|
+
|
|
29
|
+
domani = Domani(api_key=api_key, base_url=base_url)
|
|
30
|
+
address = f"hi@{domain}"
|
|
31
|
+
|
|
32
|
+
# Typed search (README shape: search(query={...}))
|
|
33
|
+
results = domani.search(query={"q": f"sdk-py-{domain}"})
|
|
34
|
+
if not isinstance(results, dict):
|
|
35
|
+
fail(f"search returned non-dict: {results!r}")
|
|
36
|
+
|
|
37
|
+
# Create a mailbox (README shape)
|
|
38
|
+
mb = domani.create_mailbox_by_address(body={"address": address})
|
|
39
|
+
if not isinstance(mb, dict) or mb.get("address") != address:
|
|
40
|
+
fail(f"create_mailbox address mismatch: {mb!r}")
|
|
41
|
+
|
|
42
|
+
# Send (README shape)
|
|
43
|
+
sent = domani.send_email_by_address(
|
|
44
|
+
address, body={"to": "user@example.com", "subject": "SDK E2E", "text": "hi from the Python SDK"}
|
|
45
|
+
)
|
|
46
|
+
if not isinstance(sent, dict) or not sent.get("status"):
|
|
47
|
+
fail(f"send returned no status: {sent!r}")
|
|
48
|
+
|
|
49
|
+
# Suppression: add -> list -> a blocked send raises DomaniError -> remove
|
|
50
|
+
domani.add_suppression(body={"address": "blocked@example.com"})
|
|
51
|
+
listed = domani.list_suppressions()
|
|
52
|
+
addrs = [s.get("address") for s in (listed or {}).get("suppressions", [])]
|
|
53
|
+
if "blocked@example.com" not in addrs:
|
|
54
|
+
fail(f"suppression not listed: {listed!r}")
|
|
55
|
+
|
|
56
|
+
raised = False
|
|
57
|
+
try:
|
|
58
|
+
domani.send_email_by_address(address, body={"to": "blocked@example.com", "text": "should be blocked"})
|
|
59
|
+
except DomaniError as e:
|
|
60
|
+
if e.status == 400 and e.code == "RECIPIENTS_SUPPRESSED":
|
|
61
|
+
raised = True
|
|
62
|
+
else:
|
|
63
|
+
fail(f"wrong DomaniError: {e.status} {e.code}")
|
|
64
|
+
if not raised:
|
|
65
|
+
fail("blocked send should have raised DomaniError")
|
|
66
|
+
|
|
67
|
+
domani.remove_suppression("blocked@example.com")
|
|
68
|
+
print("[sdk-py-e2e] OK")
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
if __name__ == "__main__":
|
|
72
|
+
main()
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "domani"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Official Python SDK for the domani API - domains, email, and identity for AI agents."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.8"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
keywords = ["domani", "domains", "email", "api", "sdk", "agents", "mcp"]
|
|
13
|
+
authors = [{ name = "domani" }]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"License :: OSI Approved :: MIT License",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
[project.urls]
|
|
21
|
+
Homepage = "https://domani.run"
|
|
22
|
+
Repository = "https://github.com/gwendall/domani"
|
|
23
|
+
|
|
24
|
+
[tool.hatch.build.targets.wheel]
|
|
25
|
+
packages = ["domani"]
|