Use

Mail workflows

Search multiple mail accounts, retrieve threads, and create reversible provider Drafts.

ctxindex presents Gmail and Outlook messages through the provider-neutral mail.message Profile. Each mailbox is a Source attached to one Account and one Realm, while search can span Sources and providers.

Search across accounts

First inspect the configured Sources and their Account bindings:

ctxindex account list --format json
ctxindex source list --format json

Search every configured mailbox by omitting --realm and --source:

ctxindex search "renewal notice" --kind mail.message --format json

Restrict a search to one operating context or mailbox when the task requires it:

ctxindex search "renewal notice" \
  --kind mail.message \
  --realm company \
  --format json

ctxindex search "flight receipt" \
  --kind mail.message \
  --source personal-gmail \
  --format json

An omitted Realm spans all Realms. --realm company is exact and never includes personal Sources implicitly. Gmail and Outlook mailbox Sources use provider search when routing requires it, but return the same result envelope and stable Ref shape.

To list recent unread mail without inventing query text, select one remote mailbox and use the Profile's exact boolean field. When the selected Adapter returns an opaque continuation, inspect pagination.hasMore and repeat the unchanged request. The current Microsoft mailbox Adapter supports this flow; Gmail returns no continuation and rejects a supplied token as invalid_filter before provider I/O:

ctxindex search --remote \
  --source company-mail \
  --kind mail.message \
  --field unread=true \
  --since 2026-07-01 \
  --limit 50 \
  --format json

ctxindex search --remote \
  --source company-mail \
  --kind mail.message \
  --field unread=true \
  --since 2026-07-01 \
  --limit 50 \
  --continuation '<pagination.continuation>' \
  --format json

unread=true and unread=false are exact booleans. Remote continuation cannot be combined with local --offset, and a truncated warning with a non-null continuation means more provider results are resumable. A truncation warning without a continuation is not resumable through --continuation.

Retrieve a message and its thread

Search for the message

ctxindex search "deployment approval" --kind mail.message --format json

Retrieve the complete message

ctxindex get 'ctx://01J00000000000000000000000/message/stable-message-id' --format json
ctxindex thread 'ctx://01J00000000000000000000000/message/stable-message-id' --format json

thread follows stored conversation membership and parent Relations. It returns a reply tree when parent edges are available and otherwise a date-ordered list.

Create a provider Draft

A Draft is reversible provider state, not text that exists only in an agent conversation. Inspect the live Action schema before constructing input:

ctxindex describe action mail.message.draft.create \
  --source company-mail \
  --format json

Create the Draft through the mailbox Source that should own it:

cat > /tmp/new-draft.json <<'JSON'
{
  "to": ["recipient@example.com"],
  "cc": ["reviewer@example.com"],
  "subject": "Deployment follow-up",
  "bodyText": "The deployment completed successfully."
}
JSON

ctxindex action run mail.message.draft.create \
  --source company-mail \
  --input /tmp/new-draft.json \
  --format json

Both Gmail and Outlook mailbox Adapters implement this Action. The result returns a normalized mail.message Resource Ref for the provider-persisted Draft.

Create a threaded reply Draft

A reply Action does not fetch missing parent data. First use get on the exact parent Ref so a complete, non-deleted message from the selected Source is available locally:

ctxindex get 'ctx://01J00000000000000000000000/message/stable-message-id' --format json

Then create the reply with the strict reply branch. It accepts only the local parent Ref and body text; ctxindex derives the single recipient, reply subject, and provider-native thread state from the parent:

cat > /tmp/reply-draft.json <<'JSON'
{
  "replyToRef": "ctx://01J00000000000000000000000/message/stable-message-id",
  "bodyText": "Thanks, I will review the deployment report today."
}
JSON

ctxindex action run mail.message.draft.create \
  --source company-mail \
  --input /tmp/reply-draft.json \
  --format json

The parent must belong to company-mail, be locally complete, and contain the provider threading fields. Reply-all and caller-supplied recipients or subject are intentionally unavailable.

Update an existing Draft

Inspect the current update union before constructing input:

ctxindex describe action mail.message.draft.update \
  --source company-mail \
  --format json

A standalone Draft update replaces the complete addressed content. Supply the Draft Ref returned by create or retrieval and the full desired recipient arrays, subject, and body. Empty cc and bcc arrays explicitly clear those recipients:

cat > /tmp/updated-draft.json <<'JSON'
{
  "ref": "ctx://01J00000000000000000000000/draft/stable-draft-id",
  "to": ["recipient@example.com"],
  "cc": [],
  "bcc": [],
  "subject": "Updated deployment follow-up",
  "bodyText": "The deployment and verification completed successfully."
}
JSON

ctxindex action run mail.message.draft.update \
  --source company-mail \
  --input /tmp/updated-draft.json \
  --format json

Reply context is immutable. Before updating a reply Draft, ensure both its Draft Ref and its parent message are complete local Resources in the same selected Source (Action results are already materialized; otherwise use get). The update must repeat the exact stored replyToRef and may replace only bodyText:

ctxindex get 'ctx://01J00000000000000000000000/draft/stable-draft-id' --format json
ctxindex get 'ctx://01J00000000000000000000000/message/stable-message-id' --format json

cat > /tmp/updated-reply-draft.json <<'JSON'
{
  "ref": "ctx://01J00000000000000000000000/draft/stable-draft-id",
  "replyToRef": "ctx://01J00000000000000000000000/message/stable-message-id",
  "bodyText": "Thanks, I reviewed the deployment report."
}
JSON

ctxindex action run mail.message.draft.update \
  --source company-mail \
  --input /tmp/updated-reply-draft.json \
  --format json

Changing replyToRef, using the standalone branch for a stored reply Draft, or supplying recipients or subject in the reply branch fails before provider I/O.

Provider mutations stop at reversible email Draft creation and update. ctxindex never sends email and exposes no send, reply-send, forward-send, calendar mutation, or other provider mutation.