Illustration for giving your python agent its own email with lobstermail

giving your python agent its own email with lobstermail

Step-by-step tutorial for the LobsterMail Python SDK — provision an inbox, receive emails with injection risk scoring, send messages, and handle verification codes.

6 min read

Python is where most agents live. LangChain, AutoGen, CrewAI, smolagents — the whole ML ecosystem is Python-first. So when your agent needs to interact over email (sign up for a service, catch a verification code, receive replies from a vendor), you want the setup to be a few lines of code, not an afternoon in Google Cloud Console.

This tutorial covers the LobsterMail Python SDK from scratch. By the end, your agent will have its own @lobstermail.ai address and be able to receive and send email, including the verification code pattern that breaks most agent workflows.

Why not just use Gmail?#

The blocker with Gmail is OAuth. OAuth requires a real human to open a browser, authorize the app, and click Allow. For apps people install themselves, that flow is fine. For an agent running unattended at midnight, it's a dead end.

LobsterMail skips the OAuth handshake entirely. The agent provisions its own inbox through a single SDK call. No human account creation, no browser session, nothing to pre-configure before running.

Install#

pip install lobstermail

On first run, the SDK automatically creates a free account and saves a token to ~/.lobstermail/token. Nothing else to set up. If you'd rather use explicit credentials (for example, in a serverless environment with no persistent storage), set LOBSTERMAIL_API_KEY in your environment and the SDK picks it up. Either way, there's no dashboard step before you can write code.

Provisioning an inbox#

import asyncio
from lobstermail import LobsterMail

async def main():
    lm = await LobsterMail.create()
    inbox = await lm.create_smart_inbox(name="my-agent")
    print(inbox.address)  # my-agent@lobstermail.ai

asyncio.run(main())

create_smart_inbox() generates a readable address from your agent's name and handles collisions automatically. If my-agent@lobstermail.ai is taken, the SDK tries variations (my-agent1, m-agent, and so on) until something is available. You get a clean address without writing any availability checks yourself.

For one-off tasks where the address only needs to exist for a few minutes, create_inbox() gives you a random address like lobster-a4f2@lobstermail.ai. Sign up for something, grab the code, move on.

Receiving emails#

emails = await inbox.receive()
for email in emails:
    print(email.subject)
    print(email.from_address)
    print(email.body)
    print(email.injection_risk)  # 'low' | 'medium' | 'high'

The injection_risk score is something I didn't expect to care about until I did. Every email LobsterMail delivers comes with a score rating how likely the content contains prompt injection patterns — things written to look like instructions hidden inside normal-looking email text. If your agent is reading email bodies and passing them into a model prompt (which it almost certainly is), a high score (above 0.7, roughly) is a signal to quote the content explicitly or route it through a human review step before acting on it.

This is specific to agent workloads. You won't find it in SendGrid or Resend.

Sending emails#

await inbox.send(
    to="vendor@example.com",
    subject="Quote request for Q2",
    body="Hi, I'm reaching out on behalf of Acme Corp...",
)

The free tier includes 1,000 emails a month with no credit card. Builder ($9/month) gets you 5,000 emails and up to 10 inboxes — enough for a small fleet of specialized agents each with a dedicated address.

Handling verification codes#

This is the scenario that breaks agent workflows most often. The agent registers for a service, a confirmation email lands somewhere it can't check, and the whole thing stalls. Here's a clean way to handle it:

import asyncio
import re
from lobstermail import LobsterMail

async def register_and_verify(service_name: str, registration_fn):
    lm = await LobsterMail.create()
    inbox = await lm.create_smart_inbox(name=f"agent-{service_name.lower()}")
    print(f"Inbox ready: {inbox.address}")

    # Your registration function uses inbox.address as the email field
    await registration_fn(email=inbox.address)

    # Poll for the verification email — 60-second window
    for _ in range(12):
        emails = await inbox.receive()
        for email in emails:
            keywords = ("verify", "confirm", "activate", "welcome")
            if any(kw in email.subject.lower() for kw in keywords):
                match = re.search(r"\b(\d{4,8})\b", email.body)
                if match:
                    return match.group(1)
        await asyncio.sleep(5)

    raise TimeoutError(f"No verification email from {service_name} after 60s")

The 12-iteration loop with 5-second pauses gives a 60-second window. Most services deliver within 10 seconds, but this is conservative enough for slower platforms without blocking for minutes. Tune the interval and count to fit your use case.

