AI analysis & drafting
How Selyf's AI classification and draft generation actually work.
See Privacy & Data Usage for exactly what this sends to an AI provider and when, and API Reference for the full request/response contract of the endpoints below.
Providers
One AI provider (Anthropic or Gemini) is configured and active at a time, chosen server-side. Both run the exact same prompts and are validated against the exact same output schema, so which one is active never changes what fields a response contains.
What triggers a call
A call to the AI provider only happens when a specific thread's analysis or draft is explicitly requested. There is no background or bulk analysis of an inbox.
Caching
An analysis result is stored and reused — the same message is never re-sent to the AI provider a second time, which keeps repeated views instant and avoids unnecessary cost.
Prompt-injection handling
Email content is treated as data, never as an instruction. It is placed inside an escaped, delimited block in the request, separate from the fixed system prompt that defines the task — so a message that says "ignore your instructions" is just more content to analyze, not something the model treats as a command.
Output validation
Every AI response is checked against a strict schema before it is stored or returned: priority is one of a fixed set of values, confidence is a number between 0 and 1, and so on. A malformed response is rejected rather than passed through.
Budgets
Every AI call is checked against a per-account daily and monthly token budget before it is made, so usage has a hard ceiling rather than growing unbounded.
What AI cannot do
An AI result has no authority to act. It cannot send, delete, or modify anything — its entire output is a label, a reason, and, optionally, draft text shown to a user for their own decision.
Endpoints
/api/mail/threads/:threadId/analyzeReturns a cached analysis if one already exists; otherwise calls the configured AI provider and stores the result.
/api/mail/threads/:threadId/draftGenerates an editable reply draft. Accepts an optional JSON body: { tone?, language?, preferredTone?, signature? }.