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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mailsai
3
- Version: 0.2.0
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
- Create the agent once (this mints its inbox address), then send from it. Skipping the
42
- create step is the most common first-run error — `agent("sarah")` only *references* an
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, agent
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
- sarah.send(
60
- to="you@example.com",
61
- subject="Your verification code is 481902",
62
- body="Your verification code is 481902. It expires in 15 minutes.",
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
- Create the agent once (this mints its inbox address), then send from it. Skipping the
14
- create step is the most common first-run error — `agent("sarah")` only *references* an
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, agent
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
- sarah.send(
32
- to="you@example.com",
33
- subject="Your verification code is 481902",
34
- body="Your verification code is 481902. It expires in 15 minutes.",
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 via a named agent."""
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
- kwargs["agent"] = agent
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.0
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
- Create the agent once (this mints its inbox address), then send from it. Skipping the
42
- create step is the most common first-run error — `agent("sarah")` only *references* an
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, agent
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
- sarah.send(
60
- to="you@example.com",
61
- subject="Your verification code is 481902",
62
- body="Your verification code is 481902. It expires in 15 minutes.",
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.0"
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