AI

Classify replies to the mail you send, handle detected opt-outs, and draft email text with AI.

Workwae has two optional AI features: classifying replies to the mail you send, and drafting email text for a person to edit. For connecting AI agents to mailboxes, see the MCP server.

What is sent to the model

Reply classification is off until your workspace turns it on; drafting runs only when someone asks for a draft. The text a feature needs goes to Gatewae, Workwae's AI gateway, which forwards it to a language model provider.

FeatureSent
Reply classificationThe reply's subject, the first 2,000 characters of its own text, and whether it carries standard auto-reply headers. An out-of-office reply is read once more, with the date it was sent, for its return date.
DraftingThe instructions, the draft so far and, when answering a message, that message's sender, subject and the start of its own text.

No training

Workwae uses only providers that do not train on the data it sends, and never trains a model on it.

Reply classification

Turn on aiReplyClassification in the workspace settings:

curl -X PATCH "https://api.workwae.com/v1/settings" \
  -H "Authorization: Bearer $WORKWAE_API_KEY" \
  -H "content-type: application/json" \
  -d '{ "aiReplyClassification": true, "optOutHandling": "review" }'

What gets classified:

  • New inbound mail in the inbox or junk that answers a message you sent through Workwae, matched by thread or by its In-Reply-To and References headers.
  • It is classified about a second after it is indexed.
  • Mail more than seven days old when indexed is not classified.
  • Turning the setting off drops queued replies unread.

The result

Each result is stored as the message's classification and sent as the message.classified event.

FieldMeaning
kindopt_out, auto_reply, bounce or human_reply. probabilities holds each kind's probability, 0 to 1.
optOutProbabilityThe probability the reply asks to stop receiving email, 0 to 1.
autoReplyCategoryOn auto-replies only: out_of_office, left_company, address_changed, ticket_acknowledgment, challenge_response or other; null otherwise.
returnDateFor out-of-office replies, resolved against the day the reply was sent. null when the date is not real, falls before that day, or is more than a year after it.
alternateContactsUp to five addresses a left_company or address_changed reply names, read from its text as written, never from the model.
outboxIdThe send the reply answers.
model, classifiedAtThe model that classified it, and when.

React to it in your webhook handler:

import { parseWebhook } from "@workwae/sdk";

const event = await parseWebhook(secret, request.headers, await request.text());
if (event.known && event.type === "message.classified") {
  const { classification: c } = event.data;
  if (c.autoReplyCategory === "out_of_office" && c.returnDate) {
    await pause(c.outboxId, c.returnDate);
  }
  if (c.kind === "human_reply") await stopFollowUps(c.outboxId);
}

Opt-outs and the review queue

optOutHandling decides what a detected opt-out from the send's own recipient does, at an optOutProbability of 0.9 or more:

ValueEffect
offIgnores it.
review (default)Holds the address in the review queue.
suppressSuppresses the address at once.
  • Replies saying the person left or changed address always wait for review unless the setting is off; they never suppress at once.
  • Unsubscribe links always suppress, whatever the setting.

Work through the queue by approving or dismissing each review:

const reviews = workwae.suppressionReviews;
const { items } = await reviews.list({});
for (const review of items) {
  const ok = await askOwner(review.address, review.source);
  const input = { reviewId: review.id };
  await (ok ? reviews.approve(input) : reviews.dismiss(input));
}

Suppression lists covers each mode in detail.

Drafting

POST /v1/ai/draft writes an email body for a person to edit and returns { text }. Nothing is sent or saved. It needs an API key with the write scope and the AI module in the tenant's plan (plan_module_unavailable otherwise).

curl -X POST "https://api.workwae.com/v1/ai/draft" \
  -H "Authorization: Bearer $WORKWAE_API_KEY" \
  -H "content-type: application/json" \
  -d '{
  "instructions": "Shorter, and offer a call on Thursday",
  "context": { "accountId": "'"$ACCOUNT_ID"'", "replyToMessageId": "'"$MESSAGE_ID"'" },
  "format": "markdown"
}'
FieldMeaning
instructionsWhat to write, in the user's words; up to 2,000 characters.
formattext or markdown.
context.replyToMessageIdOptional. The message being answered; needs context.accountId, the account holding it.
context.currentDraftOptional. The draft so far, to rewrite or continue; up to 20,000 characters.

Limits and errors

Limit or reasonMeaning
Daily classification capEach workspace has a daily cap on classified replies; past it, replies wait for the next UTC day.
rate_limitedDrafts have a per-minute limit per workspace; past it, 429 with Retry-After.
ai_refusedThe model declines the instructions.
ai_unavailableThe AI gateway fails or does not answer in time.
ai_suspendedThe AI service refuses Workwae's requests for now; try again later.
ai_disabledAI drafting is not configured on this deployment.
unknown_account, unknown_messagecontext names an account the tenant does not have, or a message the account does not have.
  • If the gateway fails, classification retries a reply later, backing off to an hour, for up to seven days.
  • Every answered call counts toward aiCalls in your daily usage (GET /v1/usage; see Rate limits and usage).

Every route and its full list of errors is in the AI reference.

On this page