Library / SDK
KeyID-AI/sdk-py avatar
KeyID-AI/sdk-py

KeyID's Python SDK Gives an AI Agent an Email Address Without a Human in the Loop

Free email for AI agents. No signup, no human needed. Install → provision → send and receive. Ed25519 keypair auth.

301 stars0 forksPythonLicense varies

At a glance

What is it?
The keyid package provisions a mailbox from a generated Ed25519 keypair and wraps send, receive, threads, drafts and webhooks in one client. It is a thin layer over a hosted service, and the README does not say what happens when that service says no.
Who is it for?
Adopt keyid if you are prototyping an agent that needs a real inbox now and you accept that the address is issued by KeyID's infrastructure rather than yours. Do not adopt it if your agent handles regulated correspondence, needs a domain you control, or must keep working when a third-party API is unreachable.
Can I use it commercially?
Not without permission. GitHub finds no licence file in the repository, and without a licence all rights are reserved by default: you may read the code but not reuse it. Check the README, or ask the authors, before using it.
Is it still maintained?
Activity is slowing. The repository last received commits 6 months ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

The signup wall that agent frameworks keep hitting

Most email providers assume a person is on the other end of the integration. You create an account, confirm a phone number, solve a challenge, and only then does your code get credentials. An autonomous agent cannot do any of that, so the usual workaround is a shared human-owned mailbox with an app password pasted into an environment variable. That works until several agents need separate identities, or until one of them sends something that gets the shared address flagged. KeyID's pitch is that the agent generates an Ed25519 keypair, calls provision(), and receives an address it owns. The README states that KeyID handles domain management, rotation, reputation monitoring and deliverability. The intended user is someone building an agent that must send or receive mail as part of a task, and who does not want to run mail infrastructure to get there.

What provision() actually does, and what the SDK hides

The authentication model is challenge-response over Ed25519. On first use the SDK generates a keypair, or loads one you pass in. The provision() call registers the public key with KeyID and returns an email address. Every later call signs a nonce and exchanges it for an authenticated session, and the README says the SDK handles this automatically. The constructor accepts three shapes: KeyID() to auto-generate, KeyID(public_key="...hex...", private_key="...hex...") to reuse an existing pair, and KeyID(base_url="https://your-instance.com") to point at a different deployment. That third option is the only hint in the material that the backend could be self-hosted, and the README does not describe what such an instance would need. The client covers more surface than the quick start suggests: identity and address history, inbox with pagination and search, single-message reads and label updates, unread counts, send with HTML and CC/BCC and scheduling, reply, reply-all, forward, threads with permanent delete, a full draft lifecycle ending in send_draft, signature and forwarding and auto-reply settings, contacts, webhooks with delivery history, allow and block lists, and a metrics query. If you have used a transactional email API, the shape will be familiar. The difference is that the identity is created by the client rather than issued to it.

Install and the first four calls

Installation is one line: pip install keyid. Python 3.9 or newer is required, and httpx is pulled in automatically. The README's quick start is four operations. agent = KeyID() constructs the client. result = agent.provision() returns a dict whose email key holds the address. agent.get_inbox() returns a dict with a messages list, and each entry exposes from, subject and id. agent.send("user@example.com", "Hello", "Message body") takes recipient, subject and body positionally. agent.reply(inbox["messages"][0]["id"], "Thanks!") takes a message id first. Optional arguments appear in the feature list rather than the quick start: scheduled_at accepts an ISO 8601 timestamp such as "2025-01-01T10:00:00Z", search takes a plain string, is_starred takes a boolean on update_message, and html takes markup alongside the plain-text body. Note the return convention. Methods hand back dictionaries with named keys rather than typed objects, so a typo in a key name fails at runtime with a KeyError instead of at import. For a three-line integration that is a reasonable trade. For a codebase where an agent parses its own inbox, it means every field access is a place a silent mismatch can hide.

Key custody is the part you own

The SDK makes keypair generation invisible, which is convenient and also the main operational risk. If the private key lives only in process memory and the process dies, the README does not say whether the address survives. It does document get_recovery_token() under Identity, described as a recovery token for key rotation, and get_addresses() to list current and historical addresses. Those two methods imply that identity is meant to outlive a single keypair, but the material never explains the sequence: when to call get_recovery_token(), what you pass to rotate, or whether an old key stops working immediately. The environment-variable path for loading a keypair is mentioned in the authentication section but the variable names are not given. Anyone deploying this should treat the private key as a secret to be stored deliberately, not left to the default auto-generation path, and should read the source for the env var names before assuming they exist as documented.

The dependency you cannot see from the client

Every method here is a network call to KeyID's service. The README is explicit that KeyID, not your code, manages domains, rotation, reputation and deliverability. That is the value proposition and the failure mode in one sentence. If the service is unreachable, an agent that needs to read a verification email mid-task has no fallback, because the SDK holds no local mailbox. If KeyID rotates an address, get_addresses() will show the history but the README does not describe how inbound mail to a retired address is handled. Sending volume is also governed by a reputation system the README says exists but does not quantify, so an agent that suddenly sends a few hundred messages may discover limits that are not documented in the SDK. A second gap: the package metadata lists no licence, while the README's final section says MIT. That inconsistency matters if you are vendoring the code or running a licence scan, and it should be resolved against the repository before you rely on either statement.

Where a plain SMTP client is the better answer

The obvious alternative is Python's own smtplib plus imaplib against a mailbox you already control, or a transactional provider such as Postmark or Resend for outbound only. The difference is who holds the identity. With smtplib you configure a host, port, username and password, and the address is one you registered; nothing is provisioned on your behalf and nothing expires because a vendor changed policy. With keyid, provision() creates the identity and KeyID's infrastructure carries it. That trade is worth making when the agent needs an address immediately and no human can create one, and it is the wrong trade when the address is a business asset, when deliverability must be traceable to your own domain, or when the agent must keep operating during an outage of a third party. The SDK also has no local queue, so an unsent message during downtime is simply lost unless your code retries. A plain SMTP client with a retry loop around sendmail() gives you that durability for free.

Version 0.1.2 and the cost of staying current

The only release listed is v0.1.2, published the same day as the last push to main, which suggests active but very early development. At this stage the upgrade cost is not migration effort, it is API churn. A pre-1.0 client with this many methods across identity, messages, threads, drafts, settings, contacts, webhooks, lists and metrics has many surfaces that can shift before a stable release. Pin the version in your requirements file and read the diff before bumping, because the README gives no compatibility policy and no changelog beyond the release tag. The MIT claim in the README, if it holds, permits commercial use and modification with attribution, but the empty licence field in the package metadata means you should confirm the actual file in the repository rather than trusting either the README or this summary. Nothing here is legal advice; a licence scan on the pinned version will settle it.

Editorial conclusion

Adopt keyid if you are prototyping an agent that needs a real inbox now and you accept that the address is issued by KeyID's infrastructure rather than yours. Do not adopt it if your agent handles regulated correspondence, needs a domain you control, or must keep working when a third-party API is unreachable. Before writing it into anything durable, verify three things the README does not answer: whether provision() is rate limited per keypair, what the recovery token actually restores after key rotation, and whether the SDK pins an httpx version range.

Official sources

  1. Issues
  2. KeyID-AI/sdk-py on GitHub
  3. Project website
  4. README
  5. Releases
Community notes

Community notes