All posts
Guide·Jul 6, 2026·6 min read

How to send encrypted email (developer guide)

Learn how to send encrypted email: the consumer routes plus a developer guide to end-to-end encrypted, zero-knowledge email between AgenticEmail inboxes.

Dvir Atias

Dvir Atias

Founder

To send an encrypted email, you scramble the message so that only the intended recipient can read it. For people, that usually means S/MIME in Outlook or Gmail, or an end-to-end encrypted provider like ProtonMail. For code, AgenticEmail lets one inbox send an encrypted email to another AgenticEmail inbox with opt-in, end-to-end encryption: you generate a key on your own machine, publish it, and every message to a peer that has also published a key is sealed automatically before it leaves your process. The encryption is agent-to-agent and zero-knowledge, so neither AgenticEmail nor its cloud provider can read the body.

The rest of this guide covers the consumer routes briefly, then goes deep on sending an encrypted email straight from your own code.

Encrypted email for people: Outlook, Gmail, and ProtonMail

If you just want to send one encrypted message from a mail client, you have three practical options:

  • S/MIME in Outlook or Gmail. Both support S/MIME, but each side needs a certificate from a certificate authority, and Gmail S/MIME is limited to Google Workspace accounts that an admin has enabled. Once certificates are installed and exchanged, the client encrypts on send.
  • PGP through a plugin. Tools like Mailvelope add OpenPGP to webmail. Both parties exchange public keys first. This is powerful but famously fiddly, which is why we wrote about a modern alternative to PGP encryption.
  • ProtonMail or a dedicated provider. Messages between two Proton users are end-to-end encrypted by default, with password-protected messages for outside addresses.

The common thread is that both the sender and the recipient must have set up keys. That is not a limitation of any one product, it is how end-to-end encrypted email works: without the recipient's public key, there is nobody to encrypt to.

Encrypted email from your own code

The rest of this guide is for the case the tools above do not cover: your application or AI agent needs to send an encrypted email programmatically. AgenticEmail handles this between two of its own inboxes. Both must publish a key, and then sends between them are encrypted automatically. Here is the full flow.

Step 1: Create the two inboxes

End-to-end encryption is agent-to-agent, so you need a sender inbox and a recipient inbox. Create both:

curl -X POST https://api.agenticemail.dev/v1/inboxes \
  -H "Authorization: Bearer $AGENTICEMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"username": "agent-a"}'
# -> agent-a@inbox.agenticemail.dev

curl -X POST https://api.agenticemail.dev/v1/inboxes \
  -H "Authorization: Bearer $AGENTICEMAIL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"username": "agent-b"}'
# -> agent-b@inbox.agenticemail.dev

You cannot end-to-end encrypt to an external address like Gmail or Outlook, because that mailbox has no key registered with AgenticEmail. Encryption is available only between two AgenticEmail inboxes that have each published a key. Everything else falls back to plaintext.

Step 2: Generate and publish a key for each inbox

This is the step that makes the whole thing zero-knowledge. The private key is generated on your machine by the CLI or SDK and never touches AgenticEmail servers. Only the public key is uploaded when you publish.

agenticemail keys generate agent-a
agenticemail keys publish agent-a

agenticemail keys generate agent-b
agenticemail keys publish agent-b

Under the hood this is standards-based JOSE: an ECDH-ES key pair over the P-256 curve for encryption, plus an Ed25519 key pair used to sign each message so the recipient can verify the sender. keys generate writes the private key locally, keys publish uploads the public half to a directory that peers can look up. Do this once per inbox. The zero-knowledge model is the whole point, and we go deeper on it in zero-knowledge email.

Step 3: Send the message (it encrypts automatically)

Once both inboxes have published keys, you do not call a special "encrypt" method. You send normally, and AgenticEmail sees that the recipient has a published key and seals the message before it leaves your machine.

From the CLI:

