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.5

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.5
File Size Uploaded
carlyemail-0.4.5.tar.gz 15.8 kB Details

Built distribution (wheel)

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

Total release size: 32.5 kB

Release files / carlyemail-0.4.5.tar.gz

Download URL carlyemail-0.4.5.tar.gz
Size 15.8 kB
Tags Source
SHA-256 checksum
How to use checksums
0ccb2ad499a19f9f20dc2b308b30e65ca848ce72494100b0af2ed51e98d41905
BLAKE2b-256 checksum
How to use checksums
c0d9e80735c06287d31e55f8b2118ddf0ca0035f02a9d84ca031133a58f4e2c2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.15

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

Download URL carlyemail-0.4.5-py3-none-any.whl
Size 16.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
59743af576eaaec74fd5a0eb72e9529560065d281c11383ba58360e94301f742
BLAKE2b-256 checksum
How to use checksums
47107a45b1ba9e2d5e8e76d0c637fd2cb9809ee2420cd5fc0120b16e66758fb6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.15

Release history Release notifications | RSS feed

This release

0.4.5 This release

2 release files

0.4.4

2 release files

0.4.3

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