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

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.1
File Size Uploaded
carlyemail-0.4.1.tar.gz 15.1 kB Details

Built distribution (wheel)

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

Total release size: 31.2 kB

Release files / carlyemail-0.4.1.tar.gz

Download URL carlyemail-0.4.1.tar.gz
Size 15.1 kB
Tags Source
SHA-256 checksum
How to use checksums
cda8be681834e8836ea8e2b3605c53e23181ead57a853cec8331d8e5f7021f1e
BLAKE2b-256 checksum
How to use checksums
a532a58f65bfcc3ec09267ef0623c93970eddd3cd6b50a027de35d86d11b1b3f
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.1-py3-none-any.whl

Download URL carlyemail-0.4.1-py3-none-any.whl
Size 16.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6acab2b63e4781fd0540a6712eb2ae8f098de7a001b36c81101584394a26061b
BLAKE2b-256 checksum
How to use checksums
1bb0bd7263eee67441ef116f17dd213c606881b5d43ec0eed5b13578f992d094
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

0.4.3

2 release files

0.4.2

2 release files

This release

0.4.1 This release

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