agenticemail messages send agent-a \
  --to agent-b@inbox.agenticemail.dev \
  --subject "Signed and encrypted" \
  --text "This body is sealed with JWE before it ever leaves the process."

From the TypeScript SDK, pass your identity when you construct the client. With autoPublish: true the SDK publishes your public key for you:

import { readFileSync } from "node:fs";
import { AgenticEmail } from "agenticemail";

const identity = JSON.parse(readFileSync("./agent-a.identity.json", "utf8"));

const client = new AgenticEmail({
  apiKey: process.env.AGENTICEMAIL_API_KEY!,
  e2e: { identity, autoPublish: true },
});

await client.messages.send("agent-a", {
  to: "agent-b@inbox.agenticemail.dev",
  subject: "Signed and encrypted",
  text: "Sealed client-side, opened client-side.",
});

From Python, install the extra with pip install 'agenticemail[e2e]' and pass the same identity:

import os, json
from agenticemail import AgenticEmail

with open("agent-a.identity.json") as f:
    identity = json.load(f)

client = AgenticEmail(
    os.environ["AGENTICEMAIL_API_KEY"],
    e2e={"identity": identity, "auto_publish": True},
)

client.messages.send(
    "agent-a",
    to="agent-b@inbox.agenticemail.dev",
    subject="Signed and encrypted",
    text="Sealed client-side, opened client-side.",
)

In each case the message is signed with Ed25519 (JWS) and then encrypted with JWE using AES-256-GCM, with the content key wrapped per recipient via ECDH-ES. The signature is applied before encryption so the recipient can prove who sent it. If you ever need to opt out for a specific send, the CLI takes --no-encrypt to force plaintext.

Step 4: Read the message (it decrypts automatically)

On the receiving side, decryption happens with the private key that lives on that machine. Fetch the message and you get plaintext back:

agenticemail messages get agent-b <message-id>
const message = await client.messages.get("agent-b", messageId);
console.log(message.text); // already decrypted locally
message = client.messages.get("agent-b", message_id)
print(message.text)  # already decrypted locally

Because the private key never left the recipient's machine, the decryption also happens there. AgenticEmail stores and routes ciphertext it cannot read.

What to know before you rely on it

A few honest caveats so nothing surprises you in production:

  • Encryption is opt-in and conditional. A send is encrypted only when the recipient inbox has published a key. If it has not, the message goes out as plaintext. Plaintext is the default, so encryption is something you switch on, not something that is always on for all AgenticEmail mail.
  • It is agent-to-agent only. Both ends must be AgenticEmail inboxes with published keys. You cannot end-to-end encrypt to an arbitrary Gmail or Outlook recipient.
  • Back up your private key. Zero-knowledge cuts both ways. Because the key lives on your machine and never on our servers, we cannot recover it for you. If you lose the key file, you lose the ability to read messages encrypted to it. Store it the way you store any other production secret.

If you want the API-level view of the same flow, see the encrypted email API guide, and the full method reference lives in the docs.

Frequently asked questions

Can I send an encrypted email to a Gmail address with AgenticEmail?

Not with end-to-end encryption. A Gmail mailbox has no key registered with AgenticEmail, so there is no public key to encrypt to. End-to-end encryption works only between two AgenticEmail inboxes that have each published a key. Mail to external addresses is delivered as normal plaintext over TLS in transit.

What happens if the recipient has not published a key?

The message is sent as plaintext. AgenticEmail does not fail the send or silently drop it. Encryption is applied only when a published key exists for the recipient, so publishing keys on both sides is what turns encryption on.

Where is my private key stored?

On your machine, in the identity generated by the CLI or SDK. It is never uploaded to AgenticEmail, which is what makes the system zero-knowledge. Only your public key is published. Keep a backup, because a lost private key cannot be recovered.

Ready to send your first encrypted message? Grab a key from the dashboard, publish a key on each inbox, and read the docs.

Talk to a real person