lumbox 0.3.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.
lumbox-0.3.0/PKG-INFO ADDED
@@ -0,0 +1,134 @@
1
+ Metadata-Version: 2.4
2
+ Name: lumbox
3
+ Version: 0.3.0
4
+ Summary: Lumbox SDK — Email for AI agents. Create inboxes, receive OTPs, send replies.
5
+ License-Expression: MIT
6
+ Project-URL: Homepage, https://lumbox.co
7
+ Project-URL: Repository, https://github.com/kumard3/agentinbox
8
+ Project-URL: Documentation, https://docs.lumbox.co
9
+ Keywords: email,ai,agents,otp,mcp,lumbox,verification
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.9
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Requires-Python: >=3.9
19
+ Description-Content-Type: text/markdown
20
+ Requires-Dist: httpx>=0.27.0
21
+ Provides-Extra: langchain
22
+ Requires-Dist: langchain-core>=0.3.0; extra == "langchain"
23
+ Provides-Extra: crewai
24
+ Requires-Dist: crewai>=0.80.0; extra == "crewai"
25
+
26
+ # lumbox
27
+
28
+ Email for AI agents. Create inboxes, receive OTPs, extract verification codes — all via API.
29
+
30
+ ## Install
31
+
32
+ ```bash
33
+ pip install lumbox
34
+ ```
35
+
36
+ ## Quick Start
37
+
38
+ ```python
39
+ from lumbox import Lumbox
40
+
41
+ client = Lumbox(api_key="ak_...")
42
+
43
+ # Create an inbox
44
+ inbox = client.create_inbox(name="github-bot")
45
+ # → github-bot@lumbox.co
46
+
47
+ # Sign up on any service with inbox.address...
48
+
49
+ # Wait for the OTP (blocks until it arrives)
50
+ otp = inbox.wait_for_otp(timeout=60)
51
+ print(otp["code"]) # "847291"
52
+ ```
53
+
54
+ ## Auth header
55
+
56
+ The SDK sends `Authorization: Bearer ak_...` by default. The legacy
57
+ `X-API-Key` header still works server-side. Raw HTTP example:
58
+
59
+ ```bash
60
+ curl https://api.lumbox.co/v1/inboxes \
61
+ -H "Authorization: Bearer ak_your_key_here"
62
+ ```
63
+
64
+ ## Inbox-scoped API keys
65
+
66
+ Mint a key that can only ever touch one inbox:
67
+
68
+ ```python
69
+ created = client.inboxes.api_keys.create(inbox.id, name="ci-bot")
70
+ print(created["api_key"]) # shown once
71
+
72
+ client.inboxes.api_keys.list(inbox.id)
73
+ client.inboxes.api_keys.delete(inbox.id, created["id"])
74
+ ```
75
+
76
+ ## Features
77
+
78
+ - **OTP extraction** — verification codes parsed automatically
79
+ - **Long-poll** — `wait_for_email()` and `wait_for_otp()` block until arrival
80
+ - **Send/reply/forward** — full outbound email support
81
+ - **Custom domains** — DKIM, SPF, DMARC verification
82
+ - **LangChain & CrewAI** — pre-built tool wrappers
83
+
84
+ ## Framework Integration
85
+
86
+ ### LangChain
87
+
88
+ ```bash
89
+ pip install lumbox[langchain]
90
+ ```
91
+
92
+ ```python
93
+ from lumbox import create_langchain_tools
94
+
95
+ tools = create_langchain_tools(api_key="ak_...")
96
+ # Use tools directly with LangChain agents
97
+ ```
98
+
99
+ ### Any Framework
100
+
101
+ ```python
102
+ from lumbox import lumbox_tools
103
+
104
+ tools = lumbox_tools(api_key="ak_...")
105
+ # Dict of plain functions with proper type hints
106
+ ```
107
+
108
+ ## API
109
+
110
+ ### `Lumbox(api_key, base_url?)`
111
+
112
+ Create a client. `base_url` defaults to `https://api.lumbox.co`.
113
+
114
+ ### `client.create_inbox(name?, domain?)`
115
+
116
+ Returns an `Inbox` object with `.address`, `.id`, and convenience methods.
117
+
118
+ ### `inbox.wait_for_otp(timeout?, sender?)`
119
+
120
+ Blocks until an email with an OTP arrives.
121
+
122
+ ### `inbox.wait_for_email(timeout?, sender?, subject?)`
123
+
124
+ Blocks until any email arrives.
125
+
126
+ ### `inbox.list_emails()`
127
+
128
+ List all emails in the inbox.
129
+
130
+ Full API docs: [docs.lumbox.co](https://docs.lumbox.co)
131
+
132
+ ## License
133
+
134
+ MIT
lumbox-0.3.0/README.md ADDED
@@ -0,0 +1,109 @@
1
+ # lumbox
2
+
3
+ Email for AI agents. Create inboxes, receive OTPs, extract verification codes — all via API.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pip install lumbox
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```python
14
+ from lumbox import Lumbox
15
+
16
+ client = Lumbox(api_key="ak_...")
17
+
18
+ # Create an inbox
19
+ inbox = client.create_inbox(name="github-bot")
20
+ # → github-bot@lumbox.co
21
+
22
+ # Sign up on any service with inbox.address...
23
+
24
+ # Wait for the OTP (blocks until it arrives)
25
+ otp = inbox.wait_for_otp(timeout=60)
26
+ print(otp["code"]) # "847291"
27
+ ```
28
+
29
+ ## Auth header
30
+
31
+ The SDK sends `Authorization: Bearer ak_...` by default. The legacy
32
+ `X-API-Key` header still works server-side. Raw HTTP example:
33
+
34
+ ```bash
35
+ curl https://api.lumbox.co/v1/inboxes \
36
+ -H "Authorization: Bearer ak_your_key_here"
37
+ ```
38
+
39
+ ## Inbox-scoped API keys
40
+
41
+ Mint a key that can only ever touch one inbox:
42
+
43
+ ```python
44
+ created = client.inboxes.api_keys.create(inbox.id, name="ci-bot")
45
+ print(created["api_key"]) # shown once
46
+
47
+ client.inboxes.api_keys.list(inbox.id)
48
+ client.inboxes.api_keys.delete(inbox.id, created["id"])
49
+ ```
50
+
51
+ ## Features
52
+
53
+ - **OTP extraction** — verification codes parsed automatically
54
+ - **Long-poll** — `wait_for_email()` and `wait_for_otp()` block until arrival
55
+ - **Send/reply/forward** — full outbound email support
56
+ - **Custom domains** — DKIM, SPF, DMARC verification
57
+ - **LangChain & CrewAI** — pre-built tool wrappers
58
+
59
+ ## Framework Integration
60
+
61
+ ### LangChain
62
+
63
+ ```bash
64
+ pip install lumbox[langchain]
65
+ ```
66
+
67
+ ```python
68
+ from lumbox import create_langchain_tools
69
+
70
+ tools = create_langchain_tools(api_key="ak_...")
71
+ # Use tools directly with LangChain agents
72
+ ```
73
+
74
+ ### Any Framework
75
+
76
+ ```python
77
+ from lumbox import lumbox_tools
78
+
79
+ tools = lumbox_tools(api_key="ak_...")
80
+ # Dict of plain functions with proper type hints
81
+ ```
82
+
83
+ ## API
84
+
85
+ ### `Lumbox(api_key, base_url?)`
86
+
87
+ Create a client. `base_url` defaults to `https://api.lumbox.co`.
88
+
89
+ ### `client.create_inbox(name?, domain?)`
90
+
91
+ Returns an `Inbox` object with `.address`, `.id`, and convenience methods.
92
+
93
+ ### `inbox.wait_for_otp(timeout?, sender?)`
94
+
95
+ Blocks until an email with an OTP arrives.
96
+
97
+ ### `inbox.wait_for_email(timeout?, sender?, subject?)`
98
+
99
+ Blocks until any email arrives.
100
+
101
+ ### `inbox.list_emails()`
102
+
103
+ List all emails in the inbox.
104
+
105
+ Full API docs: [docs.lumbox.co](https://docs.lumbox.co)
106
+
107
+ ## License
108
+
109
+ MIT
@@ -0,0 +1,4 @@
1
+ from lumbox.client import Lumbox, Inbox
2
+ from lumbox.tools import lumbox_tools, create_langchain_tools
3
+
4
+ __all__ = ["Lumbox", "Inbox", "lumbox_tools", "create_langchain_tools"]
@@ -0,0 +1,389 @@
1
+ """
2
+ Lumbox Python SDK — Email for AI agents.
3
+
4
+ Usage:
5
+ from lumbox import Lumbox
6
+
7
+ client = Lumbox(api_key="ak_...")
8
+ inbox = client.create_inbox(name="my-bot")
9
+ otp = inbox.wait_for_otp(sender="github.com", timeout=60)
10
+ print(otp["code"]) # "847291"
11
+ """
12
+
13
+ from __future__ import annotations
14
+ from dataclasses import dataclass, field
15
+ from typing import Any, Optional
16
+ import httpx
17
+
18
+
19
+ class LumboxError(Exception):
20
+ def __init__(self, status: int, body: Any):
21
+ self.status = status
22
+ self.body = body
23
+ super().__init__(f"API error {status}: {body}")
24
+
25
+
26
+ @dataclass
27
+ class Inbox:
28
+ """Handle to a single agent inbox. Use methods to read emails, wait for OTPs, etc."""
29
+
30
+ id: str
31
+ address: str
32
+ name: Optional[str]
33
+ status: str
34
+ created_at: str
35
+ _client: "Lumbox" = field(repr=False)
36
+
37
+ def list_emails(
38
+ self,
39
+ *,
40
+ sender: Optional[str] = None,
41
+ category: Optional[str] = None,
42
+ since: Optional[str] = None,
43
+ unread: bool = False,
44
+ limit: int = 20,
45
+ ) -> dict:
46
+ """List emails in this inbox."""
47
+ params: dict[str, Any] = {"limit": limit}
48
+ if sender:
49
+ params["from"] = sender
50
+ if category:
51
+ params["category"] = category
52
+ if since:
53
+ params["since"] = since
54
+ if unread:
55
+ params["unread"] = "true"
56
+ return self._client._get(f"/v1/inboxes/{self.id}/emails", params=params)
57
+
58
+ def get_email(self, email_id: str) -> dict:
59
+ """Get full email content including parsed data."""
60
+ return self._client._get(f"/v1/inboxes/{self.id}/emails/{email_id}")
61
+
62
+ def wait_for_email(
63
+ self,
64
+ *,
65
+ sender: Optional[str] = None,
66
+ subject: Optional[str] = None,
67
+ category: Optional[str] = None,
68
+ has_otp: bool = False,
69
+ timeout: int = 30,
70
+ ) -> dict:
71
+ """
72
+ Block until a matching email arrives. Returns the full parsed email.
73
+
74
+ This is the primary method for AI agents. After signing up on a platform:
75
+ email = inbox.wait_for_email(sender="github.com", timeout=60)
76
+ otp = email["parsed"]["otp_codes"][0]
77
+ """
78
+ params: dict[str, Any] = {"timeout": timeout}
79
+ if sender:
80
+ params["from"] = sender
81
+ if subject:
82
+ params["subject"] = subject
83
+ if category:
84
+ params["category"] = category
85
+ if has_otp:
86
+ params["has_otp"] = "true"
87
+ return self._client._get(f"/v1/inboxes/{self.id}/wait", params=params)
88
+
89
+ def wait_for_otp(
90
+ self,
91
+ *,
92
+ sender: Optional[str] = None,
93
+ timeout: int = 30,
94
+ ) -> dict:
95
+ """
96
+ Wait for an OTP/verification code. Returns just the code.
97
+
98
+ The simplest way for an agent to get a verification code:
99
+ result = inbox.wait_for_otp(sender="github.com", timeout=60)
100
+ code = result["code"] # "847291"
101
+ """
102
+ params: dict[str, Any] = {"timeout": timeout}
103
+ if sender:
104
+ params["from"] = sender
105
+ return self._client._get(f"/v1/inboxes/{self.id}/otp", params=params)
106
+
107
+ def delete(self) -> dict:
108
+ """Delete this inbox and all its emails."""
109
+ return self._client._delete(f"/v1/inboxes/{self.id}")
110
+
111
+
112
+ class Lumbox:
113
+ """
114
+ Lumbox client — create email inboxes for AI agents.
115
+
116
+ Args:
117
+ api_key: Your Lumbox API key (starts with 'ak_')
118
+ base_url: API base URL (default: https://api.lumbox.co)
119
+ """
120
+
121
+ def __init__(
122
+ self,
123
+ api_key: str,
124
+ base_url: str = "https://api.lumbox.co",
125
+ ):
126
+ self._api_key = api_key
127
+ self._base_url = base_url.rstrip("/")
128
+ self._http = httpx.Client(
129
+ base_url=self._base_url,
130
+ headers={
131
+ "Content-Type": "application/json",
132
+ "Authorization": f"Bearer {api_key}",
133
+ },
134
+ timeout=httpx.Timeout(130), # long timeout for /wait endpoints
135
+ )
136
+ self.inboxes = _InboxesNamespace(self)
137
+
138
+ def _get(self, path: str, params: Optional[dict] = None) -> dict:
139
+ resp = self._http.get(path, params=params)
140
+ data = resp.json()
141
+ if not resp.is_success:
142
+ raise LumboxError(resp.status_code, data)
143
+ return data
144
+
145
+ def _post(
146
+ self, path: str, json: Optional[dict] = None, idempotency_key: Optional[str] = None
147
+ ) -> dict:
148
+ headers = {"Idempotency-Key": idempotency_key} if idempotency_key else None
149
+ resp = self._http.post(path, json=json or {}, headers=headers)
150
+ data = resp.json()
151
+ if not resp.is_success:
152
+ raise LumboxError(resp.status_code, data)
153
+ return data
154
+
155
+ def _delete(self, path: str) -> dict:
156
+ resp = self._http.delete(path)
157
+ data = resp.json()
158
+ if not resp.is_success:
159
+ raise LumboxError(resp.status_code, data)
160
+ return data
161
+
162
+ def _to_inbox(self, data: dict) -> Inbox:
163
+ return Inbox(
164
+ id=data["id"],
165
+ address=data["address"],
166
+ name=data.get("name"),
167
+ status=data["status"],
168
+ created_at=data["created_at"],
169
+ _client=self,
170
+ )
171
+
172
+ # --- Inboxes ---
173
+
174
+ def create_inbox(
175
+ self,
176
+ *,
177
+ name: Optional[str] = None,
178
+ domain: Optional[str] = None,
179
+ local_part: Optional[str] = None,
180
+ ) -> Inbox:
181
+ """Create a new email inbox for an AI agent."""
182
+ body: dict[str, Any] = {}
183
+ if name:
184
+ body["name"] = name
185
+ if domain:
186
+ body["domain"] = domain
187
+ if local_part:
188
+ body["local_part"] = local_part
189
+ data = self._post("/v1/inboxes", json=body)
190
+ return self._to_inbox(data)
191
+
192
+ def list_inboxes(self) -> list[Inbox]:
193
+ """List all inboxes."""
194
+ res = self._get("/v1/inboxes")
195
+ return [self._to_inbox(d) for d in res["data"]]
196
+
197
+ def get_inbox(self, inbox_id: str) -> Inbox:
198
+ """Get an inbox by ID."""
199
+ data = self._get(f"/v1/inboxes/{inbox_id}")
200
+ return self._to_inbox(data)
201
+
202
+ # --- Domains ---
203
+
204
+ def add_domain(self, domain: str) -> dict:
205
+ """Add a custom domain. Returns DNS records to configure."""
206
+ return self._post("/v1/domains", json={"domain": domain})
207
+
208
+ def verify_domain(self, domain_id: str) -> dict:
209
+ """Verify DNS records and activate a domain."""
210
+ return self._post(f"/v1/domains/{domain_id}/verify")
211
+
212
+ def list_domains(self) -> dict:
213
+ """List all custom domains."""
214
+ return self._get("/v1/domains")
215
+
216
+ # --- Search ---
217
+
218
+ def search_emails(
219
+ self,
220
+ *,
221
+ query: Optional[str] = None,
222
+ sender: Optional[str] = None,
223
+ category: Optional[str] = None,
224
+ inbox_id: Optional[str] = None,
225
+ ) -> dict:
226
+ """Search emails across all inboxes."""
227
+ params: dict[str, Any] = {}
228
+ if query:
229
+ params["q"] = query
230
+ if sender:
231
+ params["from"] = sender
232
+ if category:
233
+ params["category"] = category
234
+ if inbox_id:
235
+ params["inbox_id"] = inbox_id
236
+ return self._get("/v1/emails", params=params)
237
+
238
+ def _patch(self, path: str, json: Optional[dict] = None) -> dict:
239
+ resp = self._http.patch(path, json=json or {})
240
+ data = resp.json()
241
+ if not resp.is_success:
242
+ raise LumboxError(resp.status_code, data)
243
+ return data
244
+
245
+ # --- SMTP Configuration (BYOS) ---
246
+
247
+ def configure_smtp(
248
+ self,
249
+ *,
250
+ provider: str,
251
+ name: Optional[str] = None,
252
+ host: Optional[str] = None,
253
+ port: Optional[int] = None,
254
+ username: Optional[str] = None,
255
+ password: Optional[str] = None,
256
+ api_key: Optional[str] = None,
257
+ region: Optional[str] = None,
258
+ from_email: Optional[str] = None,
259
+ is_default: bool = False,
260
+ ) -> dict:
261
+ """Configure a custom SMTP provider (BYOS)."""
262
+ body: dict[str, Any] = {"provider": provider, "is_default": is_default}
263
+ if name:
264
+ body["name"] = name
265
+ if host:
266
+ body["host"] = host
267
+ if port:
268
+ body["port"] = port
269
+ if username:
270
+ body["username"] = username
271
+ if password:
272
+ body["password"] = password
273
+ if api_key:
274
+ body["api_key"] = api_key
275
+ if region:
276
+ body["region"] = region
277
+ if from_email:
278
+ body["from_email"] = from_email
279
+ return self._post("/v1/smtp-configs", json=body)
280
+
281
+ def list_smtp_configs(self) -> dict:
282
+ """List all SMTP configurations."""
283
+ return self._get("/v1/smtp-configs")
284
+
285
+ def test_smtp_config(self, config_id: str, to: str) -> dict:
286
+ """Send a test email through an SMTP config."""
287
+ return self._post(f"/v1/smtp-configs/{config_id}/test", json={"to": to})
288
+
289
+ # --- Thread Routing ---
290
+
291
+ def assign_thread(
292
+ self,
293
+ thread_id: str,
294
+ agent_id: str,
295
+ *,
296
+ role: Optional[str] = None,
297
+ ) -> dict:
298
+ """Assign an agent to a thread."""
299
+ body: dict[str, Any] = {"agent_id": agent_id}
300
+ if role:
301
+ body["role"] = role
302
+ return self._post(f"/v1/threads/{thread_id}/assign", json=body)
303
+
304
+ def unassign_thread(self, thread_id: str, agent_id: str) -> dict:
305
+ """Unassign an agent from a thread."""
306
+ return self._post(f"/v1/threads/{thread_id}/unassign", json={"agent_id": agent_id})
307
+
308
+ def list_thread_agents(self, thread_id: str) -> dict:
309
+ """List agents assigned to a thread."""
310
+ return self._get(f"/v1/threads/{thread_id}/agents")
311
+
312
+ def create_routing_rule(
313
+ self,
314
+ *,
315
+ name: str,
316
+ conditions: dict,
317
+ target_agent_id: Optional[str] = None,
318
+ target_inbox_id: Optional[str] = None,
319
+ priority: int = 0,
320
+ ) -> dict:
321
+ """Create an auto-routing rule for thread assignment."""
322
+ body: dict[str, Any] = {
323
+ "name": name,
324
+ "conditions": conditions,
325
+ "priority": priority,
326
+ }
327
+ if target_agent_id:
328
+ body["target_agent_id"] = target_agent_id
329
+ if target_inbox_id:
330
+ body["target_inbox_id"] = target_inbox_id
331
+ return self._post("/v1/routing-rules", json=body)
332
+
333
+ def list_routing_rules(self) -> dict:
334
+ """List all routing rules."""
335
+ return self._get("/v1/routing-rules")
336
+
337
+ def delete_routing_rule(self, rule_id: str) -> dict:
338
+ """Delete a routing rule."""
339
+ return self._delete(f"/v1/routing-rules/{rule_id}")
340
+
341
+ # --- Webhooks ---
342
+
343
+ def create_webhook(self, url: str, *, events: Optional[list[str]] = None) -> dict:
344
+ """Register a webhook URL for real-time notifications."""
345
+ body: dict[str, Any] = {"url": url}
346
+ if events:
347
+ body["events"] = events
348
+ return self._post("/v1/webhooks", json=body)
349
+
350
+ def list_webhooks(self) -> dict:
351
+ """List all webhooks."""
352
+ return self._get("/v1/webhooks")
353
+
354
+ def test_webhook(self, webhook_id: str) -> dict:
355
+ """Send a test payload to a webhook."""
356
+ return self._post(f"/v1/webhooks/{webhook_id}/test")
357
+
358
+ def delete_webhook(self, webhook_id: str) -> dict:
359
+ """Delete a webhook."""
360
+ return self._delete(f"/v1/webhooks/{webhook_id}")
361
+
362
+
363
+ class _InboxApiKeys:
364
+ """Manage API keys scoped to a single inbox."""
365
+
366
+ def __init__(self, client: "Lumbox"):
367
+ self._client = client
368
+
369
+ def create(self, inbox_id: str, *, name: str) -> dict:
370
+ """Mint a new key locked to this inbox. The plaintext key is returned only once."""
371
+ return self._client._post(
372
+ f"/v1/inboxes/{inbox_id}/api-keys",
373
+ json={"name": name},
374
+ )
375
+
376
+ def list(self, inbox_id: str) -> dict:
377
+ """List keys scoped to this inbox."""
378
+ return self._client._get(f"/v1/inboxes/{inbox_id}/api-keys")
379
+
380
+ def delete(self, inbox_id: str, key_id: str) -> dict:
381
+ """Revoke an inbox-scoped key."""
382
+ return self._client._delete(f"/v1/inboxes/{inbox_id}/api-keys/{key_id}")
383
+
384
+
385
+ class _InboxesNamespace:
386
+ """Namespace accessor: ``client.inboxes.api_keys.create(...)``."""
387
+
388
+ def __init__(self, client: "Lumbox"):
389
+ self.api_keys = _InboxApiKeys(client)
@@ -0,0 +1,255 @@
1
+ """
2
+ Pre-built tool wrappers for popular AI agent frameworks.
3
+
4
+ Works with: LangChain, CrewAI, OpenAI Agents SDK, Vercel AI SDK, LlamaIndex, AutoGen
5
+
6
+ Usage (LangChain):
7
+ from lumbox import create_langchain_tools
8
+ tools = create_langchain_tools(api_key="ak_...")
9
+
10
+ Usage (any framework — raw function tools):
11
+ from lumbox import lumbox_tools
12
+ tools = lumbox_tools(api_key="ak_...")
13
+ """
14
+
15
+ from __future__ import annotations
16
+ from typing import Optional
17
+ from lumbox.client import Lumbox
18
+
19
+
20
+ def lumbox_tools(
21
+ api_key: str,
22
+ base_url: str = "https://api.lumbox.co",
23
+ ) -> dict:
24
+ """
25
+ Returns a dict of plain functions that any framework can use as tools.
26
+ Each function has proper type hints and docstrings for schema generation.
27
+
28
+ Returns:
29
+ {
30
+ "create_inbox": <function>,
31
+ "wait_for_email": <function>,
32
+ "get_otp": <function>,
33
+ "list_emails": <function>,
34
+ "read_email": <function>,
35
+ "search_emails": <function>,
36
+ "add_domain": <function>,
37
+ "verify_domain": <function>,
38
+ }
39
+ """
40
+ client = Lumbox(api_key=api_key, base_url=base_url)
41
+
42
+ def create_inbox(name: str = "", domain: str = "") -> str:
43
+ """Create a new email inbox for an AI agent.
44
+
45
+ Args:
46
+ name: Human-readable name like 'github-signup-bot'. Used as the email local part.
47
+ domain: Custom domain if configured. Leave empty for default domain.
48
+
49
+ Returns:
50
+ JSON with inbox id, email address, and status.
51
+ """
52
+ inbox = client.create_inbox(
53
+ name=name or None,
54
+ domain=domain or None,
55
+ )
56
+ return f'{{"id": "{inbox.id}", "address": "{inbox.address}", "name": "{inbox.name}", "status": "{inbox.status}"}}'
57
+
58
+ def wait_for_email(
59
+ inbox_id: str,
60
+ sender: str = "",
61
+ subject: str = "",
62
+ category: str = "",
63
+ timeout: int = 30,
64
+ ) -> str:
65
+ """Wait for an email to arrive in an inbox. Blocks until received or timeout.
66
+
67
+ Use this after signing up on a platform to get the verification email.
68
+
69
+ Args:
70
+ inbox_id: The inbox ID to watch (e.g., inb_abc123).
71
+ sender: Filter by sender address or domain (e.g., 'github.com').
72
+ subject: Filter by subject keyword.
73
+ category: Filter by category: verification, security, notification, transactional, newsletter.
74
+ timeout: Max seconds to wait (default 30, max 120).
75
+
76
+ Returns:
77
+ JSON with full email data including parsed OTP codes and verification links.
78
+ """
79
+ import json
80
+
81
+ inbox = client.get_inbox(inbox_id)
82
+ result = inbox.wait_for_email(
83
+ sender=sender or None,
84
+ subject=subject or None,
85
+ category=category or None,
86
+ timeout=timeout,
87
+ )
88
+ return json.dumps(result, indent=2)
89
+
90
+ def get_otp(
91
+ inbox_id: str,
92
+ sender: str = "",
93
+ timeout: int = 30,
94
+ ) -> str:
95
+ """Get the latest OTP/verification code from an inbox. Blocks until one arrives.
96
+
97
+ The simplest way for an agent to get a verification code.
98
+
99
+ Args:
100
+ inbox_id: The inbox ID.
101
+ sender: Filter by sender (e.g., 'github.com').
102
+ timeout: Max seconds to wait (default 30, max 120).
103
+
104
+ Returns:
105
+ JSON with code, sender, subject, and expiry time.
106
+ """
107
+ import json
108
+
109
+ inbox = client.get_inbox(inbox_id)
110
+ result = inbox.wait_for_otp(sender=sender or None, timeout=timeout)
111
+ return json.dumps(result, indent=2)
112
+
113
+ def list_emails(
114
+ inbox_id: str,
115
+ sender: str = "",
116
+ category: str = "",
117
+ unread: bool = False,
118
+ limit: int = 10,
119
+ ) -> str:
120
+ """List emails in an inbox.
121
+
122
+ Args:
123
+ inbox_id: The inbox ID.
124
+ sender: Filter by sender.
125
+ category: Filter by category.
126
+ unread: Only show unread emails.
127
+ limit: Max emails to return (default 10).
128
+
129
+ Returns:
130
+ JSON array of emails with parsed data.
131
+ """
132
+ import json
133
+
134
+ inbox = client.get_inbox(inbox_id)
135
+ result = inbox.list_emails(
136
+ sender=sender or None,
137
+ category=category or None,
138
+ unread=unread,
139
+ limit=limit,
140
+ )
141
+ return json.dumps(result, indent=2)
142
+
143
+ def read_email(inbox_id: str, email_id: str) -> str:
144
+ """Read the full content of a specific email.
145
+
146
+ Args:
147
+ inbox_id: The inbox ID.
148
+ email_id: The email ID.
149
+
150
+ Returns:
151
+ JSON with full email content, parsed OTP codes, verification links, and AI summary.
152
+ """
153
+ import json
154
+
155
+ inbox = client.get_inbox(inbox_id)
156
+ result = inbox.get_email(email_id)
157
+ return json.dumps(result, indent=2)
158
+
159
+ def search_emails(
160
+ query: str = "",
161
+ sender: str = "",
162
+ category: str = "",
163
+ inbox_id: str = "",
164
+ ) -> str:
165
+ """Search emails across all inboxes.
166
+
167
+ Args:
168
+ query: Search query (searches subject, body, summary).
169
+ sender: Filter by sender.
170
+ category: Filter by category.
171
+ inbox_id: Limit to a specific inbox.
172
+
173
+ Returns:
174
+ JSON array of matching emails.
175
+ """
176
+ import json
177
+
178
+ result = client.search_emails(
179
+ query=query or None,
180
+ sender=sender or None,
181
+ category=category or None,
182
+ inbox_id=inbox_id or None,
183
+ )
184
+ return json.dumps(result, indent=2)
185
+
186
+ def add_domain(domain: str) -> str:
187
+ """Add a custom domain for agent email addresses.
188
+
189
+ Args:
190
+ domain: The domain to add (e.g., 'mycompany.com').
191
+
192
+ Returns:
193
+ JSON with domain ID and DNS records to configure.
194
+ """
195
+ import json
196
+
197
+ result = client.add_domain(domain)
198
+ return json.dumps(result, indent=2)
199
+
200
+ def verify_domain(domain_id: str) -> str:
201
+ """Verify DNS records and activate a custom domain.
202
+
203
+ Args:
204
+ domain_id: The domain ID to verify.
205
+
206
+ Returns:
207
+ JSON with verification status for each DNS record.
208
+ """
209
+ import json
210
+
211
+ result = client.verify_domain(domain_id)
212
+ return json.dumps(result, indent=2)
213
+
214
+ return {
215
+ "create_inbox": create_inbox,
216
+ "wait_for_email": wait_for_email,
217
+ "get_otp": get_otp,
218
+ "list_emails": list_emails,
219
+ "read_email": read_email,
220
+ "search_emails": search_emails,
221
+ "add_domain": add_domain,
222
+ "verify_domain": verify_domain,
223
+ }
224
+
225
+
226
+ def create_langchain_tools(
227
+ api_key: str,
228
+ base_url: str = "https://api.lumbox.co",
229
+ ):
230
+ """
231
+ Create LangChain-compatible tools for Lumbox.
232
+
233
+ Usage:
234
+ from lumbox import create_langchain_tools
235
+ from langchain_openai import ChatOpenAI
236
+ from langchain.agents import create_tool_calling_agent, AgentExecutor
237
+
238
+ tools = create_langchain_tools(api_key="ak_...")
239
+ llm = ChatOpenAI(model="gpt-4o")
240
+ agent = create_tool_calling_agent(llm, tools, prompt)
241
+ executor = AgentExecutor(agent=agent, tools=tools)
242
+ """
243
+ try:
244
+ from langchain_core.tools import StructuredTool
245
+ except ImportError:
246
+ raise ImportError(
247
+ "langchain-core is required. Install with: pip install lumbox[langchain]"
248
+ )
249
+
250
+ raw_tools = lumbox_tools(api_key=api_key, base_url=base_url)
251
+
252
+ return [
253
+ StructuredTool.from_function(func, name=name)
254
+ for name, func in raw_tools.items()
255
+ ]
@@ -0,0 +1,134 @@
1
+ Metadata-Version: 2.4
2
+ Name: lumbox
3
+ Version: 0.3.0
4
+ Summary: Lumbox SDK — Email for AI agents. Create inboxes, receive OTPs, send replies.
5
+ License-Expression: MIT
6
+ Project-URL: Homepage, https://lumbox.co
7
+ Project-URL: Repository, https://github.com/kumard3/agentinbox
8
+ Project-URL: Documentation, https://docs.lumbox.co
9
+ Keywords: email,ai,agents,otp,mcp,lumbox,verification
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.9
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Requires-Python: >=3.9
19
+ Description-Content-Type: text/markdown
20
+ Requires-Dist: httpx>=0.27.0
21
+ Provides-Extra: langchain
22
+ Requires-Dist: langchain-core>=0.3.0; extra == "langchain"
23
+ Provides-Extra: crewai
24
+ Requires-Dist: crewai>=0.80.0; extra == "crewai"
25
+
26
+ # lumbox
27
+
28
+ Email for AI agents. Create inboxes, receive OTPs, extract verification codes — all via API.
29
+
30
+ ## Install
31
+
32
+ ```bash
33
+ pip install lumbox
34
+ ```
35
+
36
+ ## Quick Start
37
+
38
+ ```python
39
+ from lumbox import Lumbox
40
+
41
+ client = Lumbox(api_key="ak_...")
42
+
43
+ # Create an inbox
44
+ inbox = client.create_inbox(name="github-bot")
45
+ # → github-bot@lumbox.co
46
+
47
+ # Sign up on any service with inbox.address...
48
+
49
+ # Wait for the OTP (blocks until it arrives)
50
+ otp = inbox.wait_for_otp(timeout=60)
51
+ print(otp["code"]) # "847291"
52
+ ```
53
+
54
+ ## Auth header
55
+
56
+ The SDK sends `Authorization: Bearer ak_...` by default. The legacy
57
+ `X-API-Key` header still works server-side. Raw HTTP example:
58
+
59
+ ```bash
60
+ curl https://api.lumbox.co/v1/inboxes \
61
+ -H "Authorization: Bearer ak_your_key_here"
62
+ ```
63
+
64
+ ## Inbox-scoped API keys
65
+
66
+ Mint a key that can only ever touch one inbox:
67
+
68
+ ```python
69
+ created = client.inboxes.api_keys.create(inbox.id, name="ci-bot")
70
+ print(created["api_key"]) # shown once
71
+
72
+ client.inboxes.api_keys.list(inbox.id)
73
+ client.inboxes.api_keys.delete(inbox.id, created["id"])
74
+ ```
75
+
76
+ ## Features
77
+
78
+ - **OTP extraction** — verification codes parsed automatically
79
+ - **Long-poll** — `wait_for_email()` and `wait_for_otp()` block until arrival
80
+ - **Send/reply/forward** — full outbound email support
81
+ - **Custom domains** — DKIM, SPF, DMARC verification
82
+ - **LangChain & CrewAI** — pre-built tool wrappers
83
+
84
+ ## Framework Integration
85
+
86
+ ### LangChain
87
+
88
+ ```bash
89
+ pip install lumbox[langchain]
90
+ ```
91
+
92
+ ```python
93
+ from lumbox import create_langchain_tools
94
+
95
+ tools = create_langchain_tools(api_key="ak_...")
96
+ # Use tools directly with LangChain agents
97
+ ```
98
+
99
+ ### Any Framework
100
+
101
+ ```python
102
+ from lumbox import lumbox_tools
103
+
104
+ tools = lumbox_tools(api_key="ak_...")
105
+ # Dict of plain functions with proper type hints
106
+ ```
107
+
108
+ ## API
109
+
110
+ ### `Lumbox(api_key, base_url?)`
111
+
112
+ Create a client. `base_url` defaults to `https://api.lumbox.co`.
113
+
114
+ ### `client.create_inbox(name?, domain?)`
115
+
116
+ Returns an `Inbox` object with `.address`, `.id`, and convenience methods.
117
+
118
+ ### `inbox.wait_for_otp(timeout?, sender?)`
119
+
120
+ Blocks until an email with an OTP arrives.
121
+
122
+ ### `inbox.wait_for_email(timeout?, sender?, subject?)`
123
+
124
+ Blocks until any email arrives.
125
+
126
+ ### `inbox.list_emails()`
127
+
128
+ List all emails in the inbox.
129
+
130
+ Full API docs: [docs.lumbox.co](https://docs.lumbox.co)
131
+
132
+ ## License
133
+
134
+ MIT
@@ -0,0 +1,10 @@
1
+ README.md
2
+ pyproject.toml
3
+ lumbox/__init__.py
4
+ lumbox/client.py
5
+ lumbox/tools.py
6
+ lumbox.egg-info/PKG-INFO
7
+ lumbox.egg-info/SOURCES.txt
8
+ lumbox.egg-info/dependency_links.txt
9
+ lumbox.egg-info/requires.txt
10
+ lumbox.egg-info/top_level.txt
@@ -0,0 +1,7 @@
1
+ httpx>=0.27.0
2
+
3
+ [crewai]
4
+ crewai>=0.80.0
5
+
6
+ [langchain]
7
+ langchain-core>=0.3.0
@@ -0,0 +1 @@
1
+ lumbox
@@ -0,0 +1,32 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "lumbox"
7
+ version = "0.3.0"
8
+ description = "Lumbox SDK — Email for AI agents. Create inboxes, receive OTPs, send replies."
9
+ readme = "README.md"
10
+ license = "MIT"
11
+ requires-python = ">=3.9"
12
+ dependencies = ["httpx>=0.27.0"]
13
+ keywords = ["email", "ai", "agents", "otp", "mcp", "lumbox", "verification"]
14
+ classifiers = [
15
+ "Development Status :: 4 - Beta",
16
+ "Intended Audience :: Developers",
17
+ "Programming Language :: Python :: 3",
18
+ "Programming Language :: Python :: 3.9",
19
+ "Programming Language :: Python :: 3.10",
20
+ "Programming Language :: Python :: 3.11",
21
+ "Programming Language :: Python :: 3.12",
22
+ "Programming Language :: Python :: 3.13",
23
+ ]
24
+
25
+ [project.urls]
26
+ Homepage = "https://lumbox.co"
27
+ Repository = "https://github.com/kumard3/agentinbox"
28
+ Documentation = "https://docs.lumbox.co"
29
+
30
+ [project.optional-dependencies]
31
+ langchain = ["langchain-core>=0.3.0"]
32
+ crewai = ["crewai>=0.80.0"]
lumbox-0.3.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+