Skip to main content

Python SDK for the e2a protocol — email-to-agent authentication

Project description

e2a Python SDK

Python SDK for the e2a protocol — email-to-agent authentication.

Install

pip install e2a

Quick start

from e2a import E2AClient

# Reads E2A_API_KEY from environment automatically
client = E2AClient()

# Or pass explicitly:
# client = E2AClient(api_key="e2a_your_api_key")

Mount the webhook in your web framework:

FastAPI:

from fastapi import FastAPI, Request

app = FastAPI()

@app.post("/webhook")
async def webhook(request: Request):
    email = client.parse(await request.body())
    print(f"From: {email.sender}, Subject: {email.subject}")
    email.reply("Thanks for reaching out!")
    return {"ok": True}

Flask:

from flask import Flask, request

app = Flask(__name__)

@app.post("/webhook")
def webhook():
    email = client.parse(request.get_data())
    email.reply("Thanks for reaching out!")
    return {"ok": True}

Conversation threading

e2a supports an opaque conversation_id that lets your agent track multi-turn email threads. Pass it when replying, and e2a will include it in the webhook when the human responds.

@app.post("/webhook")
async def webhook(request: Request):
    email = client.parse(await request.body())

    if email.conversation_id:
        # Follow-up — route to existing conversation
        conversation = get_conversation(email.conversation_id)
    else:
        # First contact — create a new conversation
        conversation = create_conversation(sender=email.sender)

    response = conversation.generate_reply(email)

    # Tag the reply so future emails in this thread are linked
    email.reply(
        body=response.text,
        html_body=response.html,
        conversation_id=conversation.id,
    )
    return {"ok": True}

Works the same for outbound emails:

result = client.send(
    to="alice@example.com",
    subject="Following up",
    body="Hi Alice, just checking in.",
    conversation_id="conv_abc123",
)
# When Alice replies, the webhook will include conversation_id="conv_abc123"

Attachments

Receiving attachments

Inbound email attachments are automatically parsed and available on email.attachments:

email = client.parse(body)
for att in email.attachments:
    print(f"{att.filename} ({att.content_type}, {att.size} bytes)")
    save_file(att.filename, att.data)

Sending attachments

Pass Attachment objects when sending or replying:

from e2a import Attachment

# Read a file
with open("report.pdf", "rb") as f:
    pdf_data = f.read()

# Send with attachment
client.send(
    to="alice@example.com",
    subject="Your report",
    body="See attached.",
    attachments=[
        Attachment(
            filename="report.pdf",
            content_type="application/pdf",
            data=pdf_data,
            size=len(pdf_data),
        )
    ],
)

# Or reply with attachment
email.reply(
    "Here's the file you requested.",
    attachments=[
        Attachment(filename="data.csv", content_type="text/csv", data=csv_bytes, size=len(csv_bytes))
    ],
)

Async support

For async frameworks like FastAPI, use AsyncE2AClient. Same interface, all I/O methods are async:

from e2a import AsyncE2AClient

client = AsyncE2AClient()  # reads E2A_API_KEY from env

@app.post("/webhook")
async def webhook(request: Request):
    email = client.parse(await request.body())
    await email.reply("Thanks!", conversation_id="conv_123")
    return {"ok": True}

WebSocket (real-time delivery for local agents)

Local-mode agents can receive emails in real time via WebSocket using the async listen() method. No public URL needed.

pip install e2a[ws]
import asyncio
from e2a import AsyncE2AClient

async def main():
    async with AsyncE2AClient(api_key="e2a_...") as client:
        async for email in client.listen("my-bot@agents.e2a.dev"):
            print(f"From: {email.sender}, Subject: {email.subject}")
            await email.reply("Got it!")

asyncio.run(main())

listen() connects to e2a's WebSocket endpoint, receives lightweight notifications, fetches the full message via REST, and yields AsyncInboundEmail objects. It reconnects automatically with exponential backoff (1s, 2s, 4s, ... up to 30s).

