diff --git a/src/commands/openclaw.ts b/src/commands/openclaw.ts
index 868e9e5..9d04c90 100644
--- a/src/commands/openclaw.ts
+++ b/src/commands/openclaw.ts
@@ -467,15 +467,20 @@ openmail send --to "recipient@example.com" --subject "Report" --body "
See att
\`--body\` accepts plain text or HTML — HTML is detected and rendered automatically.
-Add \`--attach \` to attach files (repeatable). The response includes
-\`messageId\` and \`threadId\` — store \`threadId\` to continue the conversation
-later. Subject is ignored when replying in a thread.
+Add \`--attach \` to attach files (repeatable). Add \`--cc \` (repeatable)
+to copy other recipients. The response includes \`messageId\` and \`threadId\` — store
+\`threadId\` to continue the conversation later. \`--subject\` is optional when
+\`--thread-id\` is set — the thread subject is used automatically.
**Always reply in the existing thread.** When the user asks you to reply
to an email, look up the thread with \`openmail inbox\` or
\`openmail threads list\` first, then use \`--thread-id\`. Never create a
new thread unless the user explicitly asks for one.
+**When you were CC'd:** check \`deliveryRole\` and \`headerTo\` on the inbound
+message (via \`threads get\`). If \`deliveryRole\` is \`cc\`, reply to \`headerTo\`
+— not to \`fromAddr\`. OpenMail auto-CCs \`fromAddr\` on thread replies.
+
## Checking for new mail
**Always use \`threads list --is-read false\` to check for new mail.**
@@ -527,6 +532,10 @@ Each message has:
| \`id\` | Message identifier |
| \`threadId\` | Conversation thread |
| \`fromAddr\` | Sender address |
+| \`toAddr\` | Envelope recipient (your inbox address on inbound) |
+| \`headerTo\` | Who the sender addressed in the To header (use for CC replies) |
+| \`deliveryRole\` | \`to\` or \`cc\` — primary recipient vs CC'd |
+| \`cc\` | CC recipients |
| \`subject\` | Subject line |
| \`bodyText\` | Plain text body (use this) |
| \`attachments\` | Array with \`filename\`, \`url\`, \`sizeBytes\` |
diff --git a/src/commands/send.ts b/src/commands/send.ts
index 6720398..1a39922 100644
--- a/src/commands/send.ts
+++ b/src/commands/send.ts
@@ -48,11 +48,12 @@ export async function runSendCommand(
const noQuote = getBooleanFlag(parsed.flags, "no-quote");
const idempotencyKey = getStringFlag(parsed.flags, "idempotency-key");
const replyTo = getStringFlag(parsed.flags, "reply-to");
+ const cc = getRepeatedStringFlag("cc");
const attachPaths = getRepeatedStringFlag("attach");
if (!inboxId) throw new Error("missing inbox id; run `openmail init` or pass --inbox-id");
if (!to) throw new Error("missing --to");
- if (!subject) throw new Error("missing --subject");
+ if (!threadId && !subject) throw new Error("missing --subject (optional when --thread-id is set)");
if (!body) throw new Error("missing --body");
let attachments: { path: string; filename: string; contentType: string }[] | undefined;
@@ -80,13 +81,14 @@ export async function runSendCommand(
return client.sendEmail({
inboxId,
to,
- subject,
+ subject: subject ?? "",
body,
bodyHtml,
threadId,
includeQuote: noQuote ? false : undefined,
idempotencyKey,
replyTo,
+ cc: cc.length > 0 ? cc : undefined,
attachments,
});
}
diff --git a/src/index.ts b/src/index.ts
index 12ebfd7..144d6c4 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -361,14 +361,16 @@ function printHelp(topic?: string) {
"openmail send",
"",
"Usage:",
- " send --to --subject --body [--inbox-id ]",
+ " send --to --body [--subject ] [--inbox-id ]",
" [--thread-id ] [--no-quote] [--idempotency-key ]",
- " [--reply-to ] [--attach ]",
+ " [--reply-to ] [--cc ] [--attach ]",
"",
"Options:",
+ " --subject Required for new threads; optional with --thread-id",
" --body Plain text or HTML (HTML is auto-detected and rendered)",
" --thread-id Reply in a thread; quotes the previous message by default",
" --no-quote Send only your reply text (skip auto-quoted history)",
+ " --cc CC recipient (repeatable)",
" --reply-to Address replies should go to. Free plan: must be the",
" address of an inbox you own. Developer+: any address.",
" --attach Attach a file (repeatable for multiple files)",
diff --git a/src/lib/http.ts b/src/lib/http.ts
index 8026a21..651c8fa 100644
--- a/src/lib/http.ts
+++ b/src/lib/http.ts
@@ -65,6 +65,7 @@ export class OpenMailHttpClient {
includeQuote?: boolean;
idempotencyKey?: string;
replyTo?: string;
+ cc?: string[];
attachments?: { path: string; filename: string; contentType: string }[];
}) {
const idempotencyKey = params.idempotencyKey ?? crypto.randomUUID();
@@ -82,6 +83,9 @@ export class OpenMailHttpClient {
formData.append("includeQuote", "false");
}
if (params.replyTo) formData.append("replyTo", params.replyTo);
+ if (params.cc?.length) {
+ for (const addr of params.cc) formData.append("cc", addr);
+ }
for (const att of params.attachments) {
const data = await readFile(att.path);
@@ -96,7 +100,7 @@ export class OpenMailHttpClient {
});
}
- const payload: Record = {
+ const payload: Record = {
to: params.to,
subject: params.subject,
body: params.body,
@@ -105,6 +109,7 @@ export class OpenMailHttpClient {
if (params.threadId) payload.threadId = params.threadId;
if (params.includeQuote === false) payload.includeQuote = false;
if (params.replyTo) payload.replyTo = params.replyTo;
+ if (params.cc?.length) payload.cc = params.cc;
return this.post(url, payload, {
"Idempotency-Key": idempotencyKey,