mailsai 0.2.0__tar.gz → 0.2.2__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.
- {mailsai-0.2.0 → mailsai-0.2.2}/PKG-INFO +34 -15
- {mailsai-0.2.0 → mailsai-0.2.2}/README.md +33 -14
- {mailsai-0.2.0 → mailsai-0.2.2}/mailsai/__init__.py +41 -4
- {mailsai-0.2.0 → mailsai-0.2.2}/mailsai.egg-info/PKG-INFO +34 -15
- {mailsai-0.2.0 → mailsai-0.2.2}/pyproject.toml +1 -1
- {mailsai-0.2.0 → mailsai-0.2.2}/LICENSE +0 -0
- {mailsai-0.2.0 → mailsai-0.2.2}/mailsai.egg-info/SOURCES.txt +0 -0
- {mailsai-0.2.0 → mailsai-0.2.2}/mailsai.egg-info/dependency_links.txt +0 -0
- {mailsai-0.2.0 → mailsai-0.2.2}/mailsai.egg-info/requires.txt +0 -0
- {mailsai-0.2.0 → mailsai-0.2.2}/mailsai.egg-info/top_level.txt +0 -0
- {mailsai-0.2.0 → mailsai-0.2.2}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mailsai
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Email API for AI agents — Python SDK. Per-agent email addresses on your domain, per-agent reputation, and prompt-injection scanning on every inbound. Send, receive structured inbound replies, and identify agents via REST. Intent + entity extraction is opt-in.
|
|
5
5
|
Author-email: "Mails.ai" <support@mails.ai>
|
|
6
6
|
License: MIT
|
|
@@ -38,29 +38,48 @@ pip install mailsai
|
|
|
38
38
|
|
|
39
39
|
## Quick start
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
agent, it does not create one, so sending returns `404 agent_not_found`.
|
|
41
|
+
No agent to create, no domain to verify, no DNS. `from` names the sender and the agent is
|
|
42
|
+
created on first use.
|
|
44
43
|
|
|
45
44
|
```python
|
|
46
|
-
from mailsai import Client
|
|
45
|
+
from mailsai import Client
|
|
47
46
|
|
|
48
47
|
# Reads MAILS_API_KEY from env (or pass api_key=... explicitly)
|
|
49
48
|
client = Client()
|
|
50
49
|
|
|
51
|
-
# One-time setup: create the agent. It gets sarah@<your-workspace>.mails.ai
|
|
52
|
-
client.create_agent("sarah")
|
|
53
|
-
|
|
54
|
-
sarah = agent("sarah")
|
|
55
|
-
|
|
56
50
|
# mails.ai is transactional-only: mail the recipient asked for. Cold outreach and bulk
|
|
57
51
|
# marketing are refused with 422 cold_email_prohibited — that is deliberate, and it is
|
|
58
52
|
# what keeps the sending reputation clean for everyone on the platform.
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
53
|
+
sent = client.send(**{
|
|
54
|
+
"from": "sarah",
|
|
55
|
+
"to": "you@example.com",
|
|
56
|
+
"subject": "Your verification code is 481902",
|
|
57
|
+
"body": "Your verification code is 481902. It expires in 15 minutes.",
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
print(sent["from"]) # sarah@<your-workspace>.mails.ai — which identity actually sent
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
A **test key** (`mk_test_…`) runs that entire path — validation, the firewall, threading,
|
|
64
|
+
events, webhooks — and delivers nothing, so you can integrate before you decide anything. On a
|
|
65
|
+
test key, check `sent.get("classifier_warning")`: it is how a sandbox send tells you the same
|
|
66
|
+
message would be refused on a live key.
|
|
67
|
+
|
|
68
|
+
Already have an agent, or want to name it explicitly? Both of these still work:
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
client.create_agent("sarah") # explicit creation, when you want it
|
|
72
|
+
client.send("sarah", to="…", subject="…", body="…")
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Note the free plan allows **one** agent. If `from` names a second one and there is no room,
|
|
76
|
+
the send is refused with `402 plan_limit_exceeded` naming the agent you do have — it is never
|
|
77
|
+
quietly sent under a different identity.
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
from mailsai import agent
|
|
81
|
+
|
|
82
|
+
sarah = agent("sarah")
|
|
64
83
|
|
|
65
84
|
@sarah.on_reply
|
|
66
85
|
def handle(reply):
|
|
@@ -10,29 +10,48 @@ pip install mailsai
|
|
|
10
10
|
|
|
11
11
|
## Quick start
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
agent, it does not create one, so sending returns `404 agent_not_found`.
|
|
13
|
+
No agent to create, no domain to verify, no DNS. `from` names the sender and the agent is
|
|
14
|
+
created on first use.
|
|
16
15
|
|
|
17
16
|
```python
|
|
18
|
-
from mailsai import Client
|
|
17
|
+
from mailsai import Client
|
|
19
18
|
|
|
20
19
|
# Reads MAILS_API_KEY from env (or pass api_key=... explicitly)
|
|
21
20
|
client = Client()
|
|
22
21
|
|
|
23
|
-
# One-time setup: create the agent. It gets sarah@<your-workspace>.mails.ai
|
|
24
|
-
client.create_agent("sarah")
|
|
25
|
-
|
|
26
|
-
sarah = agent("sarah")
|
|
27
|
-
|
|
28
22
|
# mails.ai is transactional-only: mail the recipient asked for. Cold outreach and bulk
|
|
29
23
|
# marketing are refused with 422 cold_email_prohibited — that is deliberate, and it is
|
|
30
24
|
# what keeps the sending reputation clean for everyone on the platform.
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
25
|
+
sent = client.send(**{
|
|
26
|
+
"from": "sarah",
|
|
27
|
+
"to": "you@example.com",
|
|
28
|
+
"subject": "Your verification code is 481902",
|
|
29
|
+
"body": "Your verification code is 481902. It expires in 15 minutes.",
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
print(sent["from"]) # sarah@<your-workspace>.mails.ai — which identity actually sent
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
A **test key** (`mk_test_…`) runs that entire path — validation, the firewall, threading,
|
|
36
|
+
events, webhooks — and delivers nothing, so you can integrate before you decide anything. On a
|
|
37
|
+
test key, check `sent.get("classifier_warning")`: it is how a sandbox send tells you the same
|
|
38
|
+
message would be refused on a live key.
|
|
39
|
+
|
|
40
|
+
Already have an agent, or want to name it explicitly? Both of these still work:
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
client.create_agent("sarah") # explicit creation, when you want it
|
|
44
|
+
client.send("sarah", to="…", subject="…", body="…")
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Note the free plan allows **one** agent. If `from` names a second one and there is no room,
|
|
48
|
+
the send is refused with `402 plan_limit_exceeded` naming the agent you do have — it is never
|
|
49
|
+
quietly sent under a different identity.
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
from mailsai import agent
|
|
53
|
+
|
|
54
|
+
sarah = agent("sarah")
|
|
36
55
|
|
|
37
56
|
@sarah.on_reply
|
|
38
57
|
def handle(reply):
|
|
@@ -13,8 +13,11 @@ Quick start:
|
|
|
13
13
|
# event["intent"] -> "schedule_demo"
|
|
14
14
|
# event["entities"] -> {"date": "...", "time": "..."}
|
|
15
15
|
# event["urgency"] -> 0.8
|
|
16
|
-
# event["injection_score"] -> 0.02
|
|
16
|
+
# event["injection_score"] -> 0.02 (the evidence)
|
|
17
|
+
# event["quarantined"] -> False (our VERDICT — gate on this)
|
|
17
18
|
# event["sender_reputation"] -> 0.91
|
|
19
|
+
if event.get("quarantined"):
|
|
20
|
+
return # we judged it an attack; don't act on it
|
|
18
21
|
print(event["intent"], event["entities"])
|
|
19
22
|
|
|
20
23
|
sarah.start_listening() # blocks; runs SSE consumer
|
|
@@ -206,14 +209,48 @@ class Client:
|
|
|
206
209
|
|
|
207
210
|
# --- high-level helpers ---
|
|
208
211
|
|
|
209
|
-
def send(self, agent: str, **kwargs: Any) -> Dict[str, Any]:
|
|
210
|
-
"""POST /api/v1/messages — send
|
|
212
|
+
def send(self, agent: Optional[str] = None, **kwargs: Any) -> Dict[str, Any]:
|
|
213
|
+
"""POST /api/v1/messages — send an email.
|
|
214
|
+
|
|
215
|
+
``agent`` is OPTIONAL. Omit it and the workspace's single agent is used, or one is
|
|
216
|
+
created for you, named from ``from``::
|
|
217
|
+
|
|
218
|
+
client.send(**{"from": "billing", "to": "dana@acme.com",
|
|
219
|
+
"subject": "Invoice #221", "body": "…"})
|
|
220
|
+
|
|
221
|
+
or name it explicitly, positionally or by keyword::
|
|
222
|
+
|
|
223
|
+
client.send("sarah", to="dana@acme.com", subject="…", body="…")
|
|
224
|
+
|
|
225
|
+
WHY THIS IS OPTIONAL NOW. ``agent`` was a required positional here long after the API
|
|
226
|
+
made it optional (2026-08-05), so the exact call our own positioning tells developers
|
|
227
|
+
to write — ``{from, to, subject, body}``, no setup — raised a TypeError in Python
|
|
228
|
+
before it ever reached the network, while the same call worked in TypeScript. Python
|
|
229
|
+
is the language of the agent ecosystem (both the OpenAI-Agents and LangGraph examples
|
|
230
|
+
are Python), so that gap fell on the users most likely to be the buyer.
|
|
231
|
+
|
|
232
|
+
``from`` selects the agent by handle — ``"billing"``, ``"billing@acme.com"`` and
|
|
233
|
+
``"Acme <billing@acme.com>"`` all mean the agent called ``billing``. It is resolved if
|
|
234
|
+
it exists, created if it does not, and refused with 402 ``plan_limit_exceeded`` if
|
|
235
|
+
your plan has no room; it is never resolved to a DIFFERENT agent. Read ``from`` on the
|
|
236
|
+
result for the address that actually sent.
|
|
237
|
+
"""
|
|
211
238
|
idempotency_key = kwargs.pop("idempotency_key", None)
|
|
212
|
-
|
|
239
|
+
if agent is not None:
|
|
240
|
+
kwargs["agent"] = agent
|
|
213
241
|
body = {k: v for k, v in kwargs.items() if v is not None}
|
|
214
242
|
headers = {"Idempotency-Key": idempotency_key} if idempotency_key else None
|
|
215
243
|
return self._request("POST", "/api/v1/messages", body, headers)
|
|
216
244
|
|
|
245
|
+
def me(self) -> Dict[str, Any]:
|
|
246
|
+
"""GET /api/v1/me — who this key is: workspace, tier, scopes, mode.
|
|
247
|
+
|
|
248
|
+
Present in the TypeScript SDK and exposed as an MCP tool; it was missing here, so a
|
|
249
|
+
Python caller had no way to answer "which workspace am I talking to, and is this key
|
|
250
|
+
live or test?" without a raw HTTP call.
|
|
251
|
+
"""
|
|
252
|
+
return self._request("GET", "/api/v1/me")
|
|
253
|
+
|
|
217
254
|
def reply(self, message_id: str, **kwargs: Any) -> Dict[str, Any]:
|
|
218
255
|
body = {k: v for k, v in kwargs.items() if v is not None}
|
|
219
256
|
return self._request("POST", f"/api/v1/messages/{message_id}/reply", body)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mailsai
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Email API for AI agents — Python SDK. Per-agent email addresses on your domain, per-agent reputation, and prompt-injection scanning on every inbound. Send, receive structured inbound replies, and identify agents via REST. Intent + entity extraction is opt-in.
|
|
5
5
|
Author-email: "Mails.ai" <support@mails.ai>
|
|
6
6
|
License: MIT
|
|
@@ -38,29 +38,48 @@ pip install mailsai
|
|
|
38
38
|
|
|
39
39
|
## Quick start
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
agent, it does not create one, so sending returns `404 agent_not_found`.
|
|
41
|
+
No agent to create, no domain to verify, no DNS. `from` names the sender and the agent is
|
|
42
|
+
created on first use.
|
|
44
43
|
|
|
45
44
|
```python
|
|
46
|
-
from mailsai import Client
|
|
45
|
+
from mailsai import Client
|
|
47
46
|
|
|
48
47
|
# Reads MAILS_API_KEY from env (or pass api_key=... explicitly)
|
|
49
48
|
client = Client()
|
|
50
49
|
|
|
51
|
-
# One-time setup: create the agent. It gets sarah@<your-workspace>.mails.ai
|
|
52
|
-
client.create_agent("sarah")
|
|
53
|
-
|
|
54
|
-
sarah = agent("sarah")
|
|
55
|
-
|
|
56
50
|
# mails.ai is transactional-only: mail the recipient asked for. Cold outreach and bulk
|
|
57
51
|
# marketing are refused with 422 cold_email_prohibited — that is deliberate, and it is
|
|
58
52
|
# what keeps the sending reputation clean for everyone on the platform.
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
53
|
+
sent = client.send(**{
|
|
54
|
+
"from": "sarah",
|
|
55
|
+
"to": "you@example.com",
|
|
56
|
+
"subject": "Your verification code is 481902",
|
|
57
|
+
"body": "Your verification code is 481902. It expires in 15 minutes.",
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
print(sent["from"]) # sarah@<your-workspace>.mails.ai — which identity actually sent
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
A **test key** (`mk_test_…`) runs that entire path — validation, the firewall, threading,
|
|
64
|
+
events, webhooks — and delivers nothing, so you can integrate before you decide anything. On a
|
|
65
|
+
test key, check `sent.get("classifier_warning")`: it is how a sandbox send tells you the same
|
|
66
|
+
message would be refused on a live key.
|
|
67
|
+
|
|
68
|
+
Already have an agent, or want to name it explicitly? Both of these still work:
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
client.create_agent("sarah") # explicit creation, when you want it
|
|
72
|
+
client.send("sarah", to="…", subject="…", body="…")
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Note the free plan allows **one** agent. If `from` names a second one and there is no room,
|
|
76
|
+
the send is refused with `402 plan_limit_exceeded` naming the agent you do have — it is never
|
|
77
|
+
quietly sent under a different identity.
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
from mailsai import agent
|
|
81
|
+
|
|
82
|
+
sarah = agent("sarah")
|
|
64
83
|
|
|
65
84
|
@sarah.on_reply
|
|
66
85
|
def handle(reply):
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "mailsai"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.2"
|
|
8
8
|
description = "Email API for AI agents — Python SDK. Per-agent email addresses on your domain, per-agent reputation, and prompt-injection scanning on every inbound. Send, receive structured inbound replies, and identify agents via REST. Intent + entity extraction is opt-in."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|