Parameters:

Parameter Type Default Description
agent_email str client.agent_email Agent email to listen for
reconnect bool True Auto-reconnect on disconnect
max_backoff float 30.0 Maximum reconnect delay (seconds)

Sending emails

Send outbound emails directly:

result = client.send(
    to="alice@example.com",
    subject="Hello from my agent",
    body="Hi Alice!",
    conversation_id="conv_abc123",  # optional
)
print(result.status, result.message_id)

InboundEmail

Field Type Description
message_id str Unique e2a message ID
conversation_id str | None Your thread ID from a prior reply, or None for first contact
sender str Sender email address
recipient str Recipient email address (your agent)
subject str Email subject line
text_body str Plain-text email body
html_body str | None HTML email body, if present
attachments list[Attachment] File attachments (empty list if none)
is_verified bool Whether the sender's identity is verified
auth AuthHeaders Full authentication details
raw_message bytes Raw RFC 2822 email bytes

Methods:

  • email.reply(body, html_body=None, conversation_id=None, attachments=None)SendResult

API Reference

E2AClient(api_key=None, base_url="https://e2a.dev")

api_key is optional — falls back to the E2A_API_KEY environment variable.

  • client.parse(body)InboundEmail
  • client.reply(message_id, body, html_body=None, conversation_id=None, attachments=None)SendResult
  • client.send(to, subject, body, conversation_id=None, attachments=None)SendResult

AsyncE2AClient(api_key=None, agent_email=None, base_url="https://e2a.dev")

Same as E2AClientreply() and send() are async. parse() is sync (no I/O needed). api_key falls back to env var. Use as async context manager:

async with AsyncE2AClient() as client:
    ...

Additional async methods:

  • client.listen(agent_email=None, reconnect=True, max_backoff=30.0)AsyncIterator[AsyncInboundEmail] (requires e2a[ws])

Models

  • InboundEmail / AsyncInboundEmail — parsed email with .reply()
  • Attachmentfilename, content_type, data (bytes), size
  • SendResultstatus, message_id, method
  • AuthHeadersverified, sender, entity_type, domain_check, agent_id, human_id

Exceptions

  • E2AError — API error (has status_code and message)

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

e2a-0.8.0.tar.gz (54.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

e2a-0.8.0-py3-none-any.whl (14.6 kB view details)

Uploaded Python 3

File details

Details for the file e2a-0.8.0.tar.gz.

File metadata

  • Download URL: e2a-0.8.0.tar.gz
  • Upload date:
  • Size: 54.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for e2a-0.8.0.tar.gz
Algorithm Hash digest
SHA256 a9f57606c3e6700cbf729bc31a7edb98f6576be3756b041962f5fd5aaa9e8c98
MD5 24e6a1051ea1e63193c10c0b17534474
BLAKE2b-256 96ef16aced51385466f14b6543b72abda802227bd5be2b8d3b75def9412212e4

See more details on using hashes here.

Provenance

The following attestation bundles were made for e2a-0.8.0.tar.gz:

Publisher: publish-sdk.yml on Mnexa-AI/e2a

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file e2a-0.8.0-py3-none-any.whl.

File metadata

  • Download URL: e2a-0.8.0-py3-none-any.whl
  • Upload date:
  • Size: 14.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for e2a-0.8.0-py3-none-any.whl
Algorithm Hash digest
SHA256 fdd5387dd10b017aef2a12d948e2a2a9e7149c0ce0d0199476a041b1061ff1c1
MD5 53a4e9241102386a8fe6296d38bc0af9
BLAKE2b-256 6f01f6b5ddb194c96ee0ba6626b38c2c7c7af4ffea5badf18e36edec4cf5bc16

See more details on using hashes here.

Provenance

The following attestation bundles were made for e2a-0.8.0-py3-none-any.whl:

Publisher: publish-sdk.yml on Mnexa-AI/e2a

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page