Skip to main content

carlyemail

Real email inboxes for AI agents. Create one with a call, then send, receive and reply to real mail — from Gmail, Outlook, anywhere.

pip install carlyemail

Getting a key

No dashboard step. Sign-up is one unauthenticated call, which is what lets an agent create its own mailbox while it is running.

from carlyemail import CarlyEmail

signup = CarlyEmail(api_key="none").agent.sign_up(
    {"human_email": "you@example.com", "username": "hello"}
)
print(signup["api_key"], signup["inbox_id"])

Confirm the six-digit code that arrives, and the account can send:

carly = CarlyEmail(api_key=signup["api_key"])
carly.agent.verify({"otp_code": "123456"})

Or from a terminal: npx carlyemail signup.

Using it

from carlyemail import CarlyEmail

carly = CarlyEmail()          # reads CARLYEMAIL_API_KEY

inbox = carly.inboxes.create({"username": "hello"})

carly.messages.send(
    inbox["email"],
    {"to": ["you@example.com"], "subject": "Hello", "text": "From an agent."},
)

for message in carly.messages.list(inbox["email"])["messages"]:
    print(message["subject"], "from", message["from"])

Replying to a message keeps the conversation together — In-Reply-To and References are set for you, so it threads in the recipient's client instead of starting a second exchange.

carly.messages.reply(inbox["email"], message["message_id"], {"text": "On it."})

Errors

Every error carries the message, the thing that clears it, and a documentation link. All three are on the exception.

from carlyemail import CarlyEmailError

try:
    carly.inboxes.create({"username": "hello"})
except CarlyEmailError as error:
    print(error.status, error.code)   # 429 inbox_limit_reached
    print(error.fix)                  # Upgrade the plan, or delete an inbox…
    print(error.docs)

Receiving

carlyemail.inbound is the other half — the bit that decides whether a delivery should wake your agent at all.

from carlyemail.inbound import create_email_router
from fastapi import FastAPI

app = FastAPI()


async def on_email(email):
    print(email.from_address, email.subject, email.text)


app.include_router(create_email_router(on_email, path="/hooks/carlyemail"))

Your handler runs only for mail worth answering. It verifies the signature over the raw bytes, returns 401 for anything that does not verify — including a signature header that is not valid base64, the case that otherwise 500s a public endpoint — admits message.received and not the spam, blocked or unauthenticated variants, drops mail the inbox sent itself, checks the sender against allow_from, ignores redeliveries, and answers the request before your agent starts thinking.

InboundReceiver.decide(body, headers) is the same logic with no framework in it, for everything that is not FastAPI. Neither needs a dependency beyond the standard library; FastAPI is imported only if you call create_email_router.

Full options: https://docs.carlyemail.com/guides/receiving#the-receiver

Resources

agent, auth, billing, inboxes, messages, threads, drafts, lists, domains, api_keys, metrics, pods, organizations, inbox_events, webhooks.

Every method is generated from the OpenAPI spec, so this client cannot describe an endpoint the API does not serve.

Also

MIT.

Release files for carlyemail 0.4.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for carlyemail 0.4.3
File Size Uploaded
carlyemail-0.4.3.tar.gz 15.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for carlyemail 0.4.3
File Interpreter ABI Platform
carlyemail-0.4.3-py3-none-any.whl Python 3 none any Details

Total release size: 31.3 kB

Release files / carlyemail-0.4.3.tar.gz

Download URL carlyemail-0.4.3.tar.gz
Size 15.1 kB
Tags Source
SHA-256 checksum
How to use checksums
a7100a70d9ca3114b6ff5e91bd1674ec33ce9084ef11bc92ec289238a6b25c73
BLAKE2b-256 checksum
How to use checksums
0a732248c6e48c055d77effd66d838dc14fd79d5142b59bcff52c116ff55344c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.3

Release files / carlyemail-0.4.3-py3-none-any.whl

Download URL carlyemail-0.4.3-py3-none-any.whl
Size 16.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
cc03a9c6bbb7ca2546602fd2089d29c0640fe83ea70ae0efcd4a42b136c5c893
BLAKE2b-256 checksum
How to use checksums
6b689c43cfad8dfc608b7be4c37c1aa9c431e94e9d4985cbaf8b9ec6e14a96fb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.3

Release history Release notifications | RSS feed

0.4.5

2 release files

0.4.4

2 release files

This release

0.4.3 This release

2 release files

0.4.2

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page