Skip to content

Mail

Knobs Mail is thread-centric: inbound and outbound messages group into threads per mailbox. The API reads threads and messages, sends outbound mail from any of your active addresses, and exposes the compose AI assists.

All routes are workspace-scoped and accept a session JWT or API key. You only ever see mail addressed to (or sent from) your own mailboxes and aliases.

Endpoint Description
GET /v1/workspaces/{wid}/mail/threads List your threads, newest first; ?folder= (inbox/archive/trash/spam/sent) and ?limit=
GET /v1/workspaces/{wid}/mail/threads/{tid} Thread with its ordered messages
GET /v1/workspaces/{wid}/mail/threads/{tid}/messages The messages in a thread (?limit=)
GET /v1/workspaces/{wid}/mail/messages/{mid}/raw-url Short-lived signed URL for the raw MIME → {url}
GET /v1/workspaces/{wid}/mail/messages/{mid}/attachments/{idx}/download-url Signed URL for attachment idx{url} (409 until extraction finishes)
Terminal window
curl -H "Authorization: Bearer knak_..." \
"https://api.knobs.io/v1/workspaces/{wid}/mail/threads?folder=inbox&limit=25"
{
"items": [
{
"id": "t_8c1f…",
"subject": "Q3 planning",
"snippet": "Here's the draft agenda…",
"folder": "inbox",
"unread": true,
"starred": false,
"lastMessageAt": "2026-07-16T15:02:11Z"
}
],
"nextPageToken": ""
}
Endpoint Description
POST /v1/workspaces/{wid}/mail/messages:send Send a message
POST /v1/workspaces/{wid}/mail/drafts/{rpc...} Draft RPCs — currently {draftId}:send, equivalent to messages:send
Terminal window
curl -X POST https://api.knobs.io/v1/workspaces/{wid}/mail/messages:send \
-H "Authorization: Bearer knak_..." \
-H "Content-Type: application/json" \
-d '{
"from": "jane@acme.knobs.io",
"to": ["sam@example.com"],
"cc": [],
"subject": "Q3 planning",
"text": "Draft agenda attached.",
"html": "<p>Draft agenda attached.</p>",
"clientMutationId": "0d5e0a4e-8f0f-4f57-a2ff-0f6a4b1c9d21",
"replyToMessageId": "",
"attachmentNodeIds": ["n_77aa…"]
}'

Returns 201 with the stored message. Notes:

  • from must be one of your active mailboxes or aliases (403 sender_not_authorized otherwise) on a verified domain.
  • replyToMessageId threads the send as a reply and sets the reply headers.
  • attachmentNodeIds reference Drive file nodes; total attachment size is capped at 25 MiB.
  • clientMutationId makes retries safe.
Endpoint Description
POST /v1/workspaces/{wid}/mail/threads/{tid}/read {read: true|false} — mark read/unread
POST /v1/workspaces/{wid}/mail/threads/{tid}/move {folder} — move to inbox/archive/trash/spam
POST /v1/workspaces/{wid}/mail/threads/{tid}/star {starred: true|false}

Each returns the updated thread.

Synchronous single-turn generations for the composer. Results are ephemeral — returned for review, never persisted. Rate-limited per user (429 resource_exhausted).

Endpoint Description
POST /v1/workspaces/{wid}/mail/ai:suggest-reply {threadId, guidance?} → a drafted reply
POST /v1/workspaces/{wid}/mail/ai:improve-draft {text, subject?, threadId?, to?} → a cleaned-up draft
POST /v1/workspaces/{wid}/mail/ai:summarize-thread {threadId} → a thread digest

All three return the same shape:

{ "text": "Thanks Sam — Wednesday works…", "model": "", "inputTokens": 812, "outputTokens": 96 }
Endpoint Description
POST /v1/voice/read-aloud {workspaceId, entityType, entityId} → synthesized audio bytes (not JSON; Content-Type is the audio MIME)

entityType is one of mail.message, mail.thread, mail.thread.summary. The entity is resolved and ACL-checked server-side — you pass a reference, never raw text.

Inbound messages to a nonexistent address are shelved for 30 days rather than dropped. Admins can inspect and resolve them:

Endpoint Description
GET /v1/workspaces/{wid}/mail/rejections List undelivered mail, newest first
POST /v1/workspaces/{wid}/mail/rejections/{rid}/redeliver Deliver into the (now-created) mailbox — 409 still_unroutable if the address still doesn’t exist
POST /v1/workspaces/{wid}/mail/rejections/{rid}/dismiss Resolve without delivering