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 jsonSearch every configured mailbox by omitting --realm and --source:
ctxindex search "renewal notice" --kind mail.message --format jsonRestrict 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 jsonAn 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 jsonunread=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 jsonRetrieve the complete message
ctxindex get 'ctx://01J00000000000000000000000/message/stable-message-id' --format jsonLoad the related conversation
ctxindex thread 'ctx://01J00000000000000000000000/message/stable-message-id' --format jsonthread 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 jsonCreate 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 jsonBoth 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 jsonThen 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 jsonThe 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 jsonA 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 jsonReply 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 jsonChanging 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.