Metadata-Version: 2.4
Name: venumzmail
Version: 0.1.0
Summary: python sdk for the venumzmail temp email api
Requires-Python: >=3.7
Description-Content-Type: text/markdown
Requires-Dist: requests
Dynamic: description
Dynamic: description-content-type
Dynamic: requires-dist
Dynamic: requires-python
Dynamic: summary

# VenumzMail Docs

[VenumzMail](https://venumzmail.xyz)'s Python SDK and CLI tool. Create inboxes, read messages, extract OTPs, stream live mail, manage custom domains etc.. Everything which is possible through our API!

---

## Contents

- [Install](#install)
- [Auth](#auth)
- [SDK](#sdk)
  - [VMail](#vmail)
  - [create_inbox](#create_inbox)
  - [create_inboxes](#create_inboxes)
  - [list_inboxes](#list_inboxes)
  - [reactivate](#reactivate)
  - [delete_inboxes](#delete_inboxes)
  - [get_messages](#get_messages)
  - [get_message](#get_message)
  - [wait_for_message](#wait_for_message)
  - [wait_for_otp](#wait_for_otp)
  - [stream](#stream)
  - [email_login](#email_login)
  - [email_stream](#email_stream)
  - [download_attachment](#download_attachment)
  - [me](#me)
  - [Domains](#domains)
  - [Subdomains](#subdomains)
  - [Objects](#objects)
  - [Errors](#errors)
- [CLI](#cli)

---

## Install

```bash
pip install venumzmail
```

---

## Auth

Pass your API key when creating a `VMail` instance. Keys always start with `vz-`.

```python
from venumzmail import VMail

m = VMail(api_key="vz-yourkey")
```

No key = **guest mode** — tracked by IP, limited to public inboxes and basic creation. Most features require a key.

```python
g = VMail()  # guest
```

---

## SDK

### VMail

```python
VMail(api_key=None, timeout=15)
```

| param | type | default | description |
|-------|------|---------|-------------|
| `api_key` | str | None | your API key, must start with `vz-` |
| `timeout` | int | 15 | request timeout in seconds |

```python
m = VMail(api_key="vz-yourkey")
m = VMail(api_key="vz-yourkey", timeout=30)
m = VMail()  # guest mode
```

---

### create_inbox

Create a single inbox.

```python
m.create_inbox(username=None, domain=None, type="private", username_length=None)
```

| param | type | default | description |
|-------|------|---------|-------------|
| `username` | str | None | custom username, random if omitted |
| `domain` | str | None | domain to use, random built-in if omitted |
| `type` | str | `"private"` | `"private"` or `"public"` |
| `username_length` | int | None | length of random username (3–25) |

Returns an `inbox` object.

```python
box = m.create_inbox()
box = m.create_inbox(username="myinbox", domain="bomboclato.store")
box = m.create_inbox(type="public")
box = m.create_inbox(username_length=15)

print(box.email)       # myinbox@bomboclato.store
print(box.expires_at)  # 2026-06-20T10:30:00Z
print(box.active)      # True
```

---

### create_inboxes

Create multiple inboxes in one call (up to 50).

```python
m.create_inboxes(count=1, usernames=None, domain=None, type="private")
```

| param | type | default | description |
|-------|------|---------|-------------|
| `count` | int | 1 | how many inboxes to create |
| `usernames` | list[str] | None | custom usernames, must match `count` if passed |
| `domain` | str | None | domain for all inboxes |
| `type` | str | `"private"` | `"private"` or `"public"` |

Returns a list of `inbox` objects.

```python
boxes = m.create_inboxes(count=5)

boxes = m.create_inboxes(
    count=3,
    usernames=["alice", "bob", "carol"],
    domain="bomboclato.store"
)

for box in boxes:
    print(box.email)
```

---

### list_inboxes

List all inboxes on your account.

```python
m.list_inboxes()
```

Returns a list of `inbox` objects.

```python
boxes = m.list_inboxes()
for box in boxes:
    print(box.email, box.active)
```

---

### reactivate

Extend an inbox's expiry.

```python
m.reactivate(email)
```

Returns a dict with the new `expires_at`.

```python
res = m.reactivate("myinbox@bomboclato.store")
print(res["expires_at"])
```

---

### delete_inboxes

Delete inboxes by email list or wipe the oldest N.

```python
m.delete_inboxes(emails=None, count=None)
```

Pass one of — not both.

```python
m.delete_inboxes(emails=["a@bomboclato.store", "b@bomboclato.store"])
m.delete_inboxes(count=3)  # deletes 3 oldest
```

---

### get_messages

Get all messages in an inbox.

```python
m.get_messages(email)
```

Returns a list of `message` objects (empty list if no mail yet).

```python
msgs = m.get_messages("myinbox@bomboclato.store")
for msg in msgs:
    print(msg.sender, msg.subject, msg.otp)
```

---

### get_message

Get a single message by its ID.

```python
m.get_message(message_id)
```

Returns a `message` object. Raises `notFoundError` if ID doesn't exist.

```python
msg = m.get_message("48f732e8-c2e0-477e-9073-03a33d89f2cc")
print(msg.body)
print(msg.body_html)
```

---

### wait_for_message

Poll an inbox until a message arrives or timeout is hit.

```python
m.wait_for_message(email, timeout=60, poll_every=2, subject_contains=None)
```

| param | type | default | description |
|-------|------|---------|-------------|
| `email` | str | required | inbox to watch |
| `timeout` | int | 60 | seconds before giving up |
| `poll_every` | int | 2 | seconds between checks |
| `subject_contains` | str | None | only match messages with this in the subject |

Returns a `message` object or `None` if timed out.

```python
msg = m.wait_for_message("myinbox@bomboclato.store")

msg = m.wait_for_message(
    "myinbox@bomboclato.store",
    timeout=120,
    poll_every=3,
    subject_contains="welcome"
)

if msg:
    print(msg.sender, msg.subject)
else:
    print("timed out")
```

---

### wait_for_otp

Poll an inbox until a message with an OTP is found.

```python
m.wait_for_otp(email, timeout=60, poll_every=2)
```

Returns the OTP as a plain string, or `None` if timed out.

```python
otp = m.wait_for_otp("myinbox@bomboclato.store")
print(otp)  # "482910"
```

---

### stream

Live stream incoming messages for one of your own inboxes via SSE. Blocks until you break or the connection drops.

```python
m.stream(email)
```

Yields `message` objects as they arrive.

```python
for msg in m.stream("myinbox@bomboclato.store"):
    print(msg.sender, msg.subject, msg.otp)
```

---

### email_login

Read a **public** inbox without an API key.

```python
VMail().email_login(email)
```

Returns a dict with inbox info + a `messages` key containing a list of `message` objects.

```python
g = VMail()
data = g.email_login("public-inbox@bomboclato.store")

print(data["email"])
print(data["active"])
print(data["expires_at"])
for msg in data["messages"]:
    print(msg.sender, msg.subject)
```

---

### email_stream

Live stream a **public** inbox without an API key.

```python
VMail().email_stream(email)
```

Yields `message` objects as they arrive.

```python
g = VMail()
for msg in g.email_stream("public-inbox@bomboclato.store"):
    print(msg.sender, msg.subject)
```

---

### download_attachment

Download an attachment by its ID.

```python
m.download_attachment(attachment_id, save_to=None, public=False)
```

| param | type | default | description |
|-------|------|---------|-------------|
| `attachment_id` | str | required | ID from `message.attachments` |
| `save_to` | str | None | file path to save to, returns path if given |
| `public` | bool | False | set True for attachments from public inboxes |

Returns the save path if `save_to` is given, otherwise raw `bytes`.

```python
# save to file
m.download_attachment("uuid-here", save_to="./invoice.pdf")

# get bytes
data = m.download_attachment("uuid-here")

# public inbox attachment, no key needed
g = VMail()
g.download_attachment("uuid-here", save_to="file.pdf", public=True)
```

---

### me

Get account info for the current API key.

```python
m.me()
```

Returns a raw dict.

```python
info = m.me()
print(info)
# {"email": "you@example.com", "plan": "shadow", "inboxes_used": 3, ...}
```

---

### Domains

Manage custom domains on your account. Requires a paid plan.

```python
m.add_domain(domain)     # register a domain
m.list_domains()         # list all registered domains
m.remove_domain(domain)  # remove a domain
```

Before `add_domain` works, you need to add an MX record on your domain:

```
Type: MX
Host: @
Value: mail.venumzmail.xyz
Priority: 10
```

```python
m.add_domain("example.com")

for d in m.list_domains():
    print(d["domain"], d["status"])

m.remove_domain("example.com")
```

---

### Subdomains

Create subdomains on venumzmail's built-in base domains. Requires a registered account.

```python
m.add_subdomain(label, base_domain)   # e.g. "team", "venumzmail.lol" → team.venumzmail.lol
m.list_subdomains()
m.remove_subdomain(subdomain)         # full subdomain string e.g. "team.venumzmail.lol"
```

```python
m.add_subdomain("team", "venumzmail.lol")

print(m.list_subdomains())

# create an inbox on your subdomain
box = m.create_inbox(domain="team.venumzmail.lol")
print(box.email)  # something@team.venumzmail.lol

m.remove_subdomain("team.venumzmail.lol")
```

---

### Objects

#### inbox

| attribute | type | description |
|-----------|------|-------------|
| `.email` | str | full email address |
| `.domain` | str | domain part |
| `.id` | str | unique inbox ID |
| `.active` | bool | whether inbox is active |
| `.encrypted` | bool | whether inbox is encrypted |
| `.expires_at` | str | ISO 8601 expiry timestamp |
| `.created_at` | str | ISO 8601 creation timestamp |

#### message

| attribute | type | description |
|-----------|------|-------------|
| `.id` | str | unique message ID |
| `.sender` | str | sender email |
| `.subject` | str | subject line |
| `.body` | str | plain text body |
| `.body_html` | str | HTML body |
| `.otp` | str or None | extracted OTP if found, else None |
| `.received_at` | str | ISO 8601 timestamp |
| `.attachments` | list[attachment] | list of attachment objects |

#### attachment

| attribute | type | description |
|-----------|------|-------------|
| `.id` | str | attachment ID (use with `download_attachment`) |
| `.filename` | str | original filename |
| `.size` | int | size in bytes |
| `.content_type` | str | MIME type e.g. `application/pdf` |

---

### Errors

All errors extend `venumzerror` and print a colored `error:` + `fix:` hint automatically.

| error | when |
|-------|------|
| `authError` | bad/missing key, guest trying a restricted feature |
| `rateLimitError` | too many requests for your plan |
| `notFoundError` | inbox, message, or attachment doesn't exist |
| `apiError` | any other 4xx/5xx — has `.status_code` |

```python
from venumzmail import authError, rateLimitError, notFoundError, apiError

try:
    msg = m.get_message("bad-id")
except notFoundError as e:
    print(e)         # prints error + fix hint
except rateLimitError:
    time.sleep(2)
    # retry
except authError as e:
    print("check your key")
except apiError as e:
    print(e.status_code)
```

---

## CLI

### Install

```bash
pip install venumzmail
```

### Auth

```bash
# inline
vmail -k vz-yourkey <command>

# or set once
export x-api-key=vz-yourkey
vmail <command>
```

No key = guest mode.

---

### Commands

#### `help`
Show all commands.
```bash
vmail help
```

#### `me`
Get account info.
```bash
vmail me
```

#### `create`
Create one or more inboxes.

```bash
vmail create                                              # random
vmail create -u myinbox -d bomboclato.store              # custom username + domain
vmail create -t public                                   # public inbox
vmail create -l 15                                       # random 15-char username
vmail create -n 5                                        # bulk, 5 random inboxes
vmail create -n 3 --usernames alice bob carol -d bomboclato.store
```

| flag | short | description |
|------|-------|-------------|
| `--username` | `-u` | custom username |
| `--domain` | `-d` | domain |
| `--type` | `-t` | `private` (default) or `public` |
| `--length` | `-l` | random username length |
| `--count` | `-n` | number of inboxes |
| `--usernames` | | space-separated names for bulk |

#### `list`
List all inboxes.
```bash
vmail list
```

#### `reactivate`
Extend inbox expiry.
```bash
vmail reactivate myinbox@bomboclato.store
```

#### `delete`
Delete inboxes.
```bash
vmail delete --emails a@bomboclato.store b@bomboclato.store
vmail delete --count 3   # deletes 3 oldest
```

#### `messages`
List messages in an inbox.
```bash
vmail messages myinbox@bomboclato.store
vmail messages myinbox@bomboclato.store --body   # include body preview
```

Output per message:
```
[id]  from=sender@x.com  subject='Hello'  otp=None  at=2026-06-20T09:00:00Z
```

#### `message`
Get a single message in full.
```bash
vmail message <message-id>
```

#### `wait`
Block until a message arrives.
```bash
vmail wait myinbox@bomboclato.store
vmail wait myinbox@bomboclato.store --otp                        # return just the OTP
vmail wait myinbox@bomboclato.store --subject "verify"           # filter by subject
vmail wait myinbox@bomboclato.store --timeout 120 --poll 5
```

| flag | default | description |
|------|---------|-------------|
| `--otp` | off | extract and print just the OTP |
| `--subject` | none | only match messages containing this text in subject |
| `--timeout` | 60 | seconds before giving up |
| `--poll` | 2 | seconds between checks |

#### `stream`
Live stream messages as they arrive. Ctrl+C to stop.
```bash
vmail stream myinbox@bomboclato.store
vmail stream public-inbox@bomboclato.store --public   # public inbox, no key needed
```

#### `download`
Download an attachment.
```bash
vmail download <attachment-id> -o ./invoice.pdf
vmail download <attachment-id>                  # prints byte count, doesn't save
vmail download <attachment-id> --public         # public inbox attachment
```

#### `login`
Read a public inbox without a key.
```bash
vmail login public-inbox@bomboclato.store
```

#### `domains`
Manage custom domains (paid plan only).
```bash
vmail domains                        # list
vmail domains --add example.com
vmail domains --remove example.com
```

#### `subdomains`
Manage subdomains on built-in base domains.
```bash
vmail subdomains                                  # list
vmail subdomains --add team venumzmail.lol        # creates team.venumzmail.lol
vmail subdomains --remove team.venumzmail.lol
