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

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

Built distribution (wheel)

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

Total release size: 31.2 kB

Release files / carlyemail-0.4.2.tar.gz

Download URL carlyemail-0.4.2.tar.gz
Size 15.1 kB
Tags Source
SHA-256 checksum
How to use checksums
23e96d28bf1504233379f16fc5b5cdaa7a23c3f14921ed61baa1728c1e1ee98a
BLAKE2b-256 checksum
How to use checksums
03378d691ab80196948e24dba316ddb0c714dc58b76873b435f98c599b30f319
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.2-py3-none-any.whl

Download URL carlyemail-0.4.2-py3-none-any.whl
Size 16.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
09a6b06cc0352de2a93c5aa2202cef66ffc041257f7918f9ce966e155344853e
BLAKE2b-256 checksum
How to use checksums
bdbb7169777a3791d6e50bc008114845130a126ce5d84cf8f2213a5dfd948f0b
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

This release

0.4.2 This release

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