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.

Download files

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

Source Distribution

carlyemail-0.4.0.tar.gz (14.8 kB view details)

Uploaded Source

Built Distribution

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

carlyemail-0.4.0-py3-none-any.whl (15.8 kB view details)

Uploaded Python 3

File details

Details for the file carlyemail-0.4.0.tar.gz.

File metadata

  • Download URL: carlyemail-0.4.0.tar.gz
  • Upload date:
  • Size: 14.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.3

File hashes

Hashes for carlyemail-0.4.0.tar.gz
Algorithm Hash digest
SHA256 ca3cd07e1b9caf3a5ce02e658406bb8b64a852cc3c541d6d0db348c5f04df5a1
MD5 eff5447565171107a543f7371e1a5797
BLAKE2b-256 bf8c8a83d89e8def578d9c16845e86f273cd946ae0f35eb3e3e5d1423e08248c

See more details on using hashes here.

File details

Details for the file carlyemail-0.4.0-py3-none-any.whl.

File metadata

  • Download URL: carlyemail-0.4.0-py3-none-any.whl
  • Upload date:
  • Size: 15.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.3

File hashes

Hashes for carlyemail-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 baeb8eeb058525eed6cb2c96e072e1639261858024240d102815293394f8ab0e
MD5 be386bcec02e84cff225b7c27c50b43a
BLAKE2b-256 810295e15a84979fb9ec8d99d5771f6dc83c96fb1a8c8ea573a13e5aa79a6976

See more details on using hashes here.

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