Wiring into LangChain or AutoGen#

The inbox object is framework-agnostic. For LangChain agents with email tools, initialize the inbox before the agent loop, inject inbox.address wherever contact details are needed, and wrap inbox.receive() and inbox.send() as callable tools.

AutoGen agents follow the same pattern. Create the inbox before the conversation starts, pass the address into the agent's system context, and expose receive() as a tool it can call mid-task when it needs to check for a reply.

Managing inboxes#

# List all inboxes on this account
inboxes = await lm.list_inboxes()
for i in inboxes:
    print(i.address, i.created_at)

# Delete when the task is done
await lm.delete_inbox(inbox.id)

If you're spinning up ephemeral inboxes for one-off tasks, cleaning them up keeps things tidy. The free tier gives you one inbox; Builder gives you ten.

Async by default#

The SDK is async-first. Most modern agent frameworks are too, so this usually works without any adjustment. For quick scripts, asyncio.run() at the top level handles it. In Jupyter notebooks, you can await directly in cells since Jupyter runs its own event loop. If you hit "event loop already running" errors, one line of nest_asyncio.apply() resolves it.

There's no official sync wrapper in the current release. If you're stuck in a fully synchronous codebase, wrapping individual calls in asyncio.run() works for low-volume use cases until a sync client lands.

Frequently asked questions

Does LobsterMail have a Python SDK, or is it TypeScript only?

LobsterMail supports both. Install the Python package with pip install lobstermail. The TypeScript package is available as @lobsterkit/lobstermail on npm. Both SDKs expose the same core API.

Do I need to create an account before using the Python SDK?

No. On first use, LobsterMail.create() automatically signs up for a free account and saves the token to ~/.lobstermail/token. Your agent provisions itself — no dashboard, no email confirmation, no human in the loop.

What's the difference between create_smart_inbox() and create_inbox()?

create_smart_inbox(name="...") generates a human-readable address based on the name you provide and handles collisions automatically. create_inbox() gives you a random address like lobster-a4f2@lobstermail.ai immediately. Use smart inboxes for persistent agents with a public-facing address, and random inboxes for one-off tasks.

What does the injection_risk score actually measure?

It scores how likely an email's content contains prompt injection patterns — text written to look like user or system instructions that could hijack your agent's behavior. A score above 0.7 is worth treating carefully: quote the content explicitly in your prompt rather than inserting it raw. See the security and injection docs for more detail.

How many emails can I receive on the free plan?

The free plan includes 1,000 emails per month with one inbox, no credit card required. Builder ($9/month) gives you 5,000 emails and up to 10 inboxes. Neither plan restricts how many emails you can receive on a per-message basis — the limit is monthly volume.

Can I use LobsterMail in a serverless function or Docker container?

Yes. Set LOBSTERMAIL_API_KEY in your environment variables and the SDK uses that instead of looking for a token file. This is the right setup for any environment without persistent local storage — Lambda, Cloud Run, Railway, Docker, and so on.

Is there a way to receive emails in real time instead of polling?

Webhooks are available for real-time delivery. Instead of polling with inbox.receive(), you configure a webhook URL and LobsterMail POSTs each new email to your endpoint as it arrives. See the webhooks guide for setup.

Can I use my own domain instead of @lobstermail.ai?

Yes, custom domains are supported. You configure DNS records once and then provision inboxes on your own domain through the same SDK calls. The custom domains guide walks through the setup.

What Python versions are supported?

The SDK requires Python 3.9 or later. It uses asyncio throughout, which has been stable since 3.7, but some internal type hints require 3.9+.

Can an agent manage multiple inboxes?

Yes. Call lm.create_smart_inbox() or lm.create_inbox() multiple times — each call returns a distinct inbox object. The free tier supports one inbox; Builder supports up to ten. You can list and delete inboxes with lm.list_inboxes() and lm.delete_inbox(id).

What happens if my LobsterMail token is exposed?

Go to your account settings and rotate the token. The old token is immediately invalidated. Because the token is scoped to your account's inboxes only, exposure doesn't compromise anything outside LobsterMail — unlike a Google OAuth token that might have broader account access.

Does LobsterMail work with LangChain agents?

Yes. The SDK is framework-agnostic. Initialize the inbox before your agent loop, pass inbox.address into the agent's context, and expose inbox.receive() and inbox.send() as tools. The LangChain integration post has a worked example.


Give your agent its own email. Get started with LobsterMail — it's free.

Related posts